///|
fn class_comprehension_binds(expr : Expr, in_comp~ : Bool = false) -> Bool {
  match expr {
    Expr::Lambda(defaults~, ..) =>
      defaults.any(fn(expr) { class_comprehension_binds(expr, in_comp~) })
    Expr::NamedExpr(value~, ..) => in_comp || class_comprehension_binds(value)
    Expr::ListComp(..)
    | Expr::DictComp(..)
    | Expr::SetComp(..)
    | Expr::GenExp(..) =>
      expression_children(expr).any(fn(expr) {
        class_comprehension_binds(expr, in_comp=true)
      })
    _ =>
      expression_children(expr).any(fn(expr) {
        class_comprehension_binds(expr, in_comp~)
      })
  }
}

///|
// Nonlocal must have an enclosing function scope. A comprehension cannot bind
// a name in a class scope, but a nested method/lambda has its own binding scope.
fn class_scope_error_line(
  body : Array[Stmt],
  enclosing_function : Bool,
  line~ : Int32 = 1,
) -> Int32? {
  for stmt in body {
    let expressions : Array[Expr] = []
    let children : Array[Array[Stmt]] = []
    match stmt {
      Stmt::WithSpan(span~, stmt~) =>
        if class_scope_error_line([stmt], enclosing_function, line=span.line)
          is Some(line) {
          return Some(line)
        }
      Stmt::Nonlocal(_) => if !enclosing_function { return Some(line) }
      Stmt::ExprStmt(expr)
      | Stmt::Assign(value=expr, ..)
      | Stmt::AugAssign(value=expr, ..) => expressions.push(expr)
      Stmt::AnnAssign(annotation~, value~, ..) => {
        expressions.push(annotation)
        if value is Some(value) {
          expressions.push(value)
        }
      }
      Stmt::Function(
        decorators~,
        defaults~,
        annotations~,
        return_annotation~,
        ..
      ) => {
        expressions.append(decorators)
        expressions.append(defaults)
        for annotation in annotations {
          expressions.push(annotation.1)
        }
        if return_annotation is Some(expr) {
          expressions.push(expr)
        }
      }
      Stmt::If(condition~, body~, else_body~)
      | Stmt::While(condition~, body~, else_body~) => {
        expressions.push(condition)
        children.push(body)
        children.push(else_body)
      }
      Stmt::For(iter~, body~, else_body~, ..)
      | Stmt::AsyncFor(iter~, body~, else_body~, ..) => {
        expressions.push(iter)
        children.push(body)
        children.push(else_body)
      }
      Stmt::With(context~, body~, ..) | Stmt::AsyncWith(context~, body~, ..) => {
        expressions.push(context)
        children.push(body)
      }
      Stmt::Try(body~, handlers~, else_body~, finally_body~) => {
        children.push(body)
        children.push(else_body)
        children.push(finally_body)
        for handler in handlers {
          if handler.exc is Some(expr) {
            expressions.push(expr)
          }
          children.push(handler.body)
        }
      }
      Stmt::Match(subject~, cases~) => {
        expressions.push(subject)
        for case in cases {
          if case.1 is Some(expr) {
            expressions.push(expr)
          }
          children.push(case.2)
        }
      }
      Stmt::Assert(condition~, message~) => {
        expressions.push(condition)
        if message is Some(expr) {
          expressions.push(expr)
        }
      }
      Stmt::Raise(exc~, cause~) => {
        if exc is Some(expr) {
          expressions.push(expr)
        }
        if cause is Some(expr) {
          expressions.push(expr)
        }
      }
      _ => ()
    }
    if expressions.any(fn(expr) { class_comprehension_binds(expr) }) {
      return Some(line)
    }
    for child in children {
      if class_scope_error_line(child, enclosing_function, line~) is Some(line) {
        return Some(line)
      }
    }
  }
  None
}