///|
/// 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 = expressions[0].location()
      let last = expressions[expressions.length() - 1].location()
      @span.SrcSpan::new(first.start, last.end)
    }
  }
}

///|
/// 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~, ..) =>
      @span.SrcSpan::new(pattern.location().start, location.end)
    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()
  }
}

///|
pub fn BitArraySize::location(self : BitArraySize) -> @span.SrcSpan {
  match self {
    Int(location~, ..)
    | Variable(location~, ..)
    | BinaryOperator(location~, ..)
    | Block(location~, ..) => location
  }
}

///|
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
  }
}

///|
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~, ..) =>
      @span.SrcSpan::new(container.location().start, label_location.end)
    Constant(c) => c.location()
  }
}

///|
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
  }
}