[red-knot] Clarify how scopes are pushed and popped for comprehensions and generator expressions (#13353)

This commit is contained in:
Alex Waygood 2024-09-14 13:31:17 -04:00 committed by GitHub
parent 8b49845537
commit f4de49ab37
No known key found for this signature in database
GPG key ID: B5690EEEBB952194

View file

@ -325,11 +325,23 @@ impl<'db> SemanticIndexBuilder<'db> {
nested_scope nested_scope
} }
/// Visit a list of [`Comprehension`] nodes, assumed to be the "generators" that compose a /// This method does several things:
/// comprehension (that is, the `for x in y` and `for y in z` parts of `x for x in y for y in z`.) /// - It pushes a new scope onto the stack for visiting
/// a list/dict/set comprehension or generator expression
/// - Inside that scope, it visits a list of [`Comprehension`] nodes,
/// assumed to be the "generators" that compose a comprehension
/// (that is, the `for x in y` and `for y in z` parts of `x for x in y for y in z`).
/// - Inside that scope, it also calls a closure for visiting the outer `elt`
/// of a list/dict/set comprehension or generator expression
/// - It then pops the new scope off the stack
/// ///
/// [`Comprehension`]: ast::Comprehension /// [`Comprehension`]: ast::Comprehension
fn visit_generators(&mut self, scope: NodeWithScopeRef, generators: &'db [ast::Comprehension]) { fn with_generators_scope(
&mut self,
scope: NodeWithScopeRef,
generators: &'db [ast::Comprehension],
visit_outer_elt: impl FnOnce(&mut Self),
) {
let mut generators_iter = generators.iter(); let mut generators_iter = generators.iter();
let Some(generator) = generators_iter.next() else { let Some(generator) = generators_iter.next() else {
@ -368,6 +380,9 @@ impl<'db> SemanticIndexBuilder<'db> {
self.visit_expr(expr); self.visit_expr(expr);
} }
} }
visit_outer_elt(self);
self.pop_scope();
} }
fn declare_parameter(&mut self, parameter: AnyParameterRef) { fn declare_parameter(&mut self, parameter: AnyParameterRef) {
@ -878,6 +893,7 @@ where
} }
self.visit_expr(lambda.body.as_ref()); self.visit_expr(lambda.body.as_ref());
self.pop_scope();
} }
ast::Expr::If(ast::ExprIf { ast::Expr::If(ast::ExprIf {
body, test, orelse, .. body, test, orelse, ..
@ -898,30 +914,33 @@ where
elt, generators, .. elt, generators, ..
}, },
) => { ) => {
self.visit_generators( self.with_generators_scope(
NodeWithScopeRef::ListComprehension(list_comprehension), NodeWithScopeRef::ListComprehension(list_comprehension),
generators, generators,
|builder| builder.visit_expr(elt),
); );
self.visit_expr(elt);
} }
ast::Expr::SetComp( ast::Expr::SetComp(
set_comprehension @ ast::ExprSetComp { set_comprehension @ ast::ExprSetComp {
elt, generators, .. elt, generators, ..
}, },
) => { ) => {
self.visit_generators( self.with_generators_scope(
NodeWithScopeRef::SetComprehension(set_comprehension), NodeWithScopeRef::SetComprehension(set_comprehension),
generators, generators,
|builder| builder.visit_expr(elt),
); );
self.visit_expr(elt);
} }
ast::Expr::Generator( ast::Expr::Generator(
generator @ ast::ExprGenerator { generator @ ast::ExprGenerator {
elt, generators, .. elt, generators, ..
}, },
) => { ) => {
self.visit_generators(NodeWithScopeRef::GeneratorExpression(generator), generators); self.with_generators_scope(
self.visit_expr(elt); NodeWithScopeRef::GeneratorExpression(generator),
generators,
|builder| builder.visit_expr(elt),
);
} }
ast::Expr::DictComp( ast::Expr::DictComp(
dict_comprehension @ ast::ExprDictComp { dict_comprehension @ ast::ExprDictComp {
@ -931,28 +950,19 @@ where
.. ..
}, },
) => { ) => {
self.visit_generators( self.with_generators_scope(
NodeWithScopeRef::DictComprehension(dict_comprehension), NodeWithScopeRef::DictComprehension(dict_comprehension),
generators, generators,
|builder| {
builder.visit_expr(key);
builder.visit_expr(value);
},
); );
self.visit_expr(key);
self.visit_expr(value);
} }
_ => { _ => {
walk_expr(self, expr); walk_expr(self, expr);
} }
} }
if matches!(
expr,
ast::Expr::Lambda(_)
| ast::Expr::ListComp(_)
| ast::Expr::SetComp(_)
| ast::Expr::Generator(_)
| ast::Expr::DictComp(_)
) {
self.pop_scope();
}
} }
fn visit_parameters(&mut self, parameters: &'ast ast::Parameters) { fn visit_parameters(&mut self, parameters: &'ast ast::Parameters) {