mirror of
https://github.com/astral-sh/ruff.git
synced 2025-08-02 18:02:23 +00:00
Rename semantic model methods to use current_*
prefix (#6347)
## Summary This PR attempts to draw a clearer divide between "methods that take (e.g.) an expression or statement as input" and "methods that rely on the _current_ expression or statement" in the semantic model, by renaming methods like `stmt()` to `current_statement()`. This had led to confusion in the past. For example, prior to this PR, we had `scope()` (which returns the current scope), and `parent_scope`, which returns the parent _of a scope that's passed in_. Now, the API is clearer: `current_scope` returns the current scope, and `parent_scope` takes a scope as argument and returns its parent. Per above, I also changed `stmt` to `statement` and `expr` to `expression`.
This commit is contained in:
parent
b763973357
commit
bae87fa016
54 changed files with 331 additions and 290 deletions
|
@ -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);
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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,
|
||||
..
|
||||
|
|
|
@ -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(
|
||||
|
|
|
@ -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, ..
|
||||
})
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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());
|
||||
|
|
|
@ -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))
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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")
|
||||
})
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
@ -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;
|
||||
};
|
||||
|
||||
|
|
|
@ -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;
|
||||
};
|
||||
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
@ -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;
|
||||
};
|
||||
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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(),
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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;
|
||||
};
|
||||
|
||||
|
|
|
@ -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)));
|
||||
}
|
||||
|
|
|
@ -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<Fix> {
|
||||
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<Fix> {
|
||||
let statement = checker.semantic().statements[statement_id];
|
||||
let parent = checker.semantic().statements.parent(statement);
|
||||
let member_names: Vec<Cow<'_, str>> = 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,
|
||||
|
|
|
@ -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<Fix> {
|
||||
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<Fix> {
|
||||
let statement = checker.semantic().statements[statement_id];
|
||||
let parent = checker.semantic().statements.parent(statement);
|
||||
let member_names: Vec<Cow<'_, str>> = 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,
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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())
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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())
|
||||
|
|
|
@ -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))
|
||||
|
|
|
@ -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), ..
|
||||
|
|
|
@ -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())
|
||||
{
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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<Fix> {
|
||||
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<Fix> {
|
||||
let statement = checker.semantic().statements[statement_id];
|
||||
let parent = checker.semantic().statements.parent(statement);
|
||||
|
||||
let member_names: Vec<Cow<'_, str>> = 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(),
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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))
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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(),
|
||||
|
|
|
@ -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(),
|
||||
|
|
|
@ -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)));
|
||||
}
|
||||
|
|
|
@ -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,
|
||||
..
|
||||
|
|
|
@ -185,7 +185,7 @@ impl<'a> Binding<'a> {
|
|||
/// Returns the range of the binding's parent.
|
||||
pub fn parent_range(&self, semantic: &SemanticModel) -> Option<TextRange> {
|
||||
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())
|
||||
|
|
|
@ -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<NodeId>,
|
||||
statement_id: Option<NodeId>,
|
||||
|
||||
/// Stack of all visited expressions.
|
||||
exprs: Nodes<'a, Expr>,
|
||||
expressions: Nodes<'a, Expr>,
|
||||
|
||||
/// The identifier of the current expression.
|
||||
expr_id: Option<NodeId>,
|
||||
expression_id: Option<NodeId>,
|
||||
|
||||
/// 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::<Stmt>::default(),
|
||||
stmt_id: None,
|
||||
exprs: Nodes::<Expr>::default(),
|
||||
expr_id: None,
|
||||
statements: Nodes::<Stmt>::default(),
|
||||
statement_id: None,
|
||||
expressions: Nodes::<Expr>::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<ImportedName> {
|
||||
// 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<Item = &'a Expr> + '_ {
|
||||
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<Item = &'a Stmt> + '_ {
|
||||
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<Item = &'a Expr> + '_ {
|
||||
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<Item = &Scope> {
|
||||
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<Item = &Scope> {
|
||||
self.scopes.ancestors(self.scope_id)
|
||||
}
|
||||
|
||||
/// Returns an iterator over all parent statements.
|
||||
pub fn parents(&self) -> impl Iterator<Item = &Stmt> + '_ {
|
||||
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;
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue