///|
// Scope declarations apply to a whole function, but cannot follow a use or
// binding of that name in the same lexical scope. Nested function/class bodies
// are deliberately not included by collect_function_local_names.
fn scope_decl_after_use_line(body : Array[Stmt]) -> Int32? {
let seen : Set[String] = Set([])
let globals : Set[String] = Set([])
let nonlocals : Set[String] = Set([])
fn record(stmt : Stmt) -> Unit {
// Include assignment-expression bindings even inside comprehensions.
for name in collect_function_local_names([stmt], [], [], []) {
seen.add(name)
}
for
name in collect_function_local_names(
[stmt],
[],
[],
[],
include_reads=true,
) {
seen.add(name)
}
}
fn walk(stmts : Array[Stmt], line : Int32) -> Int32? {
for stmt in stmts {
let error = match stmt {
Stmt::WithSpan(span~, stmt~) => walk([stmt], span.line)
Stmt::Global(names) => {
for name in names {
if seen.contains(name) || nonlocals.contains(name) {
return Some(line)
}
globals.add(name)
}
None
}
Stmt::Nonlocal(names) => {
for name in names {
if seen.contains(name) || globals.contains(name) {
return Some(line)
}
nonlocals.add(name)
}
None
}
Stmt::If(condition~, body~, else_body~)
| Stmt::While(condition~, body~, else_body~) => {
record(Stmt::ExprStmt(condition))
match walk(body, line) {
Some(line) => Some(line)
None => walk(else_body, line)
}
}
Stmt::For(target~, iter~, body~, else_body~)
| Stmt::AsyncFor(target~, iter~, body~, else_body~) => {
record(Stmt::Assign(target~, value=iter))
match walk(body, line) {
Some(line) => Some(line)
None => walk(else_body, line)
}
}
Stmt::With(context~, target~, body~)
| Stmt::AsyncWith(context~, target~, body~) => {
record(Stmt::ExprStmt(context))
if target is Some(name) {
seen.add(name)
}
walk(body, line)
}
Stmt::Try(body~, handlers~, else_body~, finally_body~) => {
if walk(body, line) is Some(line) {
return Some(line)
}
for handler in handlers {
if handler.exc is Some(expr) {
record(Stmt::ExprStmt(expr))
}
if handler.name is Some(name) {
seen.add(name)
}
if walk(handler.body, line) is Some(line) {
return Some(line)
}
}
if walk(else_body, line) is Some(line) {
return Some(line)
}
walk(finally_body, line)
}
Stmt::Match(subject~, cases~) => {
record(Stmt::ExprStmt(subject))
for case in cases {
let captures : Set[String] = Set([])
let _ = pattern_bindings_unique(case.0, captures)
for name in captures {
seen.add(name)
}
if case.1 is Some(expr) {
record(Stmt::ExprStmt(expr))
}
if walk(case.2, line) is Some(line) {
return Some(line)
}
}
None
}
_ => {
record(stmt)
None
}
}
if error is Some(line) {
return Some(line)
}
}
None
}
walk(body, 1)
}