///|
/// The span of an expression. A `Pipeline` has no span of its own: it runs
/// from the start of its first expression to the end of its last one, as
/// `UntypedExpr::location` does in the compiler.
pub fn Expr::location(self : Expr) -> @span.SrcSpan {
  match self {
    Int(location~, ..)
    | Float(location~, ..)
    | String(location~, ..)
    | Block(location~, ..)
    | Var(location~, ..)
    | Fn(location~, ..)
    | List(location~, ..)
    | Call(location~, ..)
    | BinOp(location~, ..)
    | Case(location~, ..)
    | FieldAccess(location~, ..)
    | Tuple(location~, ..)
    | TupleIndex(location~, ..)
    | Todo(location~, ..)
    | Panic(location~, ..)
    | Echo(location~, ..)
    | BitArray(location~, ..)
    | RecordUpdate(location~, ..)
    | NegateBool(location~, ..)
    | NegateInt(location~, ..) => location
    Pipeline(expressions~) => {
      let first = for expr = expressions[0] {
        match expr {
          Pipeline(expressions~) => continue expressions[0]
          other => break other.location().start
        }
      }
      let last = for expr = expressions[expressions.length() - 1] {
        match expr {
          Pipeline(expressions~) =>
            continue expressions[expressions.length() - 1]
          other => break other.location().end
        }
      }
      @span.SrcSpan::from_compiler(first, last).unwrap()
    }
  }
}

///|
/// The span of a pattern. A `BitArraySize` pattern has its size's span; an
/// `Assign` (`pattern as name`) runs from its inner pattern's start to the
/// name's end (its `location` field is the name's span), as
/// `Pattern::location` does in the compiler.
pub fn Pattern::location(self : Pattern) -> @span.SrcSpan {
  match self {
    Assign(location~, pattern~, ..) => {
      let start = for current = pattern {
        match current {
          Assign(pattern~, ..) => continue pattern
          other => break other.location().start
        }
      }
      @span.SrcSpan::from_compiler(start, location.end).unwrap()
    }
    Int(location~, ..)
    | Float(location~, ..)
    | String(location~, ..)
    | Variable(location~, ..)
    | Discard(location~, ..)
    | List(location~, ..)
    | Constructor(location~, ..)
    | Tuple(location~, ..)
    | BitArray(location~, ..)
    | StringPrefix(location~, ..) => location
    BitArraySize(size) => size.location()
  }
}

///|
/// Return the byte span of a bit array size expression.
pub fn BitArraySize::location(self : BitArraySize) -> @span.SrcSpan {
  match self {
    Int(location~, ..)
    | Variable(location~, ..)
    | BinaryOperator(location~, ..)
    | Block(location~, ..) => location
  }
}

///|
/// Return the byte span of a type annotation.
pub fn TypeAst::location(self : TypeAst) -> @span.SrcSpan {
  match self {
    Constructor(c) => c.location
    Fn(f) => f.location
    Var(v) => v.location
    Tuple(t) => t.location
    Hole(h) => h.location
  }
}

///|
/// Return the byte span of a constant.
pub fn Constant::location(self : Constant) -> @span.SrcSpan {
  match self {
    Int(location~, ..)
    | Float(location~, ..)
    | String(location~, ..)
    | Tuple(location~, ..)
    | List(location~, ..)
    | Record(location~, ..)
    | RecordUpdate(location~, ..)
    | BitArray(location~, ..)
    | Var(location~, ..)
    | BinaryOperator(location~, ..)
    | Todo(location~, ..) => location
  }
}

///|
/// The span of a clause guard. A `FieldAccess` has no span field: it runs
/// from the start of its container to the end of its label, as
/// `ClauseGuard::location` does in the compiler.
pub fn ClauseGuard::location(self : ClauseGuard) -> @span.SrcSpan {
  match self {
    Block(location~, ..)
    | BinaryOperator(location~, ..)
    | Not(location~, ..)
    | Var(location~, ..)
    | TupleIndex(location~, ..) => location
    FieldAccess(label_location~, container~, ..) => {
      let start = for current = container {
        match current {
          FieldAccess(container~, ..) => continue container
          other => break other.location().start
        }
      }
      @span.SrcSpan::from_compiler(start, label_location.end).unwrap()
    }
    Constant(c) => c.location()
  }
}

///|
/// Return the byte span of a statement.
/// An expression statement uses the expression's span.
pub fn Statement::location(self : Statement) -> @span.SrcSpan {
  match self {
    Expression(e) => e.location()
    Assignment(a) => a.location
    Use(u) => u.location
    Assert(a) => a.location
  }
}

///|
/// The span of a definition: the compiler's `location` field, which for a
/// function or custom type covers the head only (`end_position` ends the
/// body).
pub fn Definition::location(self : Definition) -> @span.SrcSpan {
  match self {
    Function(f) => f.location
    TypeAlias(t) => t.location
    CustomType(c) => c.location
    Import(i) => i.location
    ModuleConstant(c) => c.location
  }
}