diff --git a/crates/ruff/src/checkers/ast/analyze/deferred_for_loops.rs b/crates/ruff/src/checkers/ast/analyze/deferred_for_loops.rs index ebacfc4778..c9ed5be35d 100644 --- a/crates/ruff/src/checkers/ast/analyze/deferred_for_loops.rs +++ b/crates/ruff/src/checkers/ast/analyze/deferred_for_loops.rs @@ -16,7 +16,7 @@ pub(crate) fn deferred_for_loops(checker: &mut Checker) { }) | Stmt::AsyncFor(ast::StmtAsyncFor { target, iter, body, .. - }) = &checker.semantic.stmt() + }) = &checker.semantic.current_statement() { if checker.enabled(Rule::UnusedLoopControlVariable) { flake8_bugbear::rules::unused_loop_control_variable(checker, target, body); diff --git a/crates/ruff/src/checkers/ast/analyze/deferred_scopes.rs b/crates/ruff/src/checkers/ast/analyze/deferred_scopes.rs index ac7e675492..b9c6e56601 100644 --- a/crates/ruff/src/checkers/ast/analyze/deferred_scopes.rs +++ b/crates/ruff/src/checkers/ast/analyze/deferred_scopes.rs @@ -112,7 +112,11 @@ pub(crate) fn deferred_scopes(checker: &mut Checker) { // If the bindings are in different forks, abort. if shadowed.source.map_or(true, |left| { binding.source.map_or(true, |right| { - branch_detection::different_forks(left, right, &checker.semantic.stmts) + branch_detection::different_forks( + left, + right, + &checker.semantic.statements, + ) }) }) { continue; @@ -172,7 +176,7 @@ pub(crate) fn deferred_scopes(checker: &mut Checker) { if shadowed.kind.is_function_definition() && visibility::is_overload( cast::decorator_list( - checker.semantic.stmts[shadowed.source.unwrap()], + checker.semantic.statements[shadowed.source.unwrap()], ), &checker.semantic, ) @@ -195,7 +199,11 @@ pub(crate) fn deferred_scopes(checker: &mut Checker) { // If the bindings are in different forks, abort. if shadowed.source.map_or(true, |left| { binding.source.map_or(true, |right| { - branch_detection::different_forks(left, right, &checker.semantic.stmts) + branch_detection::different_forks( + left, + right, + &checker.semantic.statements, + ) }) }) { continue; diff --git a/crates/ruff/src/checkers/ast/analyze/expression.rs b/crates/ruff/src/checkers/ast/analyze/expression.rs index 2b462698ef..6398e14e87 100644 --- a/crates/ruff/src/checkers/ast/analyze/expression.rs +++ b/crates/ruff/src/checkers/ast/analyze/expression.rs @@ -84,7 +84,7 @@ pub(crate) fn expression(expr: &Expr, checker: &mut Checker) { // traverse nested unions. let is_unchecked_union = checker .semantic - .expr_grandparent() + .current_expression_grandparent() .and_then(Expr::as_subscript_expr) .map_or(true, |parent| { !checker.semantic.match_typing_expr(&parent.value, "Union") @@ -206,11 +206,16 @@ pub(crate) fn expression(expr: &Expr, checker: &mut Checker) { } ExprContext::Store => { if checker.enabled(Rule::NonLowercaseVariableInFunction) { - if checker.semantic.scope().kind.is_any_function() { + if checker.semantic.current_scope().kind.is_any_function() { // Ignore globals. - if !checker.semantic.scope().get(id).is_some_and(|binding_id| { - checker.semantic.binding(binding_id).is_global() - }) { + if !checker + .semantic + .current_scope() + .get(id) + .is_some_and(|binding_id| { + checker.semantic.binding(binding_id).is_global() + }) + { pep8_naming::rules::non_lowercase_variable_in_function( checker, expr, id, ); @@ -219,7 +224,7 @@ pub(crate) fn expression(expr: &Expr, checker: &mut Checker) { } if checker.enabled(Rule::MixedCaseVariableInClassScope) { if let ScopeKind::Class(ast::StmtClassDef { arguments, .. }) = - &checker.semantic.scope().kind + &checker.semantic.current_scope().kind { pep8_naming::rules::mixed_case_variable_in_class_scope( checker, @@ -230,7 +235,7 @@ pub(crate) fn expression(expr: &Expr, checker: &mut Checker) { } } if checker.enabled(Rule::MixedCaseVariableInGlobalScope) { - if matches!(checker.semantic.scope().kind, ScopeKind::Module) { + if matches!(checker.semantic.current_scope().kind, ScopeKind::Module) { pep8_naming::rules::mixed_case_variable_in_global_scope( checker, expr, id, ); @@ -243,7 +248,7 @@ pub(crate) fn expression(expr: &Expr, checker: &mut Checker) { checker.diagnostics.push(diagnostic); } } - if let ScopeKind::Class(class_def) = checker.semantic.scope().kind { + if let ScopeKind::Class(class_def) = checker.semantic.current_scope().kind { if checker.enabled(Rule::BuiltinAttributeShadowing) { flake8_builtins::rules::builtin_attribute_shadowing( checker, class_def, id, *range, @@ -668,7 +673,7 @@ pub(crate) fn expression(expr: &Expr, checker: &mut Checker) { flake8_comprehensions::rules::unnecessary_map( checker, expr, - checker.semantic.expr_parent(), + checker.semantic.current_expression_parent(), func, args, ); @@ -1082,7 +1087,7 @@ pub(crate) fn expression(expr: &Expr, checker: &mut Checker) { // Avoid duplicate checks if the parent is an `|` since these rules // traverse nested unions. let is_unchecked_union = !matches!( - checker.semantic.expr_parent(), + checker.semantic.current_expression_parent(), Some(Expr::BinOp(ast::ExprBinOp { op: Operator::BitOr, .. diff --git a/crates/ruff/src/checkers/ast/analyze/statement.rs b/crates/ruff/src/checkers/ast/analyze/statement.rs index cbdbff9f5a..eee23db997 100644 --- a/crates/ruff/src/checkers/ast/analyze/statement.rs +++ b/crates/ruff/src/checkers/ast/analyze/statement.rs @@ -53,7 +53,7 @@ pub(crate) fn statement(stmt: &Stmt, checker: &mut Checker) { if checker.enabled(Rule::BreakOutsideLoop) { if let Some(diagnostic) = pyflakes::rules::break_outside_loop( stmt, - &mut checker.semantic.parents().skip(1), + &mut checker.semantic.current_statements().skip(1), ) { checker.diagnostics.push(diagnostic); } @@ -63,7 +63,7 @@ pub(crate) fn statement(stmt: &Stmt, checker: &mut Checker) { if checker.enabled(Rule::ContinueOutsideLoop) { if let Some(diagnostic) = pyflakes::rules::continue_outside_loop( stmt, - &mut checker.semantic.parents().skip(1), + &mut checker.semantic.current_statements().skip(1), ) { checker.diagnostics.push(diagnostic); } @@ -113,7 +113,7 @@ pub(crate) fn statement(stmt: &Stmt, checker: &mut Checker) { if let Some(diagnostic) = pep8_naming::rules::invalid_first_argument_name_for_class_method( checker, - checker.semantic.scope(), + checker.semantic.current_scope(), name, decorator_list, parameters, @@ -125,7 +125,7 @@ pub(crate) fn statement(stmt: &Stmt, checker: &mut Checker) { if checker.enabled(Rule::InvalidFirstArgumentNameForMethod) { if let Some(diagnostic) = pep8_naming::rules::invalid_first_argument_name_for_method( checker, - checker.semantic.scope(), + checker.semantic.current_scope(), name, decorator_list, parameters, @@ -193,7 +193,7 @@ pub(crate) fn statement(stmt: &Stmt, checker: &mut Checker) { } if checker.enabled(Rule::DunderFunctionName) { if let Some(diagnostic) = pep8_naming::rules::dunder_function_name( - checker.semantic.scope(), + checker.semantic.current_scope(), stmt, name, &checker.settings.pep8_naming.ignore_names, @@ -348,7 +348,7 @@ pub(crate) fn statement(stmt: &Stmt, checker: &mut Checker) { if checker.enabled(Rule::YieldInForLoop) { pyupgrade::rules::yield_in_for_loop(checker, stmt); } - if let ScopeKind::Class(class_def) = checker.semantic.scope().kind { + if let ScopeKind::Class(class_def) = checker.semantic.current_scope().kind { if checker.enabled(Rule::BuiltinAttributeShadowing) { flake8_builtins::rules::builtin_method_shadowing( checker, @@ -766,7 +766,7 @@ pub(crate) fn statement(stmt: &Stmt, checker: &mut Checker) { } } else if &alias.name == "*" { if checker.enabled(Rule::UndefinedLocalWithNestedImportStarUsage) { - if !matches!(checker.semantic.scope().kind, ScopeKind::Module) { + if !matches!(checker.semantic.current_scope().kind, ScopeKind::Module) { checker.diagnostics.push(Diagnostic::new( pyflakes::rules::UndefinedLocalWithNestedImportStarUsage { name: helpers::format_import_from(level, module), @@ -982,7 +982,7 @@ pub(crate) fn statement(stmt: &Stmt, checker: &mut Checker) { flake8_simplify::rules::nested_if_statements( checker, if_, - checker.semantic.stmt_parent(), + checker.semantic.current_statement_parent(), ); } if checker.enabled(Rule::IfWithSameArms) { @@ -1004,7 +1004,7 @@ pub(crate) fn statement(stmt: &Stmt, checker: &mut Checker) { tryceratops::rules::type_check_without_type_error( checker, if_, - checker.semantic.stmt_parent(), + checker.semantic.current_statement_parent(), ); } if checker.enabled(Rule::OutdatedVersionBlock) { @@ -1110,7 +1110,7 @@ pub(crate) fn statement(stmt: &Stmt, checker: &mut Checker) { checker, stmt, body, - checker.semantic.stmt_parent(), + checker.semantic.current_statement_parent(), ); } if checker.enabled(Rule::RedefinedLoopName) { @@ -1338,7 +1338,7 @@ pub(crate) fn statement(stmt: &Stmt, checker: &mut Checker) { // Ignore assignments in function bodies; those are covered by other rules. if !checker .semantic - .scopes() + .current_scopes() .any(|scope| scope.kind.is_any_function()) { if checker.enabled(Rule::UnprefixedTypeParam) { @@ -1403,7 +1403,7 @@ pub(crate) fn statement(stmt: &Stmt, checker: &mut Checker) { // Ignore assignments in function bodies; those are covered by other rules. if !checker .semantic - .scopes() + .current_scopes() .any(|scope| scope.kind.is_any_function()) { flake8_pyi::rules::annotated_assignment_default_in_stub( diff --git a/crates/ruff/src/checkers/ast/mod.rs b/crates/ruff/src/checkers/ast/mod.rs index 778d8fe81f..e604047ccf 100644 --- a/crates/ruff/src/checkers/ast/mod.rs +++ b/crates/ruff/src/checkers/ast/mod.rs @@ -182,7 +182,7 @@ impl<'a> Checker<'a> { } // Find the quote character used to start the containing f-string. - let expr = model.expr()?; + let expr = model.current_expression()?; let string_range = self.indexer.f_string_range(expr.start())?; let trailing_quote = trailing_quote(self.locator.slice(string_range))?; @@ -202,7 +202,7 @@ impl<'a> Checker<'a> { /// thus be applied whenever we delete a statement, but can otherwise be omitted. pub(crate) fn isolation(&self, parent: Option<&Stmt>) -> IsolationLevel { parent - .and_then(|stmt| self.semantic.stmts.node_id(stmt)) + .and_then(|stmt| self.semantic.statements.node_id(stmt)) .map_or(IsolationLevel::default(), |node_id| { IsolationLevel::Group(node_id.into()) }) @@ -264,7 +264,7 @@ where { fn visit_stmt(&mut self, stmt: &'b Stmt) { // Step 0: Pre-processing - self.semantic.push_stmt(stmt); + self.semantic.push_statement(stmt); // Track whether we've seen docstrings, non-imports, etc. match stmt { @@ -288,7 +288,7 @@ where self.semantic.flags |= SemanticModelFlags::FUTURES_BOUNDARY; if !self.semantic.seen_import_boundary() && !helpers::is_assignment_to_a_dunder(stmt) - && !helpers::in_nested_block(self.semantic.parents()) + && !helpers::in_nested_block(self.semantic.current_statements()) { self.semantic.flags |= SemanticModelFlags::IMPORT_BOUNDARY; } @@ -372,7 +372,7 @@ where ); } else if &alias.name == "*" { self.semantic - .scope_mut() + .current_scope_mut() .add_star_import(StarImport { level, module }); } else { let mut flags = BindingFlags::EXTERNAL; @@ -421,7 +421,7 @@ where BindingKind::Global, BindingFlags::GLOBAL, ); - let scope = self.semantic.scope_mut(); + let scope = self.semantic.current_scope_mut(); scope.add(name, binding_id); } } @@ -444,7 +444,7 @@ where BindingKind::Nonlocal(scope_id), BindingFlags::NONLOCAL, ); - let scope = self.semantic.scope_mut(); + let scope = self.semantic.current_scope_mut(); scope.add(name, binding_id); } } @@ -657,7 +657,7 @@ where // available at runtime. // See: https://docs.python.org/3/reference/simple_stmts.html#annotated-assignment-statements let runtime_annotation = if self.semantic.future_annotations() { - if self.semantic.scope().kind.is_class() { + if self.semantic.current_scope().kind.is_class() { let baseclasses = &self .settings .flake8_type_checking @@ -676,7 +676,7 @@ where } } else { matches!( - self.semantic.scope().kind, + self.semantic.current_scope().kind, ScopeKind::Class(_) | ScopeKind::Module ) }; @@ -777,7 +777,7 @@ where analyze::statement(stmt, self); self.semantic.flags = flags_snapshot; - self.semantic.pop_stmt(); + self.semantic.pop_statement(); } fn visit_annotation(&mut self, expr: &'b Expr) { @@ -813,7 +813,7 @@ where return; } - self.semantic.push_expr(expr); + self.semantic.push_expression(expr); // Store the flags prior to any further descent, so that we can restore them after visiting // the node. @@ -841,7 +841,7 @@ where }) => { if let Expr::Name(ast::ExprName { id, ctx, range: _ }) = func.as_ref() { if id == "locals" && ctx.is_load() { - let scope = self.semantic.scope_mut(); + let scope = self.semantic.current_scope_mut(); scope.set_uses_locals(); } } @@ -1231,7 +1231,7 @@ where analyze::expression(expr, self); self.semantic.flags = flags_snapshot; - self.semantic.pop_expr(); + self.semantic.pop_expression(); } fn visit_except_handler(&mut self, except_handler: &'b ExceptHandler) { @@ -1611,7 +1611,7 @@ impl<'a> Checker<'a> { } fn handle_node_store(&mut self, id: &'a str, expr: &Expr) { - let parent = self.semantic.stmt(); + let parent = self.semantic.current_statement(); if matches!( parent, @@ -1646,7 +1646,7 @@ impl<'a> Checker<'a> { return; } - let scope = self.semantic.scope(); + let scope = self.semantic.current_scope(); if scope.kind.is_module() && match parent { @@ -1696,7 +1696,11 @@ impl<'a> Checker<'a> { return; } - if self.semantic.expr_ancestors().any(Expr::is_named_expr_expr) { + if self + .semantic + .current_expressions() + .any(Expr::is_named_expr_expr) + { self.add_binding( id, expr.range(), @@ -1721,7 +1725,7 @@ impl<'a> Checker<'a> { self.semantic.resolve_del(id, expr.range()); - if helpers::on_conditional_branch(&mut self.semantic.parents()) { + if helpers::on_conditional_branch(&mut self.semantic.current_statements()) { return; } @@ -1729,7 +1733,7 @@ impl<'a> Checker<'a> { let binding_id = self.semantic .push_binding(expr.range(), BindingKind::Deletion, BindingFlags::empty()); - let scope = self.semantic.scope_mut(); + let scope = self.semantic.current_scope_mut(); scope.add(id, binding_id); } @@ -1821,7 +1825,7 @@ impl<'a> Checker<'a> { for snapshot in deferred_functions { self.semantic.restore(snapshot); - match &self.semantic.stmt() { + match &self.semantic.current_statement() { Stmt::FunctionDef(ast::StmtFunctionDef { body, parameters, .. }) diff --git a/crates/ruff/src/rules/flake8_bandit/rules/hardcoded_sql_expression.rs b/crates/ruff/src/rules/flake8_bandit/rules/hardcoded_sql_expression.rs index 5b63205d5f..2c7da19db0 100644 --- a/crates/ruff/src/rules/flake8_bandit/rules/hardcoded_sql_expression.rs +++ b/crates/ruff/src/rules/flake8_bandit/rules/hardcoded_sql_expression.rs @@ -63,7 +63,7 @@ fn matches_string_format_expression(expr: &Expr, model: &SemanticModel) -> bool }) => { // Only evaluate the full BinOp, not the nested components. if model - .expr_parent() + .current_expression_parent() .map_or(true, |parent| !parent.is_bin_op_expr()) { if any_over_expr(expr, &has_string_literal) { diff --git a/crates/ruff/src/rules/flake8_bugbear/rules/cached_instance_method.rs b/crates/ruff/src/rules/flake8_bugbear/rules/cached_instance_method.rs index 8b5d81ba25..61e99ed491 100644 --- a/crates/ruff/src/rules/flake8_bugbear/rules/cached_instance_method.rs +++ b/crates/ruff/src/rules/flake8_bugbear/rules/cached_instance_method.rs @@ -77,7 +77,7 @@ fn is_cache_func(expr: &Expr, semantic: &SemanticModel) -> bool { /// B019 pub(crate) fn cached_instance_method(checker: &mut Checker, decorator_list: &[Decorator]) { - if !checker.semantic().scope().kind.is_class() { + if !checker.semantic().current_scope().kind.is_class() { return; } for decorator in decorator_list { diff --git a/crates/ruff/src/rules/flake8_bugbear/rules/setattr_with_constant.rs b/crates/ruff/src/rules/flake8_bugbear/rules/setattr_with_constant.rs index 369391d5ab..ab5bc1462e 100644 --- a/crates/ruff/src/rules/flake8_bugbear/rules/setattr_with_constant.rs +++ b/crates/ruff/src/rules/flake8_bugbear/rules/setattr_with_constant.rs @@ -97,7 +97,7 @@ pub(crate) fn setattr_with_constant( if let Stmt::Expr(ast::StmtExpr { value: child, range: _, - }) = checker.semantic().stmt() + }) = checker.semantic().current_statement() { if expr == child.as_ref() { let mut diagnostic = Diagnostic::new(SetAttrWithConstant, expr.range()); diff --git a/crates/ruff/src/rules/flake8_bugbear/rules/unused_loop_control_variable.rs b/crates/ruff/src/rules/flake8_bugbear/rules/unused_loop_control_variable.rs index dc2553d200..8ab410ecd4 100644 --- a/crates/ruff/src/rules/flake8_bugbear/rules/unused_loop_control_variable.rs +++ b/crates/ruff/src/rules/flake8_bugbear/rules/unused_loop_control_variable.rs @@ -159,7 +159,7 @@ pub(crate) fn unused_loop_control_variable(checker: &mut Checker, target: &Expr, if certainty.into() { // Avoid fixing if the variable, or any future bindings to the variable, are // used _after_ the loop. - let scope = checker.semantic().scope(); + let scope = checker.semantic().current_scope(); if scope .get_all(name) .map(|binding_id| checker.semantic().binding(binding_id)) diff --git a/crates/ruff/src/rules/flake8_datetimez/rules/call_datetime_strptime_without_zone.rs b/crates/ruff/src/rules/flake8_datetimez/rules/call_datetime_strptime_without_zone.rs index 0cde6b272e..ede442bb41 100644 --- a/crates/ruff/src/rules/flake8_datetimez/rules/call_datetime_strptime_without_zone.rs +++ b/crates/ruff/src/rules/flake8_datetimez/rules/call_datetime_strptime_without_zone.rs @@ -43,8 +43,8 @@ pub(crate) fn call_datetime_strptime_without_zone(checker: &mut Checker, call: & }; let (Some(grandparent), Some(parent)) = ( - checker.semantic().expr_grandparent(), - checker.semantic().expr_parent(), + checker.semantic().current_expression_grandparent(), + checker.semantic().current_expression_parent(), ) else { checker.diagnostics.push(Diagnostic::new( CallDatetimeStrptimeWithoutZone, diff --git a/crates/ruff/src/rules/flake8_datetimez/rules/helpers.rs b/crates/ruff/src/rules/flake8_datetimez/rules/helpers.rs index 48ccb8af23..21ab64dc75 100644 --- a/crates/ruff/src/rules/flake8_datetimez/rules/helpers.rs +++ b/crates/ruff/src/rules/flake8_datetimez/rules/helpers.rs @@ -6,7 +6,7 @@ use crate::checkers::ast::Checker; /// Check if the parent expression is a call to `astimezone`. This assumes that /// the current expression is a `datetime.datetime` object. pub(super) fn parent_expr_is_astimezone(checker: &Checker) -> bool { - checker.semantic().expr_parent().is_some_and( |parent| { + checker.semantic().current_expression_parent().is_some_and( |parent| { matches!(parent, Expr::Attribute(ExprAttribute { attr, .. }) if attr.as_str() == "astimezone") }) } diff --git a/crates/ruff/src/rules/flake8_pyi/rules/any_eq_ne_annotation.rs b/crates/ruff/src/rules/flake8_pyi/rules/any_eq_ne_annotation.rs index a33106badd..ef1edc8064 100644 --- a/crates/ruff/src/rules/flake8_pyi/rules/any_eq_ne_annotation.rs +++ b/crates/ruff/src/rules/flake8_pyi/rules/any_eq_ne_annotation.rs @@ -66,7 +66,7 @@ pub(crate) fn any_eq_ne_annotation(checker: &mut Checker, name: &str, parameters return; }; - if !checker.semantic().scope().kind.is_class() { + if !checker.semantic().current_scope().kind.is_class() { return; } diff --git a/crates/ruff/src/rules/flake8_pyi/rules/custom_type_var_return_type.rs b/crates/ruff/src/rules/flake8_pyi/rules/custom_type_var_return_type.rs index 6dd0728e77..3528a58090 100644 --- a/crates/ruff/src/rules/flake8_pyi/rules/custom_type_var_return_type.rs +++ b/crates/ruff/src/rules/flake8_pyi/rules/custom_type_var_return_type.rs @@ -91,7 +91,7 @@ pub(crate) fn custom_type_var_return_type( return; }; - if !checker.semantic().scope().kind.is_class() { + if !checker.semantic().current_scope().kind.is_class() { return; }; diff --git a/crates/ruff/src/rules/flake8_pyi/rules/non_self_return_type.rs b/crates/ruff/src/rules/flake8_pyi/rules/non_self_return_type.rs index e88e6d3f37..f4b9e32ea2 100644 --- a/crates/ruff/src/rules/flake8_pyi/rules/non_self_return_type.rs +++ b/crates/ruff/src/rules/flake8_pyi/rules/non_self_return_type.rs @@ -119,7 +119,7 @@ pub(crate) fn non_self_return_type( parameters: &Parameters, async_: bool, ) { - let ScopeKind::Class(class_def) = checker.semantic().scope().kind else { + let ScopeKind::Class(class_def) = checker.semantic().current_scope().kind else { return; }; diff --git a/crates/ruff/src/rules/flake8_pyi/rules/simple_defaults.rs b/crates/ruff/src/rules/flake8_pyi/rules/simple_defaults.rs index ef3ef722af..4426760cd1 100644 --- a/crates/ruff/src/rules/flake8_pyi/rules/simple_defaults.rs +++ b/crates/ruff/src/rules/flake8_pyi/rules/simple_defaults.rs @@ -349,8 +349,8 @@ fn is_type_var_like_call(expr: &Expr, semantic: &SemanticModel) -> bool { fn is_special_assignment(target: &Expr, semantic: &SemanticModel) -> bool { if let Expr::Name(ast::ExprName { id, .. }) = target { match id.as_str() { - "__all__" => semantic.scope().kind.is_module(), - "__match_args__" | "__slots__" => semantic.scope().kind.is_class(), + "__all__" => semantic.current_scope().kind.is_module(), + "__match_args__" | "__slots__" => semantic.current_scope().kind.is_class(), _ => false, } } else { @@ -569,7 +569,9 @@ pub(crate) fn unannotated_assignment_in_stub( return; } - if let ScopeKind::Class(ast::StmtClassDef { arguments, .. }) = checker.semantic().scope().kind { + if let ScopeKind::Class(ast::StmtClassDef { arguments, .. }) = + checker.semantic().current_scope().kind + { if is_enum(arguments.as_deref(), checker.semantic()) { return; } diff --git a/crates/ruff/src/rules/flake8_pyi/rules/str_or_repr_defined_in_stub.rs b/crates/ruff/src/rules/flake8_pyi/rules/str_or_repr_defined_in_stub.rs index e220359c91..ad3b7cf470 100644 --- a/crates/ruff/src/rules/flake8_pyi/rules/str_or_repr_defined_in_stub.rs +++ b/crates/ruff/src/rules/flake8_pyi/rules/str_or_repr_defined_in_stub.rs @@ -63,7 +63,7 @@ pub(crate) fn str_or_repr_defined_in_stub(checker: &mut Checker, stmt: &Stmt) { return; } - if !checker.semantic().scope().kind.is_class() { + if !checker.semantic().current_scope().kind.is_class() { return; } @@ -96,11 +96,12 @@ pub(crate) fn str_or_repr_defined_in_stub(checker: &mut Checker, stmt: &Stmt) { stmt.identifier(), ); if checker.patch(diagnostic.kind.rule()) { - let stmt = checker.semantic().stmt(); - let parent = checker.semantic().stmt_parent(); + let stmt = checker.semantic().current_statement(); + let parent = checker.semantic().current_statement_parent(); let edit = delete_stmt(stmt, parent, checker.locator(), checker.indexer()); diagnostic.set_fix( - Fix::automatic(edit).isolate(checker.isolation(checker.semantic().stmt_parent())), + Fix::automatic(edit) + .isolate(checker.isolation(checker.semantic().current_statement_parent())), ); } checker.diagnostics.push(diagnostic); diff --git a/crates/ruff/src/rules/flake8_pyi/rules/string_or_bytes_too_long.rs b/crates/ruff/src/rules/flake8_pyi/rules/string_or_bytes_too_long.rs index 4fd45acd7f..c66ecf894f 100644 --- a/crates/ruff/src/rules/flake8_pyi/rules/string_or_bytes_too_long.rs +++ b/crates/ruff/src/rules/flake8_pyi/rules/string_or_bytes_too_long.rs @@ -43,7 +43,7 @@ impl AlwaysAutofixableViolation for StringOrBytesTooLong { /// PYI053 pub(crate) fn string_or_bytes_too_long(checker: &mut Checker, expr: &Expr) { // Ignore docstrings. - if is_docstring_stmt(checker.semantic().stmt()) { + if is_docstring_stmt(checker.semantic().current_statement()) { return; } diff --git a/crates/ruff/src/rules/flake8_pyi/rules/unused_private_type_definition.rs b/crates/ruff/src/rules/flake8_pyi/rules/unused_private_type_definition.rs index 01d93b90e7..4d90d77371 100644 --- a/crates/ruff/src/rules/flake8_pyi/rules/unused_private_type_definition.rs +++ b/crates/ruff/src/rules/flake8_pyi/rules/unused_private_type_definition.rs @@ -173,7 +173,8 @@ pub(crate) fn unused_private_type_var( let Some(source) = binding.source else { continue; }; - let Stmt::Assign(ast::StmtAssign { targets, value, .. }) = checker.semantic().stmts[source] + let Stmt::Assign(ast::StmtAssign { targets, value, .. }) = + checker.semantic().statements[source] else { continue; }; @@ -217,7 +218,7 @@ pub(crate) fn unused_private_protocol( continue; }; - let Stmt::ClassDef(class_def) = checker.semantic().stmts[source] else { + let Stmt::ClassDef(class_def) = checker.semantic().statements[source] else { continue; }; @@ -260,7 +261,7 @@ pub(crate) fn unused_private_type_alias( }; let Stmt::AnnAssign(ast::StmtAnnAssign { target, annotation, .. - }) = checker.semantic().stmts[source] + }) = checker.semantic().statements[source] else { continue; }; @@ -304,7 +305,7 @@ pub(crate) fn unused_private_typed_dict( let Some(source) = binding.source else { continue; }; - let Stmt::ClassDef(class_def) = checker.semantic().stmts[source] else { + let Stmt::ClassDef(class_def) = checker.semantic().statements[source] else { continue; }; diff --git a/crates/ruff/src/rules/flake8_pytest_style/rules/assertion.rs b/crates/ruff/src/rules/flake8_pytest_style/rules/assertion.rs index d12e1e6d28..c70982d93c 100644 --- a/crates/ruff/src/rules/flake8_pytest_style/rules/assertion.rs +++ b/crates/ruff/src/rules/flake8_pytest_style/rules/assertion.rs @@ -246,8 +246,8 @@ pub(crate) fn unittest_assertion( if checker.patch(diagnostic.kind.rule()) { // We're converting an expression to a statement, so avoid applying the fix if // the assertion is part of a larger expression. - if checker.semantic().stmt().is_expr_stmt() - && checker.semantic().expr_parent().is_none() + if checker.semantic().current_statement().is_expr_stmt() + && checker.semantic().current_expression_parent().is_none() && !checker.indexer().comment_ranges().intersects(expr.range()) { if let Ok(stmt) = unittest_assert.generate_assert(args, keywords) { diff --git a/crates/ruff/src/rules/flake8_self/rules/private_member_access.rs b/crates/ruff/src/rules/flake8_self/rules/private_member_access.rs index b33929d0ba..1f259e51c6 100644 --- a/crates/ruff/src/rules/flake8_self/rules/private_member_access.rs +++ b/crates/ruff/src/rules/flake8_self/rules/private_member_access.rs @@ -77,7 +77,7 @@ pub(crate) fn private_member_access(checker: &mut Checker, expr: &Expr) { // Ignore accesses on instances within special methods (e.g., `__eq__`). if let ScopeKind::Function(ast::StmtFunctionDef { name, .. }) = - checker.semantic().scope().kind + checker.semantic().current_scope().kind { if matches!( name.as_str(), diff --git a/crates/ruff/src/rules/flake8_simplify/rules/ast_unary_op.rs b/crates/ruff/src/rules/flake8_simplify/rules/ast_unary_op.rs index 5dc0eb575d..c2aa396b17 100644 --- a/crates/ruff/src/rules/flake8_simplify/rules/ast_unary_op.rs +++ b/crates/ruff/src/rules/flake8_simplify/rules/ast_unary_op.rs @@ -155,14 +155,14 @@ pub(crate) fn negation_with_equal_op( if !matches!(&ops[..], [CmpOp::Eq]) { return; } - if is_exception_check(checker.semantic().stmt()) { + if is_exception_check(checker.semantic().current_statement()) { return; } // Avoid flagging issues in dunder implementations. if let ScopeKind::Function(ast::StmtFunctionDef { name, .. }) | ScopeKind::AsyncFunction(ast::StmtAsyncFunctionDef { name, .. }) = - &checker.semantic().scope().kind + &checker.semantic().current_scope().kind { if is_dunder_method(name) { return; @@ -213,14 +213,14 @@ pub(crate) fn negation_with_not_equal_op( if !matches!(&ops[..], [CmpOp::NotEq]) { return; } - if is_exception_check(checker.semantic().stmt()) { + if is_exception_check(checker.semantic().current_statement()) { return; } // Avoid flagging issues in dunder implementations. if let ScopeKind::Function(ast::StmtFunctionDef { name, .. }) | ScopeKind::AsyncFunction(ast::StmtAsyncFunctionDef { name, .. }) = - &checker.semantic().scope().kind + &checker.semantic().current_scope().kind { if is_dunder_method(name) { return; diff --git a/crates/ruff/src/rules/flake8_simplify/rules/open_file_with_context_handler.rs b/crates/ruff/src/rules/flake8_simplify/rules/open_file_with_context_handler.rs index 9ee1a5fc14..5fd3a79c6d 100644 --- a/crates/ruff/src/rules/flake8_simplify/rules/open_file_with_context_handler.rs +++ b/crates/ruff/src/rules/flake8_simplify/rules/open_file_with_context_handler.rs @@ -43,7 +43,7 @@ impl Violation for OpenFileWithContextHandler { /// Return `true` if the current expression is nested in an `await /// exit_stack.enter_async_context` call. fn match_async_exit_stack(semantic: &SemanticModel) -> bool { - let Some(expr) = semantic.expr_grandparent() else { + let Some(expr) = semantic.current_expression_grandparent() else { return false; }; let Expr::Await(ast::ExprAwait { value, range: _ }) = expr else { @@ -58,7 +58,7 @@ fn match_async_exit_stack(semantic: &SemanticModel) -> bool { if attr != "enter_async_context" { return false; } - for parent in semantic.parents() { + for parent in semantic.current_statements() { if let Stmt::With(ast::StmtWith { items, .. }) = parent { for item in items { if let Expr::Call(ast::ExprCall { func, .. }) = &item.context_expr { @@ -77,7 +77,7 @@ fn match_async_exit_stack(semantic: &SemanticModel) -> bool { /// Return `true` if the current expression is nested in an /// `exit_stack.enter_context` call. fn match_exit_stack(semantic: &SemanticModel) -> bool { - let Some(expr) = semantic.expr_parent() else { + let Some(expr) = semantic.current_expression_parent() else { return false; }; let Expr::Call(ast::ExprCall { func, .. }) = expr else { @@ -89,7 +89,7 @@ fn match_exit_stack(semantic: &SemanticModel) -> bool { if attr != "enter_context" { return false; } - for parent in semantic.parents() { + for parent in semantic.current_statements() { if let Stmt::With(ast::StmtWith { items, .. }) = parent { for item in items { if let Expr::Call(ast::ExprCall { func, .. }) = &item.context_expr { @@ -133,7 +133,7 @@ pub(crate) fn open_file_with_context_handler(checker: &mut Checker, func: &Expr) } // Ex) `with open("foo.txt") as f: ...` - if checker.semantic().stmt().is_with_stmt() { + if checker.semantic().current_statement().is_with_stmt() { return; } diff --git a/crates/ruff/src/rules/flake8_simplify/rules/reimplemented_builtin.rs b/crates/ruff/src/rules/flake8_simplify/rules/reimplemented_builtin.rs index 04008a8811..989036b0e4 100644 --- a/crates/ruff/src/rules/flake8_simplify/rules/reimplemented_builtin.rs +++ b/crates/ruff/src/rules/flake8_simplify/rules/reimplemented_builtin.rs @@ -60,7 +60,7 @@ impl Violation for ReimplementedBuiltin { /// SIM110, SIM111 pub(crate) fn convert_for_loop_to_any_all(checker: &mut Checker, stmt: &Stmt) { - if !checker.semantic().scope().kind.is_any_function() { + if !checker.semantic().current_scope().kind.is_any_function() { return; } @@ -73,7 +73,7 @@ pub(crate) fn convert_for_loop_to_any_all(checker: &mut Checker, stmt: &Stmt) { // - `for` loop with an `else: return True` or `else: return False`. // - `for` loop followed by `return True` or `return False`. let Some(terminal) = match_else_return(stmt).or_else(|| { - let parent = checker.semantic().stmt_parent()?; + let parent = checker.semantic().current_statement_parent()?; let suite = traversal::suite(stmt, parent)?; let sibling = traversal::next_sibling(stmt, suite)?; match_sibling_return(stmt, sibling) diff --git a/crates/ruff/src/rules/flake8_type_checking/helpers.rs b/crates/ruff/src/rules/flake8_type_checking/helpers.rs index 001b7f2582..31b5c6d2c3 100644 --- a/crates/ruff/src/rules/flake8_type_checking/helpers.rs +++ b/crates/ruff/src/rules/flake8_type_checking/helpers.rs @@ -35,7 +35,7 @@ pub(crate) fn runtime_evaluated( } fn runtime_evaluated_base_class(base_classes: &[String], semantic: &SemanticModel) -> bool { - let ScopeKind::Class(class_def) = &semantic.scope().kind else { + let ScopeKind::Class(class_def) = &semantic.current_scope().kind else { return false; }; @@ -49,7 +49,7 @@ fn runtime_evaluated_base_class(base_classes: &[String], semantic: &SemanticMode } fn runtime_evaluated_decorators(decorators: &[String], semantic: &SemanticModel) -> bool { - let ScopeKind::Class(class_def) = &semantic.scope().kind else { + let ScopeKind::Class(class_def) = &semantic.current_scope().kind else { return false; }; diff --git a/crates/ruff/src/rules/flake8_type_checking/rules/empty_type_checking_block.rs b/crates/ruff/src/rules/flake8_type_checking/rules/empty_type_checking_block.rs index 5bb8533778..05b3f2cb1c 100644 --- a/crates/ruff/src/rules/flake8_type_checking/rules/empty_type_checking_block.rs +++ b/crates/ruff/src/rules/flake8_type_checking/rules/empty_type_checking_block.rs @@ -58,8 +58,8 @@ pub(crate) fn empty_type_checking_block(checker: &mut Checker, stmt: &ast::StmtI let mut diagnostic = Diagnostic::new(EmptyTypeCheckingBlock, stmt.range()); if checker.patch(diagnostic.kind.rule()) { // Delete the entire type-checking block. - let stmt = checker.semantic().stmt(); - let parent = checker.semantic().stmt_parent(); + let stmt = checker.semantic().current_statement(); + let parent = checker.semantic().current_statement_parent(); let edit = autofix::edits::delete_stmt(stmt, parent, checker.locator(), checker.indexer()); diagnostic.set_fix(Fix::automatic(edit).isolate(checker.isolation(parent))); } diff --git a/crates/ruff/src/rules/flake8_type_checking/rules/runtime_import_in_type_checking_block.rs b/crates/ruff/src/rules/flake8_type_checking/rules/runtime_import_in_type_checking_block.rs index 66c187fc55..5fc6722cf0 100644 --- a/crates/ruff/src/rules/flake8_type_checking/rules/runtime_import_in_type_checking_block.rs +++ b/crates/ruff/src/rules/flake8_type_checking/rules/runtime_import_in_type_checking_block.rs @@ -93,7 +93,7 @@ pub(crate) fn runtime_import_in_type_checking_block( .is_runtime() }) { - let Some(stmt_id) = binding.source else { + let Some(statement_id) = binding.source else { continue; }; @@ -113,20 +113,23 @@ pub(crate) fn runtime_import_in_type_checking_block( }) { ignores_by_statement - .entry(stmt_id) + .entry(statement_id) .or_default() .push(import); } else { - errors_by_statement.entry(stmt_id).or_default().push(import); + errors_by_statement + .entry(statement_id) + .or_default() + .push(import); } } } // Generate a diagnostic for every import, but share a fix across all imports within the same // statement (excluding those that are ignored). - for (stmt_id, imports) in errors_by_statement { + for (statement_id, imports) in errors_by_statement { let fix = if checker.patch(Rule::RuntimeImportInTypeCheckingBlock) { - fix_imports(checker, stmt_id, &imports).ok() + fix_imports(checker, statement_id, &imports).ok() } else { None }; @@ -189,9 +192,9 @@ struct ImportBinding<'a> { } /// Generate a [`Fix`] to remove runtime imports from a type-checking block. -fn fix_imports(checker: &Checker, stmt_id: NodeId, imports: &[ImportBinding]) -> Result { - let stmt = checker.semantic().stmts[stmt_id]; - let parent = checker.semantic().stmts.parent(stmt); +fn fix_imports(checker: &Checker, statement_id: NodeId, imports: &[ImportBinding]) -> Result { + let statement = checker.semantic().statements[statement_id]; + let parent = checker.semantic().statements.parent(statement); let member_names: Vec> = imports .iter() .map(|ImportBinding { import, .. }| import.member_name()) @@ -209,7 +212,7 @@ fn fix_imports(checker: &Checker, stmt_id: NodeId, imports: &[ImportBinding]) -> // Step 1) Remove the import. let remove_import_edit = autofix::edits::remove_unused_imports( member_names.iter().map(AsRef::as_ref), - stmt, + statement, parent, checker.locator(), checker.stylist(), @@ -219,7 +222,7 @@ fn fix_imports(checker: &Checker, stmt_id: NodeId, imports: &[ImportBinding]) -> // Step 2) Add the import to the top-level. let add_import_edit = checker.importer().runtime_import_edit( &ImportedMembers { - statement: stmt, + statement, names: member_names.iter().map(AsRef::as_ref).collect(), }, at, diff --git a/crates/ruff/src/rules/flake8_type_checking/rules/typing_only_runtime_import.rs b/crates/ruff/src/rules/flake8_type_checking/rules/typing_only_runtime_import.rs index 5d40de8cc5..15a7f658c2 100644 --- a/crates/ruff/src/rules/flake8_type_checking/rules/typing_only_runtime_import.rs +++ b/crates/ruff/src/rules/flake8_type_checking/rules/typing_only_runtime_import.rs @@ -265,7 +265,7 @@ pub(crate) fn typing_only_runtime_import( continue; } - let Some(stmt_id) = binding.source else { + let Some(statement_id) = binding.source else { continue; }; @@ -282,12 +282,12 @@ pub(crate) fn typing_only_runtime_import( }) { ignores_by_statement - .entry((stmt_id, import_type)) + .entry((statement_id, import_type)) .or_default() .push(import); } else { errors_by_statement - .entry((stmt_id, import_type)) + .entry((statement_id, import_type)) .or_default() .push(import); } @@ -296,9 +296,9 @@ pub(crate) fn typing_only_runtime_import( // Generate a diagnostic for every import, but share a fix across all imports within the same // statement (excluding those that are ignored). - for ((stmt_id, import_type), imports) in errors_by_statement { + for ((statement_id, import_type), imports) in errors_by_statement { let fix = if checker.patch(rule_for(import_type)) { - fix_imports(checker, stmt_id, &imports).ok() + fix_imports(checker, statement_id, &imports).ok() } else { None }; @@ -402,9 +402,9 @@ fn is_exempt(name: &str, exempt_modules: &[&str]) -> bool { } /// Generate a [`Fix`] to remove typing-only imports from a runtime context. -fn fix_imports(checker: &Checker, stmt_id: NodeId, imports: &[ImportBinding]) -> Result { - let stmt = checker.semantic().stmts[stmt_id]; - let parent = checker.semantic().stmts.parent(stmt); +fn fix_imports(checker: &Checker, statement_id: NodeId, imports: &[ImportBinding]) -> Result { + let statement = checker.semantic().statements[statement_id]; + let parent = checker.semantic().statements.parent(statement); let member_names: Vec> = imports .iter() .map(|ImportBinding { import, .. }| import.member_name()) @@ -422,7 +422,7 @@ fn fix_imports(checker: &Checker, stmt_id: NodeId, imports: &[ImportBinding]) -> // Step 1) Remove the import. let remove_import_edit = autofix::edits::remove_unused_imports( member_names.iter().map(AsRef::as_ref), - stmt, + statement, parent, checker.locator(), checker.stylist(), @@ -432,7 +432,7 @@ fn fix_imports(checker: &Checker, stmt_id: NodeId, imports: &[ImportBinding]) -> // Step 2) Add the import to a `TYPE_CHECKING` block. let add_import_edit = checker.importer().typing_import_edit( &ImportedMembers { - statement: stmt, + statement, names: member_names.iter().map(AsRef::as_ref).collect(), }, at, diff --git a/crates/ruff/src/rules/pandas_vet/rules/attr.rs b/crates/ruff/src/rules/pandas_vet/rules/attr.rs index de8981c63d..de4de9de9e 100644 --- a/crates/ruff/src/rules/pandas_vet/rules/attr.rs +++ b/crates/ruff/src/rules/pandas_vet/rules/attr.rs @@ -53,7 +53,7 @@ pub(crate) fn attr(checker: &mut Checker, attr: &str, value: &Expr, attr_expr: & }; // Avoid flagging on function calls (e.g., `df.values()`). - if let Some(parent) = checker.semantic().expr_parent() { + if let Some(parent) = checker.semantic().current_expression_parent() { if matches!(parent, Expr::Call(_)) { return; } diff --git a/crates/ruff/src/rules/pandas_vet/rules/inplace_argument.rs b/crates/ruff/src/rules/pandas_vet/rules/inplace_argument.rs index 2282ede228..7ac4a06a89 100644 --- a/crates/ruff/src/rules/pandas_vet/rules/inplace_argument.rs +++ b/crates/ruff/src/rules/pandas_vet/rules/inplace_argument.rs @@ -85,8 +85,8 @@ pub(crate) fn inplace_argument(checker: &mut Checker, call: &ast::ExprCall) { // 2. The call is part of a larger expression (we're converting an expression to a // statement, and expressions can't contain statements). if !seen_star - && checker.semantic().stmt().is_expr_stmt() - && checker.semantic().expr_parent().is_none() + && checker.semantic().current_statement().is_expr_stmt() + && checker.semantic().current_expression_parent().is_none() { if let Some(fix) = convert_inplace_argument_to_assignment( call, diff --git a/crates/ruff/src/rules/pep8_naming/rules/mixed_case_variable_in_class_scope.rs b/crates/ruff/src/rules/pep8_naming/rules/mixed_case_variable_in_class_scope.rs index 15770fb614..ac796dfe79 100644 --- a/crates/ruff/src/rules/pep8_naming/rules/mixed_case_variable_in_class_scope.rs +++ b/crates/ruff/src/rules/pep8_naming/rules/mixed_case_variable_in_class_scope.rs @@ -69,7 +69,7 @@ pub(crate) fn mixed_case_variable_in_class_scope( return; } - let parent = checker.semantic().stmt(); + let parent = checker.semantic().current_statement(); if helpers::is_named_tuple_assignment(parent, checker.semantic()) || helpers::is_typed_dict_class(arguments, checker.semantic()) diff --git a/crates/ruff/src/rules/pep8_naming/rules/mixed_case_variable_in_global_scope.rs b/crates/ruff/src/rules/pep8_naming/rules/mixed_case_variable_in_global_scope.rs index a280e85286..4eb648fe8f 100644 --- a/crates/ruff/src/rules/pep8_naming/rules/mixed_case_variable_in_global_scope.rs +++ b/crates/ruff/src/rules/pep8_naming/rules/mixed_case_variable_in_global_scope.rs @@ -75,7 +75,7 @@ pub(crate) fn mixed_case_variable_in_global_scope(checker: &mut Checker, expr: & return; } - let parent = checker.semantic().stmt(); + let parent = checker.semantic().current_statement(); if helpers::is_named_tuple_assignment(parent, checker.semantic()) { return; } diff --git a/crates/ruff/src/rules/pep8_naming/rules/non_lowercase_variable_in_function.rs b/crates/ruff/src/rules/pep8_naming/rules/non_lowercase_variable_in_function.rs index 87383353aa..41a7fea0ca 100644 --- a/crates/ruff/src/rules/pep8_naming/rules/non_lowercase_variable_in_function.rs +++ b/crates/ruff/src/rules/pep8_naming/rules/non_lowercase_variable_in_function.rs @@ -65,7 +65,7 @@ pub(crate) fn non_lowercase_variable_in_function(checker: &mut Checker, expr: &E return; } - let parent = checker.semantic().stmt(); + let parent = checker.semantic().current_statement(); if helpers::is_named_tuple_assignment(parent, checker.semantic()) || helpers::is_typed_dict_assignment(parent, checker.semantic()) || helpers::is_type_var_assignment(parent, checker.semantic()) diff --git a/crates/ruff/src/rules/perflint/rules/incorrect_dict_iterator.rs b/crates/ruff/src/rules/perflint/rules/incorrect_dict_iterator.rs index 677d655525..58e1ed7e07 100644 --- a/crates/ruff/src/rules/perflint/rules/incorrect_dict_iterator.rs +++ b/crates/ruff/src/rules/perflint/rules/incorrect_dict_iterator.rs @@ -167,7 +167,7 @@ fn is_unused(expr: &Expr, model: &SemanticModel) -> bool { // // print(bar) // ``` - let scope = model.scope(); + let scope = model.current_scope(); scope .get_all(id) .map(|binding_id| model.binding(binding_id)) diff --git a/crates/ruff/src/rules/perflint/rules/unnecessary_list_cast.rs b/crates/ruff/src/rules/perflint/rules/unnecessary_list_cast.rs index 6ea2569024..67551e0988 100644 --- a/crates/ruff/src/rules/perflint/rules/unnecessary_list_cast.rs +++ b/crates/ruff/src/rules/perflint/rules/unnecessary_list_cast.rs @@ -102,12 +102,12 @@ pub(crate) fn unnecessary_list_cast(checker: &mut Checker, iter: &Expr) { range: iterable_range, .. }) => { - let scope = checker.semantic().scope(); + let scope = checker.semantic().current_scope(); if let Some(binding_id) = scope.get(id) { let binding = checker.semantic().binding(binding_id); if binding.kind.is_assignment() || binding.kind.is_named_expr_assignment() { if let Some(parent_id) = binding.source { - let parent = checker.semantic().stmts[parent_id]; + let parent = checker.semantic().statements[parent_id]; if let Stmt::Assign(ast::StmtAssign { value, .. }) | Stmt::AnnAssign(ast::StmtAnnAssign { value: Some(value), .. diff --git a/crates/ruff/src/rules/pycodestyle/rules/lambda_assignment.rs b/crates/ruff/src/rules/pycodestyle/rules/lambda_assignment.rs index f909ac3fea..7ae7764574 100644 --- a/crates/ruff/src/rules/pycodestyle/rules/lambda_assignment.rs +++ b/crates/ruff/src/rules/pycodestyle/rules/lambda_assignment.rs @@ -116,10 +116,10 @@ pub(crate) fn lambda_assignment( // rewriting it as a function declaration may break type-checking. // See: https://github.com/astral-sh/ruff/issues/3046 // See: https://github.com/astral-sh/ruff/issues/5421 - if (annotation.is_some() && checker.semantic().scope().kind.is_class()) + if (annotation.is_some() && checker.semantic().current_scope().kind.is_class()) || checker .semantic() - .scope() + .current_scope() .get_all(id) .any(|binding_id| checker.semantic().binding(binding_id).kind.is_annotation()) { diff --git a/crates/ruff/src/rules/pyflakes/rules/return_outside_function.rs b/crates/ruff/src/rules/pyflakes/rules/return_outside_function.rs index 6028e57491..c6416d5e3d 100644 --- a/crates/ruff/src/rules/pyflakes/rules/return_outside_function.rs +++ b/crates/ruff/src/rules/pyflakes/rules/return_outside_function.rs @@ -33,7 +33,7 @@ impl Violation for ReturnOutsideFunction { pub(crate) fn return_outside_function(checker: &mut Checker, stmt: &Stmt) { if matches!( - checker.semantic().scope().kind, + checker.semantic().current_scope().kind, ScopeKind::Class(_) | ScopeKind::Module ) { checker diff --git a/crates/ruff/src/rules/pyflakes/rules/unused_import.rs b/crates/ruff/src/rules/pyflakes/rules/unused_import.rs index 54aaea3f40..22d39f3e23 100644 --- a/crates/ruff/src/rules/pyflakes/rules/unused_import.rs +++ b/crates/ruff/src/rules/pyflakes/rules/unused_import.rs @@ -116,7 +116,7 @@ pub(crate) fn unused_import(checker: &Checker, scope: &Scope, diagnostics: &mut continue; }; - let Some(stmt_id) = binding.source else { + let Some(statement_id) = binding.source else { continue; }; @@ -132,12 +132,12 @@ pub(crate) fn unused_import(checker: &Checker, scope: &Scope, diagnostics: &mut }) { ignored - .entry((stmt_id, binding.exceptions)) + .entry((statement_id, binding.exceptions)) .or_default() .push(import); } else { unused - .entry((stmt_id, binding.exceptions)) + .entry((statement_id, binding.exceptions)) .or_default() .push(import); } @@ -148,13 +148,13 @@ pub(crate) fn unused_import(checker: &Checker, scope: &Scope, diagnostics: &mut // Generate a diagnostic for every import, but share a fix across all imports within the same // statement (excluding those that are ignored). - for ((stmt_id, exceptions), imports) in unused { + for ((statement_id, exceptions), imports) in unused { let in_except_handler = exceptions.intersects(Exceptions::MODULE_NOT_FOUND_ERROR | Exceptions::IMPORT_ERROR); let multiple = imports.len() > 1; let fix = if !in_init && !in_except_handler && checker.patch(Rule::UnusedImport) { - fix_imports(checker, stmt_id, &imports).ok() + fix_imports(checker, statement_id, &imports).ok() } else { None }; @@ -225,9 +225,9 @@ struct ImportBinding<'a> { } /// Generate a [`Fix`] to remove unused imports from a statement. -fn fix_imports(checker: &Checker, stmt_id: NodeId, imports: &[ImportBinding]) -> Result { - let stmt = checker.semantic().stmts[stmt_id]; - let parent = checker.semantic().stmts.parent(stmt); +fn fix_imports(checker: &Checker, statement_id: NodeId, imports: &[ImportBinding]) -> Result { + let statement = checker.semantic().statements[statement_id]; + let parent = checker.semantic().statements.parent(statement); let member_names: Vec> = imports .iter() @@ -236,7 +236,7 @@ fn fix_imports(checker: &Checker, stmt_id: NodeId, imports: &[ImportBinding]) -> let edit = autofix::edits::remove_unused_imports( member_names.iter().map(AsRef::as_ref), - stmt, + statement, parent, checker.locator(), checker.stylist(), diff --git a/crates/ruff/src/rules/pyflakes/rules/unused_variable.rs b/crates/ruff/src/rules/pyflakes/rules/unused_variable.rs index 94cbad3727..911582c29e 100644 --- a/crates/ruff/src/rules/pyflakes/rules/unused_variable.rs +++ b/crates/ruff/src/rules/pyflakes/rules/unused_variable.rs @@ -326,8 +326,8 @@ pub(crate) fn unused_variable(checker: &Checker, scope: &Scope, diagnostics: &mu let mut diagnostic = Diagnostic::new(UnusedVariable { name }, range); if checker.patch(diagnostic.kind.rule()) { if let Some(source) = source { - let stmt = checker.semantic().stmts[source]; - let parent = checker.semantic().stmts.parent(stmt); + let stmt = checker.semantic().statements[source]; + let parent = checker.semantic().statements.parent(stmt); if let Some(fix) = remove_unused_variable(stmt, parent, range, checker) { diagnostic.set_fix(fix); } diff --git a/crates/ruff/src/rules/pyflakes/rules/yield_outside_function.rs b/crates/ruff/src/rules/pyflakes/rules/yield_outside_function.rs index 57c6b4b687..355610adc2 100644 --- a/crates/ruff/src/rules/pyflakes/rules/yield_outside_function.rs +++ b/crates/ruff/src/rules/pyflakes/rules/yield_outside_function.rs @@ -55,7 +55,7 @@ impl Violation for YieldOutsideFunction { pub(crate) fn yield_outside_function(checker: &mut Checker, expr: &Expr) { if matches!( - checker.semantic().scope().kind, + checker.semantic().current_scope().kind, ScopeKind::Class(_) | ScopeKind::Module ) { let keyword = match expr { diff --git a/crates/ruff/src/rules/pylint/helpers.rs b/crates/ruff/src/rules/pylint/helpers.rs index 425771c55b..25ca39c544 100644 --- a/crates/ruff/src/rules/pylint/helpers.rs +++ b/crates/ruff/src/rules/pylint/helpers.rs @@ -23,7 +23,7 @@ pub(super) fn type_param_name(arguments: &Arguments) -> Option<&str> { } pub(super) fn in_dunder_init(semantic: &SemanticModel, settings: &Settings) -> bool { - let scope = semantic.scope(); + let scope = semantic.current_scope(); let (ScopeKind::Function(ast::StmtFunctionDef { name, decorator_list, diff --git a/crates/ruff/src/rules/pylint/rules/compare_to_empty_string.rs b/crates/ruff/src/rules/pylint/rules/compare_to_empty_string.rs index bb2c4d9049..a6d34938fd 100644 --- a/crates/ruff/src/rules/pylint/rules/compare_to_empty_string.rs +++ b/crates/ruff/src/rules/pylint/rules/compare_to_empty_string.rs @@ -67,7 +67,7 @@ pub(crate) fn compare_to_empty_string( // DataFrame and np.ndarray indexing. if checker .semantic() - .expr_ancestors() + .current_expressions() .any(Expr::is_subscript_expr) { return; diff --git a/crates/ruff/src/rules/pylint/rules/invalid_str_return.rs b/crates/ruff/src/rules/pylint/rules/invalid_str_return.rs index 74eddd0f92..85a8ae570d 100644 --- a/crates/ruff/src/rules/pylint/rules/invalid_str_return.rs +++ b/crates/ruff/src/rules/pylint/rules/invalid_str_return.rs @@ -29,7 +29,7 @@ pub(crate) fn invalid_str_return(checker: &mut Checker, name: &str, body: &[Stmt return; } - if !checker.semantic().scope().kind.is_class() { + if !checker.semantic().current_scope().kind.is_class() { return; } diff --git a/crates/ruff/src/rules/pylint/rules/self_assigning_variable.rs b/crates/ruff/src/rules/pylint/rules/self_assigning_variable.rs index fe54bb484e..4176626a33 100644 --- a/crates/ruff/src/rules/pylint/rules/self_assigning_variable.rs +++ b/crates/ruff/src/rules/pylint/rules/self_assigning_variable.rs @@ -62,7 +62,7 @@ pub(crate) fn self_assigning_variable(checker: &mut Checker, target: &Expr, valu // Assignments in class bodies are attributes (e.g., `x = x` assigns `x` to `self.x`, and thus // is not a self-assignment). - if checker.semantic().scope().kind.is_class() { + if checker.semantic().current_scope().kind.is_class() { return; } diff --git a/crates/ruff/src/rules/pylint/rules/unexpected_special_method_signature.rs b/crates/ruff/src/rules/pylint/rules/unexpected_special_method_signature.rs index 5727667c82..09182ef384 100644 --- a/crates/ruff/src/rules/pylint/rules/unexpected_special_method_signature.rs +++ b/crates/ruff/src/rules/pylint/rules/unexpected_special_method_signature.rs @@ -143,7 +143,7 @@ pub(crate) fn unexpected_special_method_signature( decorator_list: &[Decorator], parameters: &Parameters, ) { - if !checker.semantic().scope().kind.is_class() { + if !checker.semantic().current_scope().kind.is_class() { return; } diff --git a/crates/ruff/src/rules/pylint/rules/yield_from_in_async_function.rs b/crates/ruff/src/rules/pylint/rules/yield_from_in_async_function.rs index 5ed24e994a..723a927ed4 100644 --- a/crates/ruff/src/rules/pylint/rules/yield_from_in_async_function.rs +++ b/crates/ruff/src/rules/pylint/rules/yield_from_in_async_function.rs @@ -38,7 +38,7 @@ impl Violation for YieldFromInAsyncFunction { /// PLE1700 pub(crate) fn yield_from_in_async_function(checker: &mut Checker, expr: &ExprYieldFrom) { - let scope = checker.semantic().scope(); + let scope = checker.semantic().current_scope(); if scope.kind.is_async_function() { checker .diagnostics diff --git a/crates/ruff/src/rules/pyupgrade/rules/native_literals.rs b/crates/ruff/src/rules/pyupgrade/rules/native_literals.rs index f0942da106..07dde88bcb 100644 --- a/crates/ruff/src/rules/pyupgrade/rules/native_literals.rs +++ b/crates/ruff/src/rules/pyupgrade/rules/native_literals.rs @@ -149,7 +149,7 @@ pub(crate) fn native_literals( if checker.semantic().in_f_string() { if checker .semantic() - .expr_ancestors() + .current_expressions() .filter(|expr| expr.is_joined_str_expr()) .count() > 1 diff --git a/crates/ruff/src/rules/pyupgrade/rules/outdated_version_block.rs b/crates/ruff/src/rules/pyupgrade/rules/outdated_version_block.rs index 493b8413c7..25fecdf628 100644 --- a/crates/ruff/src/rules/pyupgrade/rules/outdated_version_block.rs +++ b/crates/ruff/src/rules/pyupgrade/rules/outdated_version_block.rs @@ -124,8 +124,8 @@ fn fix_py2_block(checker: &Checker, stmt_if: &StmtIf, branch: &IfElifBranch) -> BranchKind::If => match stmt_if.elif_else_clauses.first() { // If we have a lone `if`, delete as statement (insert pass in parent if required) None => { - let stmt = checker.semantic().stmt(); - let parent = checker.semantic().stmt_parent(); + let stmt = checker.semantic().current_statement(); + let parent = checker.semantic().current_statement_parent(); let edit = delete_stmt(stmt, parent, checker.locator(), checker.indexer()); Some(Fix::suggested(edit)) } diff --git a/crates/ruff/src/rules/pyupgrade/rules/super_call_with_parameters.rs b/crates/ruff/src/rules/pyupgrade/rules/super_call_with_parameters.rs index 8a67f5cf8d..457de15f0e 100644 --- a/crates/ruff/src/rules/pyupgrade/rules/super_call_with_parameters.rs +++ b/crates/ruff/src/rules/pyupgrade/rules/super_call_with_parameters.rs @@ -80,14 +80,14 @@ pub(crate) fn super_call_with_parameters( if !is_super_call_with_arguments(func, args) { return; } - let scope = checker.semantic().scope(); + let scope = checker.semantic().current_scope(); // Check: are we in a Function scope? if !scope.kind.is_any_function() { return; } - let mut parents = checker.semantic().parents(); + let mut parents = checker.semantic().current_statements(); // For a `super` invocation to be unnecessary, the first argument needs to match // the enclosing class, and the second argument needs to match the first diff --git a/crates/ruff/src/rules/pyupgrade/rules/unnecessary_builtin_import.rs b/crates/ruff/src/rules/pyupgrade/rules/unnecessary_builtin_import.rs index 17fbcbe284..bf7e6724b2 100644 --- a/crates/ruff/src/rules/pyupgrade/rules/unnecessary_builtin_import.rs +++ b/crates/ruff/src/rules/pyupgrade/rules/unnecessary_builtin_import.rs @@ -122,14 +122,14 @@ pub(crate) fn unnecessary_builtin_import( ); if checker.patch(diagnostic.kind.rule()) { diagnostic.try_set_fix(|| { - let stmt = checker.semantic().stmt(); - let parent = checker.semantic().stmt_parent(); + let statement = checker.semantic().current_statement(); + let parent = checker.semantic().current_statement_parent(); let edit = autofix::edits::remove_unused_imports( unused_imports .iter() .map(|alias| &alias.name) .map(ruff_python_ast::Identifier::as_str), - stmt, + statement, parent, checker.locator(), checker.stylist(), diff --git a/crates/ruff/src/rules/pyupgrade/rules/unnecessary_future_import.rs b/crates/ruff/src/rules/pyupgrade/rules/unnecessary_future_import.rs index ec4e4d7ba8..2cbf8dbe06 100644 --- a/crates/ruff/src/rules/pyupgrade/rules/unnecessary_future_import.rs +++ b/crates/ruff/src/rules/pyupgrade/rules/unnecessary_future_import.rs @@ -111,14 +111,14 @@ pub(crate) fn unnecessary_future_import(checker: &mut Checker, stmt: &Stmt, name if checker.patch(diagnostic.kind.rule()) { diagnostic.try_set_fix(|| { - let stmt = checker.semantic().stmt(); - let parent = checker.semantic().stmt_parent(); + let statement = checker.semantic().current_statement(); + let parent = checker.semantic().current_statement_parent(); let edit = autofix::edits::remove_unused_imports( unused_imports .iter() .map(|alias| &alias.name) .map(ruff_python_ast::Identifier::as_str), - stmt, + statement, parent, checker.locator(), checker.stylist(), diff --git a/crates/ruff/src/rules/pyupgrade/rules/useless_metaclass_type.rs b/crates/ruff/src/rules/pyupgrade/rules/useless_metaclass_type.rs index 70fd141857..c0ee032ef9 100644 --- a/crates/ruff/src/rules/pyupgrade/rules/useless_metaclass_type.rs +++ b/crates/ruff/src/rules/pyupgrade/rules/useless_metaclass_type.rs @@ -63,8 +63,8 @@ pub(crate) fn useless_metaclass_type( let mut diagnostic = Diagnostic::new(UselessMetaclassType, stmt.range()); if checker.patch(diagnostic.kind.rule()) { - let stmt = checker.semantic().stmt(); - let parent = checker.semantic().stmt_parent(); + let stmt = checker.semantic().current_statement(); + let parent = checker.semantic().current_statement_parent(); let edit = autofix::edits::delete_stmt(stmt, parent, checker.locator(), checker.indexer()); diagnostic.set_fix(Fix::automatic(edit).isolate(checker.isolation(parent))); } diff --git a/crates/ruff/src/rules/ruff/rules/collection_literal_concatenation.rs b/crates/ruff/src/rules/ruff/rules/collection_literal_concatenation.rs index a2156f8e11..125f950e10 100644 --- a/crates/ruff/src/rules/ruff/rules/collection_literal_concatenation.rs +++ b/crates/ruff/src/rules/ruff/rules/collection_literal_concatenation.rs @@ -166,7 +166,7 @@ fn concatenate_expressions(expr: &Expr) -> Option<(Expr, Type)> { pub(crate) fn collection_literal_concatenation(checker: &mut Checker, expr: &Expr) { // If the expression is already a child of an addition, we'll have analyzed it already. if matches!( - checker.semantic().expr_parent(), + checker.semantic().current_expression_parent(), Some(Expr::BinOp(ast::ExprBinOp { op: Operator::Add, .. diff --git a/crates/ruff_python_semantic/src/binding.rs b/crates/ruff_python_semantic/src/binding.rs index 10e1900888..ef06cce239 100644 --- a/crates/ruff_python_semantic/src/binding.rs +++ b/crates/ruff_python_semantic/src/binding.rs @@ -185,7 +185,7 @@ impl<'a> Binding<'a> { /// Returns the range of the binding's parent. pub fn parent_range(&self, semantic: &SemanticModel) -> Option { self.source - .map(|node_id| semantic.stmts[node_id]) + .map(|node_id| semantic.statements[node_id]) .and_then(|parent| { if parent.is_import_from_stmt() { Some(parent.range()) diff --git a/crates/ruff_python_semantic/src/model.rs b/crates/ruff_python_semantic/src/model.rs index e66e1c211e..5238269da3 100644 --- a/crates/ruff_python_semantic/src/model.rs +++ b/crates/ruff_python_semantic/src/model.rs @@ -31,16 +31,16 @@ pub struct SemanticModel<'a> { module_path: Option<&'a [String]>, /// Stack of all visited statements. - pub stmts: Nodes<'a, Stmt>, + pub statements: Nodes<'a, Stmt>, /// The identifier of the current statement. - stmt_id: Option, + statement_id: Option, /// Stack of all visited expressions. - exprs: Nodes<'a, Expr>, + expressions: Nodes<'a, Expr>, /// The identifier of the current expression. - expr_id: Option, + expression_id: Option, /// Stack of all scopes, along with the identifier of the current scope. pub scopes: Scopes<'a>, @@ -132,10 +132,10 @@ impl<'a> SemanticModel<'a> { Self { typing_modules, module_path: module.path(), - stmts: Nodes::::default(), - stmt_id: None, - exprs: Nodes::::default(), - expr_id: None, + statements: Nodes::::default(), + statement_id: None, + expressions: Nodes::::default(), + expression_id: None, scopes: Scopes::default(), scope_id: ScopeId::global(), definitions: Definitions::for_module(module), @@ -225,7 +225,7 @@ impl<'a> SemanticModel<'a> { flags, references: Vec::new(), scope: self.scope_id, - source: self.stmt_id, + source: self.statement_id, context: self.execution_context(), exceptions: self.exceptions(), }) @@ -233,7 +233,7 @@ impl<'a> SemanticModel<'a> { /// Return the current [`Binding`] for a given `name`. pub fn find_binding(&self, member: &str) -> Option<&Binding> { - self.scopes() + self.current_scopes() .find_map(|scope| scope.get(member)) .map(|binding_id| &self.bindings[binding_id]) } @@ -686,102 +686,110 @@ impl<'a> SemanticModel<'a> { ) -> Option { // TODO(charlie): Pass in a slice. let module_path: Vec<&str> = module.split('.').collect(); - self.scopes().enumerate().find_map(|(scope_index, scope)| { - scope.bindings().find_map(|(name, binding_id)| { - let binding = &self.bindings[binding_id]; - match &binding.kind { - // Ex) Given `module="sys"` and `object="exit"`: - // `import sys` -> `sys.exit` - // `import sys as sys2` -> `sys2.exit` - BindingKind::Import(Import { call_path }) => { - if call_path.as_ref() == module_path.as_slice() { - if let Some(source) = binding.source { - // Verify that `sys` isn't bound in an inner scope. - if self - .scopes() - .take(scope_index) - .all(|scope| !scope.has(name)) - { - return Some(ImportedName { - name: format!("{name}.{member}"), - range: self.stmts[source].range(), - context: binding.context, - }); - } - } - } - } - // Ex) Given `module="os.path"` and `object="join"`: - // `from os.path import join` -> `join` - // `from os.path import join as join2` -> `join2` - BindingKind::FromImport(FromImport { call_path }) => { - if let Some((target_member, target_module)) = call_path.split_last() { - if target_module == module_path.as_slice() && target_member == &member { + self.current_scopes() + .enumerate() + .find_map(|(scope_index, scope)| { + scope.bindings().find_map(|(name, binding_id)| { + let binding = &self.bindings[binding_id]; + match &binding.kind { + // Ex) Given `module="sys"` and `object="exit"`: + // `import sys` -> `sys.exit` + // `import sys as sys2` -> `sys2.exit` + BindingKind::Import(Import { call_path }) => { + if call_path.as_ref() == module_path.as_slice() { if let Some(source) = binding.source { - // Verify that `join` isn't bound in an inner scope. + // Verify that `sys` isn't bound in an inner scope. if self - .scopes() + .current_scopes() .take(scope_index) .all(|scope| !scope.has(name)) { return Some(ImportedName { - name: (*name).to_string(), - range: self.stmts[source].range(), + name: format!("{name}.{member}"), + range: self.statements[source].range(), context: binding.context, }); } } } } - } - // Ex) Given `module="os"` and `object="name"`: - // `import os.path ` -> `os.name` - BindingKind::SubmoduleImport(SubmoduleImport { .. }) => { - if name == module { - if let Some(source) = binding.source { - // Verify that `os` isn't bound in an inner scope. - if self - .scopes() - .take(scope_index) - .all(|scope| !scope.has(name)) + // Ex) Given `module="os.path"` and `object="join"`: + // `from os.path import join` -> `join` + // `from os.path import join as join2` -> `join2` + BindingKind::FromImport(FromImport { call_path }) => { + if let Some((target_member, target_module)) = call_path.split_last() { + if target_module == module_path.as_slice() + && target_member == &member { - return Some(ImportedName { - name: format!("{name}.{member}"), - range: self.stmts[source].range(), - context: binding.context, - }); + if let Some(source) = binding.source { + // Verify that `join` isn't bound in an inner scope. + if self + .current_scopes() + .take(scope_index) + .all(|scope| !scope.has(name)) + { + return Some(ImportedName { + name: (*name).to_string(), + range: self.statements[source].range(), + context: binding.context, + }); + } + } } } } + // Ex) Given `module="os"` and `object="name"`: + // `import os.path ` -> `os.name` + BindingKind::SubmoduleImport(SubmoduleImport { .. }) => { + if name == module { + if let Some(source) = binding.source { + // Verify that `os` isn't bound in an inner scope. + if self + .current_scopes() + .take(scope_index) + .all(|scope| !scope.has(name)) + { + return Some(ImportedName { + name: format!("{name}.{member}"), + range: self.statements[source].range(), + context: binding.context, + }); + } + } + } + } + // Non-imports. + _ => {} } - // Non-imports. - _ => {} - } - None + None + }) }) - }) } /// Push a [`Stmt`] onto the stack. - pub fn push_stmt(&mut self, stmt: &'a Stmt) { - self.stmt_id = Some(self.stmts.insert(stmt, self.stmt_id)); + pub fn push_statement(&mut self, stmt: &'a Stmt) { + self.statement_id = Some(self.statements.insert(stmt, self.statement_id)); } /// Pop the current [`Stmt`] off the stack. - pub fn pop_stmt(&mut self) { - let node_id = self.stmt_id.expect("Attempted to pop without statement"); - self.stmt_id = self.stmts.parent_id(node_id); + pub fn pop_statement(&mut self) { + let node_id = self + .statement_id + .expect("Attempted to pop without statement"); + self.statement_id = self.statements.parent_id(node_id); } /// Push a [`Expr`] onto the stack. - pub fn push_expr(&mut self, expr: &'a Expr) { - self.expr_id = Some(self.exprs.insert(expr, self.expr_id)); + pub fn push_expression(&mut self, expr: &'a Expr) { + self.expression_id = Some(self.expressions.insert(expr, self.expression_id)); } /// Pop the current [`Expr`] off the stack. - pub fn pop_expr(&mut self) { - let node_id = self.expr_id.expect("Attempted to pop without expression"); - self.expr_id = self.exprs.parent_id(node_id); + pub fn pop_expression(&mut self) { + let node_id = self + .expression_id + .expect("Attempted to pop without expression"); + self.expression_id = self.expressions.parent_id(node_id); } /// Push a [`Scope`] with the given [`ScopeKind`] onto the stack. @@ -811,69 +819,94 @@ impl<'a> SemanticModel<'a> { } /// Return the current `Stmt`. - pub fn stmt(&self) -> &'a Stmt { - let node_id = self.stmt_id.expect("No current statement"); - self.stmts[node_id] + pub fn current_statement(&self) -> &'a Stmt { + let node_id = self.statement_id.expect("No current statement"); + self.statements[node_id] } - /// Return the parent `Stmt` of the current `Stmt`, if any. - pub fn stmt_parent(&self) -> Option<&'a Stmt> { - let node_id = self.stmt_id.expect("No current statement"); - let parent_id = self.stmts.parent_id(node_id)?; - Some(self.stmts[parent_id]) - } - - /// Return the current `Expr`. - pub fn expr(&self) -> Option<&'a Expr> { - let node_id = self.expr_id?; - Some(self.exprs[node_id]) - } - - /// Return the parent `Expr` of the current `Expr`, if any. - pub fn expr_parent(&self) -> Option<&'a Expr> { - self.expr_ancestors().next() - } - - /// Return the grandparent `Expr` of the current `Expr`, if any. - pub fn expr_grandparent(&self) -> Option<&'a Expr> { - self.expr_ancestors().nth(1) - } - - /// Return an [`Iterator`] over the current `Expr` parents. - pub fn expr_ancestors(&self) -> impl Iterator + '_ { - self.expr_id + /// Returns an [`Iterator`] over the current statement hierarchy, from the current [`Stmt`] + /// through to any parents. + pub fn current_statements(&self) -> impl Iterator + '_ { + self.statement_id .iter() - .copied() .flat_map(|id| { - self.exprs - .ancestor_ids(id) - .skip(1) - .map(|id| &self.exprs[id]) + self.statements + .ancestor_ids(*id) + .map(|id| &self.statements[id]) }) .copied() } - /// Returns a reference to the global scope + /// Return the parent `Stmt` of the current `Stmt`, if any. + pub fn current_statement_parent(&self) -> Option<&'a Stmt> { + self.current_statements().nth(1) + } + + /// Return the grandparent `Stmt` of the current `Stmt`, if any. + pub fn current_statement_grandparent(&self) -> Option<&'a Stmt> { + self.current_statements().nth(2) + } + + /// Return the current `Expr`. + pub fn current_expression(&self) -> Option<&'a Expr> { + let node_id = self.expression_id?; + Some(self.expressions[node_id]) + } + + /// Returns an [`Iterator`] over the current statement hierarchy, from the current [`Expr`] + /// through to any parents. + pub fn current_expressions(&self) -> impl Iterator + '_ { + self.expression_id + .iter() + .flat_map(|id| { + self.expressions + .ancestor_ids(*id) + .map(|id| &self.expressions[id]) + }) + .copied() + } + + /// Return the parent [`Expr`] of the current [`Expr`], if any. + pub fn current_expression_parent(&self) -> Option<&'a Expr> { + self.current_expressions().nth(1) + } + + /// Return the grandparent [`Expr`] of the current [`Expr`], if any. + pub fn current_expression_grandparent(&self) -> Option<&'a Expr> { + self.current_expressions().nth(2) + } + + /// Returns a reference to the global [`Scope`]. pub fn global_scope(&self) -> &Scope<'a> { self.scopes.global() } - /// Returns a mutable reference to the global scope + /// Returns a mutable reference to the global [`Scope`]. pub fn global_scope_mut(&mut self) -> &mut Scope<'a> { self.scopes.global_mut() } - /// Returns the current top most scope. - pub fn scope(&self) -> &Scope<'a> { + /// Returns the current top-most [`Scope`]. + pub fn current_scope(&self) -> &Scope<'a> { &self.scopes[self.scope_id] } - /// Returns the parent of the given scope, if any. + /// Returns a mutable reference to the current top-most [`Scope`]. + pub fn current_scope_mut(&mut self) -> &mut Scope<'a> { + &mut self.scopes[self.scope_id] + } + + /// Returns an iterator over all scopes, starting from the current [`Scope`]. + pub fn current_scopes(&self) -> impl Iterator { + self.scopes.ancestors(self.scope_id) + } + + /// Returns the parent of the given [`Scope`], if any. pub fn parent_scope(&self, scope: &Scope) -> Option<&Scope<'a>> { scope.parent.map(|scope_id| &self.scopes[scope_id]) } - /// Returns the first parent of the given scope that is not a [`ScopeKind::Type`] scope, if any. + /// Returns the first parent of the given [`Scope`] that is not of [`ScopeKind::Type`], if any. pub fn first_non_type_parent_scope(&self, scope: &Scope) -> Option<&Scope<'a>> { let mut current_scope = scope; while let Some(parent) = self.parent_scope(current_scope) { @@ -886,22 +919,6 @@ impl<'a> SemanticModel<'a> { None } - /// Returns a mutable reference to the current top most scope. - pub fn scope_mut(&mut self) -> &mut Scope<'a> { - &mut self.scopes[self.scope_id] - } - - /// Returns an iterator over all scopes, starting from the current scope. - pub fn scopes(&self) -> impl Iterator { - self.scopes.ancestors(self.scope_id) - } - - /// Returns an iterator over all parent statements. - pub fn parents(&self) -> impl Iterator + '_ { - let node_id = self.stmt_id.expect("No current statement"); - self.stmts.ancestor_ids(node_id).map(|id| self.stmts[id]) - } - /// Set the [`Globals`] for the current [`Scope`]. pub fn set_globals(&mut self, globals: Globals<'a>) { // If any global bindings don't already exist in the global scope, add them. @@ -916,7 +933,7 @@ impl<'a> SemanticModel<'a> { range: *range, references: Vec::new(), scope: self.scope_id, - source: self.stmt_id, + source: self.statement_id, context: self.execution_context(), exceptions: self.exceptions(), flags: BindingFlags::empty(), @@ -964,13 +981,13 @@ impl<'a> SemanticModel<'a> { pub fn at_top_level(&self) -> bool { self.scope_id.is_global() && self - .stmt_id - .map_or(true, |stmt_id| self.stmts.parent_id(stmt_id).is_none()) + .statement_id + .map_or(true, |stmt_id| self.statements.parent_id(stmt_id).is_none()) } /// Return `true` if the model is in an async context. pub fn in_async_context(&self) -> bool { - for scope in self.scopes() { + for scope in self.current_scopes() { if scope.kind.is_async_function() { return true; } @@ -1048,8 +1065,8 @@ impl<'a> SemanticModel<'a> { pub fn snapshot(&self) -> Snapshot { Snapshot { scope_id: self.scope_id, - stmt_id: self.stmt_id, - expr_id: self.expr_id, + stmt_id: self.statement_id, + expr_id: self.expression_id, definition_id: self.definition_id, flags: self.flags, } @@ -1065,8 +1082,8 @@ impl<'a> SemanticModel<'a> { flags, } = snapshot; self.scope_id = scope_id; - self.stmt_id = stmt_id; - self.expr_id = expr_id; + self.statement_id = stmt_id; + self.expression_id = expr_id; self.definition_id = definition_id; self.flags = flags; }