diff --git a/crates/ruff_graph/src/collector.rs b/crates/ruff_graph/src/collector.rs index e7349ae072..d265b3ff7c 100644 --- a/crates/ruff_graph/src/collector.rs +++ b/crates/ruff_graph/src/collector.rs @@ -42,13 +42,14 @@ impl<'a> Collector<'a> { impl<'ast> SourceOrderVisitor<'ast> for Collector<'_> { fn visit_stmt(&mut self, stmt: &'ast Stmt) { match stmt { - Stmt::ImportFrom(ast::StmtImportFrom { - names, - module, - level, - range: _, - node_index: _, - }) => { + Stmt::ImportFrom(import_from) => { + let ast::StmtImportFrom { + names, + module, + level, + range: _, + node_index: _, + } = &**import_from; let module = module.as_deref(); let level = *level; for alias in names { @@ -87,24 +88,26 @@ impl<'ast> SourceOrderVisitor<'ast> for Collector<'_> { } } } - Stmt::Import(ast::StmtImport { - names, - range: _, - node_index: _, - }) => { + Stmt::Import(import_stmt) => { + let ast::StmtImport { + names, + range: _, + node_index: _, + } = &**import_stmt; for alias in names { if let Some(module_name) = ModuleName::new(alias.name.as_str()) { self.imports.push(CollectedImport::Import(module_name)); } } } - Stmt::If(ast::StmtIf { - test, - body, - elif_else_clauses, - range: _, - node_index: _, - }) => { + Stmt::If(if_stmt) => { + let ast::StmtIf { + test, + body, + elif_else_clauses, + range: _, + node_index: _, + } = &**if_stmt; // Skip TYPE_CHECKING blocks if not requested if self.type_checking_imports || !is_type_checking_condition(test) { self.visit_body(body); diff --git a/crates/ruff_linter/src/checkers/ast/analyze/statement.rs b/crates/ruff_linter/src/checkers/ast/analyze/statement.rs index e1b4da60f2..88d9616a0e 100644 --- a/crates/ruff_linter/src/checkers/ast/analyze/statement.rs +++ b/crates/ruff_linter/src/checkers/ast/analyze/statement.rs @@ -17,11 +17,12 @@ use ruff_python_ast::PythonVersion; /// Run lint rules over a [`Stmt`] syntax node. pub(crate) fn statement(stmt: &Stmt, checker: &mut Checker) { match stmt { - Stmt::Global(ast::StmtGlobal { - names, - range: _, - node_index: _, - }) => { + Stmt::Global(global) => { + let ast::StmtGlobal { + names, + range: _, + node_index: _, + } = &**global; if checker.is_rule_enabled(Rule::GlobalAtModuleLevel) { pylint::rules::global_at_module_level(checker, stmt); } @@ -31,13 +32,12 @@ pub(crate) fn statement(stmt: &Stmt, checker: &mut Checker) { } } } - Stmt::Nonlocal( - nonlocal @ ast::StmtNonlocal { + Stmt::Nonlocal(nonlocal) => { + let ast::StmtNonlocal { names, range: _, node_index: _, - }, - ) => { + } = &**nonlocal; if checker.is_rule_enabled(Rule::AmbiguousVariableName) { for name in names { pycodestyle::rules::ambiguous_variable_name(checker, name, name.range()); @@ -47,8 +47,8 @@ pub(crate) fn statement(stmt: &Stmt, checker: &mut Checker) { pylint::rules::nonlocal_and_global(checker, nonlocal); } } - Stmt::FunctionDef( - function_def @ ast::StmtFunctionDef { + Stmt::FunctionDef(function_def) => { + let ast::StmtFunctionDef { is_async, name, decorator_list, @@ -58,8 +58,7 @@ pub(crate) fn statement(stmt: &Stmt, checker: &mut Checker) { type_params: _, range: _, node_index: _, - }, - ) => { + } = &**function_def; if checker.is_rule_enabled(Rule::DjangoNonLeadingReceiverDecorator) { flake8_django::rules::non_leading_receiver_decorator(checker, decorator_list); } @@ -321,7 +320,7 @@ pub(crate) fn statement(stmt: &Stmt, checker: &mut Checker) { pylint::rules::in_function(checker, name, body); } if checker.is_rule_enabled(Rule::ReimplementedOperator) { - refurb::rules::reimplemented_operator(checker, &function_def.into()); + refurb::rules::reimplemented_operator(checker, &(&**function_def).into()); } if checker.is_rule_enabled(Rule::SslWithBadDefaults) { flake8_bandit::rules::ssl_with_bad_defaults(checker, function_def); @@ -356,8 +355,8 @@ pub(crate) fn statement(stmt: &Stmt, checker: &mut Checker) { pylint::rules::return_in_init(checker, stmt); } } - Stmt::ClassDef( - class_def @ ast::StmtClassDef { + Stmt::ClassDef(class_def) => { + let ast::StmtClassDef { name, arguments, type_params: _, @@ -365,8 +364,7 @@ pub(crate) fn statement(stmt: &Stmt, checker: &mut Checker) { body, range: _, node_index: _, - }, - ) => { + } = &**class_def; if checker.is_rule_enabled(Rule::NoClassmethodDecorator) { pylint::rules::no_classmethod_decorator(checker, stmt); } @@ -526,11 +524,12 @@ pub(crate) fn statement(stmt: &Stmt, checker: &mut Checker) { ruff::rules::implicit_class_var_in_dataclass(checker, class_def); } } - Stmt::Import(ast::StmtImport { - names, - range: _, - node_index: _, - }) => { + Stmt::Import(import) => { + let ast::StmtImport { + names, + range: _, + node_index: _, + } = &**import; if checker.is_rule_enabled(Rule::MultipleImportsOnOneLine) { pycodestyle::rules::multiple_imports_on_one_line(checker, stmt, names); } @@ -578,7 +577,7 @@ pub(crate) fn statement(stmt: &Stmt, checker: &mut Checker) { flake8_tidy_imports::rules::banned_module_level_imports(checker, stmt); } - for alias in names { + for alias in &import.names { if checker.is_rule_enabled(Rule::NonAsciiImportName) { pylint::rules::non_ascii_module_import(checker, alias); } @@ -604,7 +603,7 @@ pub(crate) fn statement(stmt: &Stmt, checker: &mut Checker) { } } if checker.is_rule_enabled(Rule::ManualFromImport) { - pylint::rules::manual_from_import(checker, stmt, alias, names); + pylint::rules::manual_from_import(checker, stmt, alias, &import.names); } if checker.is_rule_enabled(Rule::ImportSelf) { pylint::rules::import_self(checker, alias, checker.module.qualified_name()); @@ -681,17 +680,9 @@ pub(crate) fn statement(stmt: &Stmt, checker: &mut Checker) { } } } - Stmt::ImportFrom( - import_from @ ast::StmtImportFrom { - names, - module, - level, - range: _, - node_index: _, - }, - ) => { - let level = *level; - let module = module.as_deref(); + Stmt::ImportFrom(import_from) => { + let level = import_from.level; + let module = import_from.module.as_deref(); if checker.is_rule_enabled(Rule::ModuleImportNotAtTopOfFile) { pycodestyle::rules::module_import_not_at_top_of_file(checker, stmt); } @@ -699,7 +690,7 @@ pub(crate) fn statement(stmt: &Stmt, checker: &mut Checker) { pylint::rules::import_outside_top_level(checker, stmt); } if checker.is_rule_enabled(Rule::GlobalStatement) { - for name in names { + for name in &import_from.names { if let Some(asname) = name.asname.as_ref() { pylint::rules::global_statement(checker, asname); } else { @@ -708,7 +699,7 @@ pub(crate) fn statement(stmt: &Stmt, checker: &mut Checker) { } } if checker.is_rule_enabled(Rule::NonAsciiImportName) { - for alias in names { + for alias in &import_from.names { pylint::rules::non_ascii_module_import(checker, alias); } } @@ -724,7 +715,7 @@ pub(crate) fn statement(stmt: &Stmt, checker: &mut Checker) { if checker.is_rule_enabled(Rule::UnnecessaryBuiltinImport) { if let Some(module) = module { pyupgrade::rules::unnecessary_builtin_import( - checker, stmt, module, names, level, + checker, stmt, module, &import_from.names, level, ); } } @@ -760,7 +751,7 @@ pub(crate) fn statement(stmt: &Stmt, checker: &mut Checker) { &stmt, ); - for alias in names { + for alias in &import_from.names { if &alias.name == "*" { continue; } @@ -789,7 +780,7 @@ pub(crate) fn statement(stmt: &Stmt, checker: &mut Checker) { flake8_pyi::rules::from_future_import(checker, import_from); } } - for alias in names { + for alias in &import_from.names { if module != Some("__future__") && &alias.name == "*" { // F403 checker.report_diagnostic_if_enabled( @@ -890,7 +881,7 @@ pub(crate) fn statement(stmt: &Stmt, checker: &mut Checker) { checker, level, module, - names, + &import_from.names, checker.module.qualified_name(), ); } @@ -906,14 +897,14 @@ pub(crate) fn statement(stmt: &Stmt, checker: &mut Checker) { flake8_pyi::rules::bytestring_import(checker, import_from); } } - Stmt::Raise(raise @ ast::StmtRaise { exc, .. }) => { + Stmt::Raise(raise) => { if checker.is_rule_enabled(Rule::RaiseNotImplemented) { - if let Some(expr) = exc { + if let Some(expr) = &raise.exc { pyflakes::rules::raise_not_implemented(checker, expr); } } if checker.is_rule_enabled(Rule::RaiseLiteral) { - if let Some(exc) = exc { + if let Some(exc) = &raise.exc { flake8_bugbear::rules::raise_literal(checker, exc); } } @@ -922,34 +913,34 @@ pub(crate) fn statement(stmt: &Stmt, checker: &mut Checker) { Rule::FStringInException, Rule::DotFormatInException, ]) { - if let Some(exc) = exc { + if let Some(exc) = &raise.exc { flake8_errmsg::rules::string_in_exception(checker, stmt, exc); } } if checker.is_rule_enabled(Rule::OSErrorAlias) { - if let Some(item) = exc { + if let Some(item) = &raise.exc { pyupgrade::rules::os_error_alias_raise(checker, item); } } if checker.is_rule_enabled(Rule::TimeoutErrorAlias) { if checker.target_version() >= PythonVersion::PY310 { - if let Some(item) = exc { + if let Some(item) = &raise.exc { pyupgrade::rules::timeout_error_alias_raise(checker, item); } } } if checker.is_rule_enabled(Rule::RaiseVanillaClass) { - if let Some(expr) = exc { + if let Some(expr) = &raise.exc { tryceratops::rules::raise_vanilla_class(checker, expr); } } if checker.is_rule_enabled(Rule::RaiseVanillaArgs) { - if let Some(expr) = exc { + if let Some(expr) = &raise.exc { tryceratops::rules::raise_vanilla_args(checker, expr); } } if checker.is_rule_enabled(Rule::UnnecessaryParenOnRaiseException) { - if let Some(expr) = exc { + if let Some(expr) = &raise.exc { flake8_raise::rules::unnecessary_paren_on_raise_exception(checker, expr); } } @@ -957,9 +948,9 @@ pub(crate) fn statement(stmt: &Stmt, checker: &mut Checker) { pylint::rules::misplaced_bare_raise(checker, raise); } } - Stmt::AugAssign(aug_assign @ ast::StmtAugAssign { target, .. }) => { + Stmt::AugAssign(aug_assign) => { if checker.is_rule_enabled(Rule::GlobalStatement) { - if let Expr::Name(ast::ExprName { id, .. }) = target.as_ref() { + if let Expr::Name(ast::ExprName { id, .. }) = aug_assign.target.as_ref() { pylint::rules::global_statement(checker, id); } } @@ -967,13 +958,7 @@ pub(crate) fn statement(stmt: &Stmt, checker: &mut Checker) { ruff::rules::sort_dunder_all_aug_assign(checker, aug_assign); } } - Stmt::If( - if_ @ ast::StmtIf { - test, - elif_else_clauses, - .. - }, - ) => { + Stmt::If(if_) => { if checker.is_rule_enabled(Rule::TooManyNestedBlocks) { pylint::rules::too_many_nested_blocks(checker, stmt); } @@ -1036,33 +1021,33 @@ pub(crate) fn statement(stmt: &Stmt, checker: &mut Checker) { Rule::PatchVersionComparison, Rule::WrongTupleLengthVersionComparison, ]) { - if let Expr::BoolOp(ast::ExprBoolOp { values, .. }) = test.as_ref() { + if let Expr::BoolOp(ast::ExprBoolOp { values, .. }) = if_.test.as_ref() { for value in values { flake8_pyi::rules::unrecognized_version_info(checker, value); } } else { - flake8_pyi::rules::unrecognized_version_info(checker, test); + flake8_pyi::rules::unrecognized_version_info(checker, &if_.test); } } if checker.any_rule_enabled(&[ Rule::UnrecognizedPlatformCheck, Rule::UnrecognizedPlatformName, ]) { - if let Expr::BoolOp(ast::ExprBoolOp { values, .. }) = test.as_ref() { + if let Expr::BoolOp(ast::ExprBoolOp { values, .. }) = if_.test.as_ref() { for value in values { flake8_pyi::rules::unrecognized_platform(checker, value); } } else { - flake8_pyi::rules::unrecognized_platform(checker, test); + flake8_pyi::rules::unrecognized_platform(checker, &if_.test); } } if checker.is_rule_enabled(Rule::ComplexIfStatementInStub) { - if let Expr::BoolOp(ast::ExprBoolOp { values, .. }) = test.as_ref() { + if let Expr::BoolOp(ast::ExprBoolOp { values, .. }) = if_.test.as_ref() { for value in values { flake8_pyi::rules::complex_if_statement_in_stub(checker, value); } } else { - flake8_pyi::rules::complex_if_statement_in_stub(checker, test); + flake8_pyi::rules::complex_if_statement_in_stub(checker, &if_.test); } } } @@ -1091,10 +1076,10 @@ pub(crate) fn statement(stmt: &Stmt, checker: &mut Checker) { } } - let has_else_clause = elif_else_clauses.iter().any(|clause| clause.test.is_none()); + let has_else_clause = if_.elif_else_clauses.iter().any(|clause| clause.test.is_none()); - bad_version_info_comparison(checker, test.as_ref(), has_else_clause); - for clause in elif_else_clauses { + bad_version_info_comparison(checker, if_.test.as_ref(), has_else_clause); + for clause in &if_.elif_else_clauses { if let Some(test) = clause.test.as_ref() { bad_version_info_comparison(checker, test, has_else_clause); } @@ -1105,44 +1090,37 @@ pub(crate) fn statement(stmt: &Stmt, checker: &mut Checker) { ruff::rules::if_key_in_dict_del(checker, if_); } if checker.is_rule_enabled(Rule::NeedlessElse) { - ruff::rules::needless_else(checker, if_.into()); + ruff::rules::needless_else(checker, (&**if_).into()); } } - Stmt::Assert( - assert_stmt @ ast::StmtAssert { - test, - msg, - range: _, - node_index: _, - }, - ) => { + Stmt::Assert(assert_stmt) => { if !checker.semantic.in_type_checking_block() { if checker.is_rule_enabled(Rule::Assert) { flake8_bandit::rules::assert_used(checker, stmt); } } if checker.is_rule_enabled(Rule::AssertTuple) { - pyflakes::rules::assert_tuple(checker, stmt, test); + pyflakes::rules::assert_tuple(checker, stmt, &assert_stmt.test); } if checker.is_rule_enabled(Rule::AssertFalse) { - flake8_bugbear::rules::assert_false(checker, stmt, test, msg.as_deref()); + flake8_bugbear::rules::assert_false(checker, stmt, &assert_stmt.test, assert_stmt.msg.as_deref()); } if checker.is_rule_enabled(Rule::PytestAssertAlwaysFalse) { - flake8_pytest_style::rules::assert_falsy(checker, stmt, test); + flake8_pytest_style::rules::assert_falsy(checker, stmt, &assert_stmt.test); } if checker.is_rule_enabled(Rule::PytestCompositeAssertion) { flake8_pytest_style::rules::composite_condition( checker, stmt, - test, - msg.as_deref(), + &assert_stmt.test, + assert_stmt.msg.as_deref(), ); } if checker.is_rule_enabled(Rule::AssertOnStringLiteral) { - pylint::rules::assert_on_string_literal(checker, test); + pylint::rules::assert_on_string_literal(checker, &assert_stmt.test); } if checker.is_rule_enabled(Rule::InvalidMockAccess) { - pygrep_hooks::rules::non_existent_mock_method(checker, test); + pygrep_hooks::rules::non_existent_mock_method(checker, &assert_stmt.test); } if checker.is_rule_enabled(Rule::AssertWithPrintMessage) { ruff::rules::assert_with_print_message(checker, assert_stmt); @@ -1151,18 +1129,18 @@ pub(crate) fn statement(stmt: &Stmt, checker: &mut Checker) { ruff::rules::invalid_assert_message_literal_argument(checker, assert_stmt); } } - Stmt::With(with_stmt @ ast::StmtWith { items, body, .. }) => { + Stmt::With(with_stmt) => { if checker.is_rule_enabled(Rule::TooManyNestedBlocks) { pylint::rules::too_many_nested_blocks(checker, stmt); } if checker.is_rule_enabled(Rule::AssertRaisesException) { - flake8_bugbear::rules::assert_raises_exception(checker, items); + flake8_bugbear::rules::assert_raises_exception(checker, &with_stmt.items); } if checker.is_rule_enabled(Rule::PytestRaisesWithMultipleStatements) { - flake8_pytest_style::rules::complex_raises(checker, stmt, items, body); + flake8_pytest_style::rules::complex_raises(checker, stmt, &with_stmt.items, &with_stmt.body); } if checker.is_rule_enabled(Rule::PytestWarnsWithMultipleStatements) { - flake8_pytest_style::rules::complex_warns(checker, stmt, items, body); + flake8_pytest_style::rules::complex_warns(checker, stmt, &with_stmt.items, &with_stmt.body); } if checker.is_rule_enabled(Rule::MultipleWithStatements) { flake8_simplify::rules::multiple_with_statements( @@ -1184,10 +1162,10 @@ pub(crate) fn statement(stmt: &Stmt, checker: &mut Checker) { pylint::rules::useless_with_lock(checker, with_stmt); } if checker.is_rule_enabled(Rule::CancelScopeNoCheckpoint) { - flake8_async::rules::cancel_scope_no_checkpoint(checker, with_stmt, items); + flake8_async::rules::cancel_scope_no_checkpoint(checker, with_stmt, &with_stmt.items); } } - Stmt::While(while_stmt @ ast::StmtWhile { body, orelse, .. }) => { + Stmt::While(while_stmt) => { if checker.is_rule_enabled(Rule::TooManyNestedBlocks) { pylint::rules::too_many_nested_blocks(checker, stmt); } @@ -1195,29 +1173,19 @@ pub(crate) fn statement(stmt: &Stmt, checker: &mut Checker) { flake8_bugbear::rules::function_uses_loop_variable(checker, &Node::Stmt(stmt)); } if checker.is_rule_enabled(Rule::UselessElseOnLoop) { - pylint::rules::useless_else_on_loop(checker, stmt, body, orelse); + pylint::rules::useless_else_on_loop(checker, stmt, &while_stmt.body, &while_stmt.orelse); } if checker.is_rule_enabled(Rule::TryExceptInLoop) { - perflint::rules::try_except_in_loop(checker, body); + perflint::rules::try_except_in_loop(checker, &while_stmt.body); } if checker.is_rule_enabled(Rule::AsyncBusyWait) { flake8_async::rules::async_busy_wait(checker, while_stmt); } if checker.is_rule_enabled(Rule::NeedlessElse) { - ruff::rules::needless_else(checker, while_stmt.into()); + ruff::rules::needless_else(checker, (&**while_stmt).into()); } } - Stmt::For( - for_stmt @ ast::StmtFor { - target, - body, - iter, - orelse, - is_async, - range: _, - node_index: _, - }, - ) => { + Stmt::For(for_stmt) => { if checker.is_rule_enabled(Rule::TooManyNestedBlocks) { pylint::rules::too_many_nested_blocks(checker, stmt); } @@ -1235,25 +1203,25 @@ pub(crate) fn statement(stmt: &Stmt, checker: &mut Checker) { checker.analyze.for_loops.push(checker.semantic.snapshot()); } if checker.is_rule_enabled(Rule::LoopVariableOverridesIterator) { - flake8_bugbear::rules::loop_variable_overrides_iterator(checker, target, iter); + flake8_bugbear::rules::loop_variable_overrides_iterator(checker, &for_stmt.target, &for_stmt.iter); } if checker.is_rule_enabled(Rule::FunctionUsesLoopVariable) { flake8_bugbear::rules::function_uses_loop_variable(checker, &Node::Stmt(stmt)); } if checker.is_rule_enabled(Rule::ReuseOfGroupbyGenerator) { - flake8_bugbear::rules::reuse_of_groupby_generator(checker, target, body, iter); + flake8_bugbear::rules::reuse_of_groupby_generator(checker, &for_stmt.target, &for_stmt.body, &for_stmt.iter); } if checker.is_rule_enabled(Rule::UselessElseOnLoop) { - pylint::rules::useless_else_on_loop(checker, stmt, body, orelse); + pylint::rules::useless_else_on_loop(checker, stmt, &for_stmt.body, &for_stmt.orelse); } if checker.is_rule_enabled(Rule::RedefinedLoopName) { pylint::rules::redefined_loop_name(checker, stmt); } if checker.is_rule_enabled(Rule::IterationOverSet) { - pylint::rules::iteration_over_set(checker, iter); + pylint::rules::iteration_over_set(checker, &for_stmt.iter); } if checker.is_rule_enabled(Rule::DictIterMissingItems) { - pylint::rules::dict_iter_missing_items(checker, target, iter); + pylint::rules::dict_iter_missing_items(checker, &for_stmt.target, &for_stmt.iter); } if checker.is_rule_enabled(Rule::ManualListCopy) { perflint::rules::manual_list_copy(checker, for_stmt); @@ -1263,7 +1231,7 @@ pub(crate) fn statement(stmt: &Stmt, checker: &mut Checker) { pylint::rules::modified_iterating_set(checker, for_stmt); } if checker.is_rule_enabled(Rule::UnnecessaryListCast) { - perflint::rules::unnecessary_list_cast(checker, iter, body); + perflint::rules::unnecessary_list_cast(checker, &for_stmt.iter, &for_stmt.body); } if checker.is_rule_enabled(Rule::UnnecessaryListIndexLookup) { pylint::rules::unnecessary_list_index_lookup(checker, for_stmt); @@ -1274,7 +1242,7 @@ pub(crate) fn statement(stmt: &Stmt, checker: &mut Checker) { if checker.is_rule_enabled(Rule::ReadlinesInFor) { refurb::rules::readlines_in_for(checker, for_stmt); } - if !*is_async { + if !for_stmt.is_async { if checker.is_rule_enabled(Rule::ReimplementedBuiltin) { flake8_simplify::rules::convert_for_loop_to_any_all(checker, stmt); } @@ -1282,7 +1250,7 @@ pub(crate) fn statement(stmt: &Stmt, checker: &mut Checker) { flake8_simplify::rules::key_in_dict_for(checker, for_stmt); } if checker.is_rule_enabled(Rule::TryExceptInLoop) { - perflint::rules::try_except_in_loop(checker, body); + perflint::rules::try_except_in_loop(checker, &for_stmt.body); } if checker.is_rule_enabled(Rule::ForLoopSetMutations) { refurb::rules::for_loop_set_mutations(checker, for_stmt); @@ -1292,141 +1260,133 @@ pub(crate) fn statement(stmt: &Stmt, checker: &mut Checker) { } } if checker.is_rule_enabled(Rule::NeedlessElse) { - ruff::rules::needless_else(checker, for_stmt.into()); + ruff::rules::needless_else(checker, (&**for_stmt).into()); } } - Stmt::Try( - try_stmt @ ast::StmtTry { - body, - handlers, - orelse, - finalbody, - .. - }, - ) => { + Stmt::Try(try_stmt) => { if checker.is_rule_enabled(Rule::TooManyNestedBlocks) { pylint::rules::too_many_nested_blocks(checker, stmt); } if checker.is_rule_enabled(Rule::JumpStatementInFinally) { - flake8_bugbear::rules::jump_statement_in_finally(checker, finalbody); + flake8_bugbear::rules::jump_statement_in_finally(checker, &try_stmt.finalbody); } if checker.is_rule_enabled(Rule::ContinueInFinally) { if checker.target_version() <= PythonVersion::PY38 { - pylint::rules::continue_in_finally(checker, finalbody); + pylint::rules::continue_in_finally(checker, &try_stmt.finalbody); } } if checker.is_rule_enabled(Rule::DefaultExceptNotLast) { - pyflakes::rules::default_except_not_last(checker, handlers, checker.locator); + pyflakes::rules::default_except_not_last(checker, &try_stmt.handlers, checker.locator); } if checker.any_rule_enabled(&[ Rule::DuplicateHandlerException, Rule::DuplicateTryBlockException, ]) { - flake8_bugbear::rules::duplicate_exceptions(checker, handlers); + flake8_bugbear::rules::duplicate_exceptions(checker, &try_stmt.handlers); } if checker.is_rule_enabled(Rule::RedundantTupleInExceptionHandler) { - flake8_bugbear::rules::redundant_tuple_in_exception_handler(checker, handlers); + flake8_bugbear::rules::redundant_tuple_in_exception_handler(checker, &try_stmt.handlers); } if checker.is_rule_enabled(Rule::OSErrorAlias) { - pyupgrade::rules::os_error_alias_handlers(checker, handlers); + pyupgrade::rules::os_error_alias_handlers(checker, &try_stmt.handlers); } if checker.is_rule_enabled(Rule::TimeoutErrorAlias) { if checker.target_version() >= PythonVersion::PY310 { - pyupgrade::rules::timeout_error_alias_handlers(checker, handlers); + pyupgrade::rules::timeout_error_alias_handlers(checker, &try_stmt.handlers); } } if checker.is_rule_enabled(Rule::PytestAssertInExcept) { - flake8_pytest_style::rules::assert_in_exception_handler(checker, handlers); + flake8_pytest_style::rules::assert_in_exception_handler(checker, &try_stmt.handlers); } if checker.is_rule_enabled(Rule::SuppressibleException) { flake8_simplify::rules::suppressible_exception( - checker, stmt, body, handlers, orelse, finalbody, + checker, stmt, &try_stmt.body, &try_stmt.handlers, &try_stmt.orelse, &try_stmt.finalbody, ); } if checker.is_rule_enabled(Rule::ReturnInTryExceptFinally) { flake8_simplify::rules::return_in_try_except_finally( - checker, body, handlers, finalbody, + checker, &try_stmt.body, &try_stmt.handlers, &try_stmt.finalbody, ); } if checker.is_rule_enabled(Rule::TryConsiderElse) { - tryceratops::rules::try_consider_else(checker, body, orelse, handlers); + tryceratops::rules::try_consider_else(checker, &try_stmt.body, &try_stmt.orelse, &try_stmt.handlers); } if checker.is_rule_enabled(Rule::VerboseRaise) { - tryceratops::rules::verbose_raise(checker, handlers); + tryceratops::rules::verbose_raise(checker, &try_stmt.handlers); } if checker.is_rule_enabled(Rule::VerboseLogMessage) { - tryceratops::rules::verbose_log_message(checker, handlers); + tryceratops::rules::verbose_log_message(checker, &try_stmt.handlers); } if checker.is_rule_enabled(Rule::RaiseWithinTry) { - tryceratops::rules::raise_within_try(checker, body, handlers); + tryceratops::rules::raise_within_try(checker, &try_stmt.body, &try_stmt.handlers); } if checker.is_rule_enabled(Rule::UselessTryExcept) { - tryceratops::rules::useless_try_except(checker, handlers); + tryceratops::rules::useless_try_except(checker, &try_stmt.handlers); } if checker.is_rule_enabled(Rule::ErrorInsteadOfException) { - tryceratops::rules::error_instead_of_exception(checker, handlers); + tryceratops::rules::error_instead_of_exception(checker, &try_stmt.handlers); } if checker.is_rule_enabled(Rule::NeedlessElse) { - ruff::rules::needless_else(checker, try_stmt.into()); + ruff::rules::needless_else(checker, (&**try_stmt).into()); } } - Stmt::Assign(assign @ ast::StmtAssign { targets, value, .. }) => { + Stmt::Assign(assign) => { if checker.is_rule_enabled(Rule::SelfOrClsAssignment) { - for target in targets { + for target in &assign.targets { pylint::rules::self_or_cls_assignment(checker, target); } } if checker.is_rule_enabled(Rule::RedeclaredAssignedName) { - pylint::rules::redeclared_assigned_name(checker, targets); + pylint::rules::redeclared_assigned_name(checker, &assign.targets); } if checker.is_rule_enabled(Rule::LambdaAssignment) { - if let [target] = &targets[..] { - pycodestyle::rules::lambda_assignment(checker, target, value, None, stmt); + if let [target] = &assign.targets[..] { + pycodestyle::rules::lambda_assignment(checker, target, &assign.value, None, stmt); } } if checker.is_rule_enabled(Rule::AssignmentToOsEnviron) { - flake8_bugbear::rules::assignment_to_os_environ(checker, targets); + flake8_bugbear::rules::assignment_to_os_environ(checker, &assign.targets); } if checker.is_rule_enabled(Rule::HardcodedPasswordString) { - flake8_bandit::rules::assign_hardcoded_password_string(checker, value, targets); + flake8_bandit::rules::assign_hardcoded_password_string(checker, &assign.value, &assign.targets); } if checker.is_rule_enabled(Rule::GlobalStatement) { - for target in targets { - if let Expr::Name(ast::ExprName { id, .. }) = target { - pylint::rules::global_statement(checker, id); + for target in &assign.targets { + if let Expr::Name(name_expr) = target { + pylint::rules::global_statement(checker, &name_expr.id); } } } if checker.is_rule_enabled(Rule::UselessMetaclassType) { - pyupgrade::rules::useless_metaclass_type(checker, stmt, value, targets); + pyupgrade::rules::useless_metaclass_type(checker, stmt, &assign.value, &assign.targets); } if checker.is_rule_enabled(Rule::ConvertTypedDictFunctionalToClass) { pyupgrade::rules::convert_typed_dict_functional_to_class( - checker, stmt, targets, value, + checker, stmt, &assign.targets, &assign.value, ); } if checker.is_rule_enabled(Rule::ConvertNamedTupleFunctionalToClass) { pyupgrade::rules::convert_named_tuple_functional_to_class( - checker, stmt, targets, value, + checker, stmt, &assign.targets, &assign.value, ); } if checker.is_rule_enabled(Rule::PandasDfVariableName) { - pandas_vet::rules::assignment_to_df(checker, targets); + pandas_vet::rules::assignment_to_df(checker, &assign.targets); } if checker.is_rule_enabled(Rule::AirflowVariableNameTaskIdMismatch) { - airflow::rules::variable_name_task_id(checker, targets, value); + airflow::rules::variable_name_task_id(checker, &assign.targets, &assign.value); } if checker.is_rule_enabled(Rule::SelfAssigningVariable) { pylint::rules::self_assignment(checker, assign); } if checker.is_rule_enabled(Rule::TypeParamNameMismatch) { - pylint::rules::type_param_name_mismatch(checker, value, targets); + pylint::rules::type_param_name_mismatch(checker, &assign.value, &assign.targets); } if checker.is_rule_enabled(Rule::TypeNameIncorrectVariance) { - pylint::rules::type_name_incorrect_variance(checker, value); + pylint::rules::type_name_incorrect_variance(checker, &assign.value); } if checker.is_rule_enabled(Rule::TypeBivariance) { - pylint::rules::type_bivariance(checker, value); + pylint::rules::type_bivariance(checker, &assign.value); } if checker.is_rule_enabled(Rule::NonAugmentedAssignment) { pylint::rules::non_augmented_assignment(checker, assign); @@ -1449,14 +1409,14 @@ pub(crate) fn statement(stmt: &Stmt, checker: &mut Checker) { .any(|scope| scope.kind.is_function()) { if checker.is_rule_enabled(Rule::UnprefixedTypeParam) { - flake8_pyi::rules::prefix_type_params(checker, value, targets); + flake8_pyi::rules::prefix_type_params(checker, &assign.value, &assign.targets); } if checker.is_rule_enabled(Rule::AssignmentDefaultInStub) { - flake8_pyi::rules::assignment_default_in_stub(checker, targets, value); + flake8_pyi::rules::assignment_default_in_stub(checker, &assign.targets, &assign.value); } if checker.is_rule_enabled(Rule::UnannotatedAssignmentInStub) { flake8_pyi::rules::unannotated_assignment_in_stub( - checker, targets, value, + checker, &assign.targets, &assign.value, ); } if checker.is_rule_enabled(Rule::ComplexAssignmentInStub) { @@ -1464,7 +1424,7 @@ pub(crate) fn statement(stmt: &Stmt, checker: &mut Checker) { } if checker.is_rule_enabled(Rule::TypeAliasWithoutAnnotation) { flake8_pyi::rules::type_alias_without_annotation( - checker, value, targets, + checker, &assign.value, &assign.targets, ); } } @@ -1477,15 +1437,10 @@ pub(crate) fn statement(stmt: &Stmt, checker: &mut Checker) { pyupgrade::rules::non_pep695_type_alias_type(checker, assign); } } - Stmt::AnnAssign( - assign_stmt @ ast::StmtAnnAssign { - target, - value, - annotation, - .. - }, - ) => { - if let Some(value) = value { + Stmt::AnnAssign(assign_stmt) => { + let target = &assign_stmt.target; + let annotation = &assign_stmt.annotation; + if let Some(value) = &assign_stmt.value { if checker.is_rule_enabled(Rule::LambdaAssignment) { pycodestyle::rules::lambda_assignment( checker, @@ -1506,7 +1461,7 @@ pub(crate) fn statement(stmt: &Stmt, checker: &mut Checker) { flake8_bugbear::rules::unintentional_type_annotation( checker, target, - value.as_deref(), + assign_stmt.value.as_deref(), stmt, ); } @@ -1514,7 +1469,7 @@ pub(crate) fn statement(stmt: &Stmt, checker: &mut Checker) { pyupgrade::rules::non_pep695_type_alias(checker, assign_stmt); } if checker.is_rule_enabled(Rule::HardcodedPasswordString) { - if let Some(value) = value.as_deref() { + if let Some(value) = assign_stmt.value.as_deref() { flake8_bandit::rules::assign_hardcoded_password_string( checker, value, @@ -1526,7 +1481,7 @@ pub(crate) fn statement(stmt: &Stmt, checker: &mut Checker) { ruff::rules::sort_dunder_all_ann_assign(checker, assign_stmt); } if checker.source_type.is_stub() { - if let Some(value) = value { + if let Some(value) = &assign_stmt.value { if checker.is_rule_enabled(Rule::AssignmentDefaultInStub) { // Ignore assignments in function bodies; those are covered by other rules. if !checker @@ -1563,7 +1518,8 @@ pub(crate) fn statement(stmt: &Stmt, checker: &mut Checker) { } } } - Stmt::TypeAlias(ast::StmtTypeAlias { name, .. }) => { + Stmt::TypeAlias(type_alias) => { + let name = &type_alias.name; if checker.is_rule_enabled(Rule::SnakeCaseTypeAlias) { flake8_pyi::rules::snake_case_type_alias(checker, name); } @@ -1571,17 +1527,12 @@ pub(crate) fn statement(stmt: &Stmt, checker: &mut Checker) { flake8_pyi::rules::t_suffixed_type_alias(checker, name); } } - Stmt::Delete( - delete @ ast::StmtDelete { - targets, - range: _, - node_index: _, - }, - ) => { + Stmt::Delete(delete) => { + let targets = &delete.targets; if checker.is_rule_enabled(Rule::GlobalStatement) { for target in targets { - if let Expr::Name(ast::ExprName { id, .. }) = target { - pylint::rules::global_statement(checker, id); + if let Expr::Name(name_expr) = target { + pylint::rules::global_statement(checker, &name_expr.id); } } } @@ -1618,12 +1569,13 @@ pub(crate) fn statement(stmt: &Stmt, checker: &mut Checker) { pylint::rules::useless_exception_statement(checker, expr); } } - Stmt::Match(ast::StmtMatch { - subject: _, - cases, - range: _, - node_index: _, - }) => { + Stmt::Match(match_stmt) => { + let ast::StmtMatch { + subject: _, + cases, + range: _, + node_index: _, + } = &**match_stmt; if checker.is_rule_enabled(Rule::NanComparison) { pylint::rules::nan_comparison_match(checker, cases); } diff --git a/crates/ruff_linter/src/checkers/ast/mod.rs b/crates/ruff_linter/src/checkers/ast/mod.rs index 37422cbf18..736f192ab3 100644 --- a/crates/ruff_linter/src/checkers/ast/mod.rs +++ b/crates/ruff_linter/src/checkers/ast/mod.rs @@ -782,7 +782,10 @@ impl SemanticSyntaxContext for Checker<'_> { for scope in self.semantic.current_scopes() { match scope.kind { ScopeKind::Class(_) | ScopeKind::Lambda(_) => return false, - ScopeKind::Function(ast::StmtFunctionDef { is_async, .. }) => return *is_async, + ScopeKind::Function(function_def) => { + let is_async = &function_def.is_async; + return *is_async; + } ScopeKind::Generator { .. } | ScopeKind::Module | ScopeKind::Type @@ -870,9 +873,13 @@ impl SemanticSyntaxContext for Checker<'_> { for parent in self.semantic.current_statements().skip(1) { match parent { - Stmt::For(ast::StmtFor { orelse, .. }) - | Stmt::While(ast::StmtWhile { orelse, .. }) => { - if !orelse.contains(child) { + Stmt::For(node) => { + if !node.orelse.contains(child) { + return true; + } + } + Stmt::While(node) => { + if !node.orelse.contains(child) { return true; } } @@ -888,7 +895,8 @@ impl SemanticSyntaxContext for Checker<'_> { fn is_bound_parameter(&self, name: &str) -> bool { match self.semantic.current_scope().kind { - ScopeKind::Function(ast::StmtFunctionDef { parameters, .. }) => { + ScopeKind::Function(function_def) => { + let parameters = &function_def.parameters; parameters.includes(name) } ScopeKind::Class(_) @@ -932,12 +940,13 @@ impl<'a> Visitor<'a> for Checker<'a> { { self.semantic.flags |= SemanticModelFlags::MODULE_DOCSTRING_BOUNDARY; } - Stmt::ImportFrom(ast::StmtImportFrom { module, names, .. }) => { + Stmt::ImportFrom(node) => { self.semantic.flags |= SemanticModelFlags::MODULE_DOCSTRING_BOUNDARY; // Allow __future__ imports until we see a non-__future__ import. - if let Some("__future__") = module.as_deref() { - if names + if let Some("__future__") = node.module.as_deref() { + if node + .names .iter() .any(|alias| alias.name.as_str() == "annotations") { @@ -981,20 +990,22 @@ impl<'a> Visitor<'a> for Checker<'a> { // Step 1: Binding match stmt { - Stmt::AugAssign(ast::StmtAugAssign { - target, - op: _, - value: _, - range: _, - node_index: _, - }) => { + Stmt::AugAssign(node) => { + let ast::StmtAugAssign { + target, + op: _, + value: _, + range: _, + node_index: _, + } = &**node; self.handle_node_load(target); } - Stmt::Import(ast::StmtImport { - names, - range: _, - node_index: _, - }) => { + Stmt::Import(node) => { + let ast::StmtImport { + names, + range: _, + node_index: _, + } = &**node; if self.semantic.at_top_level() { self.importer.visit_import(stmt); } @@ -1043,13 +1054,14 @@ impl<'a> Visitor<'a> for Checker<'a> { } } } - Stmt::ImportFrom(ast::StmtImportFrom { - names, - module, - level, - range: _, - node_index: _, - }) => { + Stmt::ImportFrom(node) => { + let ast::StmtImportFrom { + names, + module, + level, + range: _, + node_index: _, + } = &**node; if self.semantic.at_top_level() { self.importer.visit_import(stmt); } @@ -1110,11 +1122,12 @@ impl<'a> Visitor<'a> for Checker<'a> { } } } - Stmt::Global(ast::StmtGlobal { - names, - range: _, - node_index: _, - }) => { + Stmt::Global(node) => { + let ast::StmtGlobal { + names, + range: _, + node_index: _, + } = &**node; if !self.semantic.scope_id.is_global() { for name in names { let binding_id = self.semantic.global_scope().get(name); @@ -1136,11 +1149,12 @@ impl<'a> Visitor<'a> for Checker<'a> { } } } - Stmt::Nonlocal(ast::StmtNonlocal { - names, - range: _, - node_index: _, - }) => { + Stmt::Nonlocal(node) => { + let ast::StmtNonlocal { + names, + range: _, + node_index: _, + } = &**node; if !self.semantic.scope_id.is_global() { for name in names { if let Some((scope_id, binding_id)) = self.semantic.nonlocal(name) { @@ -1174,17 +1188,13 @@ impl<'a> Visitor<'a> for Checker<'a> { // Step 2: Traversal match stmt { - Stmt::FunctionDef( - function_def @ ast::StmtFunctionDef { - name, - body, - parameters, - decorator_list, - returns, - type_params, - .. - }, - ) => { + Stmt::FunctionDef(function_def) => { + let name = &function_def.name; + let body = &function_def.body; + let parameters = &function_def.parameters; + let decorator_list = &function_def.decorator_list; + let returns = &function_def.returns; + let type_params = &function_def.type_params; // Visit the decorators and arguments, but avoid the body, which will be // deferred. for decorator in decorator_list { @@ -1313,16 +1323,12 @@ impl<'a> Visitor<'a> for Checker<'a> { BindingFlags::empty(), ); } - Stmt::ClassDef( - class_def @ ast::StmtClassDef { - name, - body, - arguments, - decorator_list, - type_params, - .. - }, - ) => { + Stmt::ClassDef(class_def) => { + let name = &class_def.name; + let body = &class_def.body; + let arguments = &class_def.arguments; + let decorator_list = &class_def.decorator_list; + let type_params = &class_def.type_params; for decorator in decorator_list { self.visit_decorator(decorator); } @@ -1369,30 +1375,20 @@ impl<'a> Visitor<'a> for Checker<'a> { BindingFlags::empty(), ); } - Stmt::TypeAlias(ast::StmtTypeAlias { - range: _, - node_index: _, - name, - type_params, - value, - }) => { + Stmt::TypeAlias(node) => { self.semantic.push_scope(ScopeKind::Type); - if let Some(type_params) = type_params { + if let Some(type_params) = &node.type_params { self.visit_type_params(type_params); } - self.visit_deferred_type_alias_value(value); + self.visit_deferred_type_alias_value(&node.value); self.semantic.pop_scope(); - self.visit_expr(name); + self.visit_expr(&node.name); } - Stmt::Try( - try_node @ ast::StmtTry { - body, - handlers, - orelse, - finalbody, - .. - }, - ) => { + Stmt::Try(try_node) => { + let body = &try_node.body; + let handlers = &try_node.handlers; + let orelse = &try_node.orelse; + let finalbody = &try_node.finalbody; // Iterate over the `body`, then the `handlers`, then the `orelse`, then the // `finalbody`, but treat the body and the `orelse` as a single branch for // flow analysis purposes. @@ -1418,64 +1414,60 @@ impl<'a> Visitor<'a> for Checker<'a> { self.visit_body(finalbody); self.semantic.pop_branch(); } - Stmt::AnnAssign(ast::StmtAnnAssign { - target, - annotation, - value, - .. - }) => { + Stmt::AnnAssign(node) => { match AnnotationContext::from_model( &self.semantic, self.settings(), self.target_version(), ) { AnnotationContext::RuntimeRequired => { - self.visit_runtime_required_annotation(annotation); + self.visit_runtime_required_annotation(&node.annotation); } AnnotationContext::RuntimeEvaluated if flake8_type_checking::helpers::is_dataclass_meta_annotation( - annotation, + &node.annotation, self.semantic(), ) => { - self.visit_runtime_required_annotation(annotation); + self.visit_runtime_required_annotation(&node.annotation); } AnnotationContext::RuntimeEvaluated => { - self.visit_runtime_evaluated_annotation(annotation); + self.visit_runtime_evaluated_annotation(&node.annotation); } AnnotationContext::TypingOnly if flake8_type_checking::helpers::is_dataclass_meta_annotation( - annotation, + &node.annotation, self.semantic(), ) => { - if let Expr::Subscript(subscript) = &**annotation { + if let Expr::Subscript(subscript) = &*node.annotation { // Ex) `InitVar[str]` self.visit_runtime_required_annotation(&subscript.value); self.visit_annotation(&subscript.slice); } else { // Ex) `InitVar` - self.visit_runtime_required_annotation(annotation); + self.visit_runtime_required_annotation(&node.annotation); } } - AnnotationContext::TypingOnly => self.visit_annotation(annotation), + AnnotationContext::TypingOnly => self.visit_annotation(&node.annotation), } - if let Some(expr) = value { - if self.semantic.match_typing_expr(annotation, "TypeAlias") { + if let Some(expr) = &node.value { + if self.semantic.match_typing_expr(&node.annotation, "TypeAlias") { self.visit_annotated_type_alias_value(expr); } else { self.visit_expr(expr); } } - self.visit_expr(target); + self.visit_expr(&node.target); } - Stmt::Assert(ast::StmtAssert { - test, - msg, - range: _, - node_index: _, - }) => { + Stmt::Assert(node) => { + let ast::StmtAssert { + test, + msg, + range: _, + node_index: _, + } = &**node; let snapshot = self.semantic.flags; self.semantic.flags |= SemanticModelFlags::ASSERT_STATEMENT; self.visit_boolean_test(test); @@ -1484,13 +1476,14 @@ impl<'a> Visitor<'a> for Checker<'a> { } self.semantic.flags = snapshot; } - Stmt::With(ast::StmtWith { - items, - body, - is_async: _, - range: _, - node_index: _, - }) => { + Stmt::With(node) => { + let ast::StmtWith { + items, + body, + is_async: _, + range: _, + node_index: _, + } = &**node; for item in items { self.visit_with_item(item); } @@ -1498,26 +1491,22 @@ impl<'a> Visitor<'a> for Checker<'a> { self.visit_body(body); self.semantic.pop_branch(); } - Stmt::While(ast::StmtWhile { - test, - body, - orelse, - range: _, - node_index: _, - }) => { + Stmt::While(node) => { + let ast::StmtWhile { + test, + body, + orelse, + range: _, + node_index: _, + } = &**node; self.visit_boolean_test(test); self.visit_body(body); self.visit_body(orelse); } - Stmt::If( - stmt_if @ ast::StmtIf { - test, - body, - elif_else_clauses, - range: _, - node_index: _, - }, - ) => { + Stmt::If(stmt_if) => { + let test = &stmt_if.test; + let body = &stmt_if.body; + let elif_else_clauses = &stmt_if.elif_else_clauses; self.visit_boolean_test(test); self.semantic.push_branch(); @@ -1542,14 +1531,14 @@ impl<'a> Visitor<'a> for Checker<'a> { if self.semantic().at_top_level() || self.semantic().current_scope().kind.is_class() { match stmt { - Stmt::Assign(ast::StmtAssign { targets, .. }) => { - if let [Expr::Name(_)] = targets.as_slice() { + Stmt::Assign(node) => { + if let [Expr::Name(_)] = node.targets.as_slice() { self.docstring_state = DocstringState::Expected(ExpectedDocstringKind::Attribute); } } - Stmt::AnnAssign(ast::StmtAnnAssign { target, .. }) => { - if target.is_name_expr() { + Stmt::AnnAssign(node) => { + if node.target.is_name_expr() { self.docstring_state = DocstringState::Expected(ExpectedDocstringKind::Attribute); } @@ -2690,13 +2679,13 @@ impl<'a> Checker<'a> { match parent { Stmt::TypeAlias(_) => flags.insert(BindingFlags::DEFERRED_TYPE_ALIAS), - Stmt::AnnAssign(ast::StmtAnnAssign { annotation, .. }) => { + Stmt::AnnAssign(node) => { // TODO: It is a bit unfortunate that we do this check twice // maybe we should change how we visit this statement // so the semantic flag for the type alias sticks around // until after we've handled this store, so we can check // the flag instead of duplicating this check - if self.semantic.match_typing_expr(annotation, "TypeAlias") { + if self.semantic.match_typing_expr(&node.annotation, "TypeAlias") { flags.insert(BindingFlags::ANNOTATED_TYPE_ALIAS); } } @@ -2707,22 +2696,22 @@ impl<'a> Checker<'a> { if scope.kind.is_module() && match parent { - Stmt::Assign(ast::StmtAssign { targets, .. }) => { - if let Some(Expr::Name(ast::ExprName { id, .. })) = targets.first() { + Stmt::Assign(node) => { + if let Some(Expr::Name(ast::ExprName { id, .. })) = node.targets.first() { id == "__all__" } else { false } } - Stmt::AugAssign(ast::StmtAugAssign { target, .. }) => { - if let Expr::Name(ast::ExprName { id, .. }) = target.as_ref() { + Stmt::AugAssign(node) => { + if let Expr::Name(ast::ExprName { id, .. }) = node.target.as_ref() { id == "__all__" } else { false } } - Stmt::AnnAssign(ast::StmtAnnAssign { target, .. }) => { - if let Expr::Name(ast::ExprName { id, .. }) = target.as_ref() { + Stmt::AnnAssign(node) => { + if let Expr::Name(ast::ExprName { id, .. }) = node.target.as_ref() { id == "__all__" } else { false @@ -2765,10 +2754,8 @@ impl<'a> Checker<'a> { // Match the left-hand side of an annotated assignment without a value, // like `x` in `x: int`. N.B. In stub files, these should be viewed // as assignments on par with statements such as `x: int = 5`. - if matches!( - parent, - Stmt::AnnAssign(ast::StmtAnnAssign { value: None, .. }) - ) && !self.semantic.in_annotation() + if matches!(parent, Stmt::AnnAssign(node) if node.value.is_none()) + && !self.semantic.in_annotation() { self.add_binding(id, expr.range(), BindingKind::Annotation, flags); return; @@ -3040,19 +3027,16 @@ impl<'a> Checker<'a> { let stmt = self.semantic.current_statement(); - let Stmt::FunctionDef(ast::StmtFunctionDef { - body, parameters, .. - }) = stmt - else { + let Stmt::FunctionDef(node) = stmt else { unreachable!("Expected Stmt::FunctionDef") }; self.with_semantic_checker(|semantic, context| semantic.visit_stmt(stmt, context)); - self.visit_parameters(parameters); + self.visit_parameters(&node.parameters); // Set the docstring state before visiting the function body. self.docstring_state = DocstringState::Expected(ExpectedDocstringKind::Function); - self.visit_body(body); + self.visit_body(&node.body); } } self.semantic.restore(snapshot); diff --git a/crates/ruff_linter/src/fix/edits.rs b/crates/ruff_linter/src/fix/edits.rs index 20f50d6e10..255da7ebc8 100644 --- a/crates/ruff_linter/src/fix/edits.rs +++ b/crates/ruff_linter/src/fix/edits.rs @@ -127,8 +127,8 @@ pub(crate) fn make_redundant_alias<'a>( stmt: &Stmt, ) -> Vec { let aliases = match stmt { - Stmt::Import(ast::StmtImport { names, .. }) => names, - Stmt::ImportFrom(ast::StmtImportFrom { names, .. }) => names, + Stmt::Import(node) => &node.names, + Stmt::ImportFrom(node) => &node.names, _ => { return Vec::new(); } @@ -404,43 +404,46 @@ fn is_only(vec: &[T], value: &T) -> bool { /// Determine if a child is the only statement in its body. fn is_lone_child(child: &Stmt, parent: &Stmt) -> bool { match parent { - Stmt::FunctionDef(ast::StmtFunctionDef { body, .. }) - | Stmt::ClassDef(ast::StmtClassDef { body, .. }) - | Stmt::With(ast::StmtWith { body, .. }) => { - if is_only(body, child) { + Stmt::FunctionDef(node) => { + if is_only(&node.body, child) { return true; } } - Stmt::For(ast::StmtFor { body, orelse, .. }) - | Stmt::While(ast::StmtWhile { body, orelse, .. }) => { - if is_only(body, child) || is_only(orelse, child) { + Stmt::ClassDef(node) => { + if is_only(&node.body, child) { return true; } } - Stmt::If(ast::StmtIf { - body, - elif_else_clauses, - .. - }) => { - if is_only(body, child) - || elif_else_clauses + Stmt::With(node) => { + if is_only(&node.body, child) { + return true; + } + } + Stmt::For(node) => { + if is_only(&node.body, child) || is_only(&node.orelse, child) { + return true; + } + } + Stmt::While(node) => { + if is_only(&node.body, child) || is_only(&node.orelse, child) { + return true; + } + } + Stmt::If(node) => { + if is_only(&node.body, child) + || node + .elif_else_clauses .iter() .any(|ast::ElifElseClause { body, .. }| is_only(body, child)) { return true; } } - Stmt::Try(ast::StmtTry { - body, - handlers, - orelse, - finalbody, - .. - }) => { - if is_only(body, child) - || is_only(orelse, child) - || is_only(finalbody, child) - || handlers.iter().any(|handler| match handler { + Stmt::Try(node) => { + if is_only(&node.body, child) + || is_only(&node.orelse, child) + || is_only(&node.finalbody, child) + || node.handlers.iter().any(|handler| match handler { ExceptHandler::ExceptHandler(ast::ExceptHandlerExceptHandler { body, .. }) => is_only(body, child), @@ -449,8 +452,8 @@ fn is_lone_child(child: &Stmt, parent: &Stmt) -> bool { return true; } } - Stmt::Match(ast::StmtMatch { cases, .. }) => { - if cases.iter().any(|case| is_only(&case.body, child)) { + Stmt::Match(node) => { + if node.cases.iter().any(|case| is_only(&case.body, child)) { return true; } } diff --git a/crates/ruff_linter/src/importer/mod.rs b/crates/ruff_linter/src/importer/mod.rs index 4ffa03d677..c6bcb937c2 100644 --- a/crates/ruff_linter/src/importer/mod.rs +++ b/crates/ruff_linter/src/importer/mod.rs @@ -236,9 +236,10 @@ impl<'a> Importer<'a> { semantic: &SemanticModel<'a>, type_checking_block: &Stmt, ) -> Option<&'a Stmt> { - let Stmt::If(ast::StmtIf { test, .. }) = type_checking_block else { + let Stmt::If(node) = type_checking_block else { return None; }; + let test = &node.test; let mut source = test; while let Expr::Attribute(ast::ExprAttribute { value, .. }) = source.as_ref() { @@ -453,17 +454,10 @@ impl<'a> Importer<'a> { if stmt.start() >= at { break; } - if let Stmt::ImportFrom(ast::StmtImportFrom { - module: name, - names, - level, - range: _, - node_index: _, - }) = stmt - { - if *level == 0 - && name.as_ref().is_some_and(|name| name == module) - && names.iter().all(|alias| alias.name.as_str() != "*") + if let Stmt::ImportFrom(node) = stmt { + if node.level == 0 + && node.module.as_ref().is_some_and(|name| name == module) + && node.names.iter().all(|alias| alias.name.as_str() != "*") { import_from = Some(*stmt); } diff --git a/crates/ruff_linter/src/renamer.rs b/crates/ruff_linter/src/renamer.rs index d4c587142d..9fee0ed87b 100644 --- a/crates/ruff_linter/src/renamer.rs +++ b/crates/ruff_linter/src/renamer.rs @@ -281,12 +281,10 @@ impl Renamer { ) -> Option { let statement = binding.statement(semantic)?; - let (ast::Stmt::Assign(ast::StmtAssign { value, .. }) - | ast::Stmt::AnnAssign(ast::StmtAnnAssign { - value: Some(value), .. - })) = statement - else { - return None; + let value = match statement { + ast::Stmt::Assign(node) => &node.value, + ast::Stmt::AnnAssign(node) => node.value.as_ref()?, + _ => return None, }; let ast::ExprCall { diff --git a/crates/ruff_linter/src/rules/airflow/rules/removal_in_3.rs b/crates/ruff_linter/src/rules/airflow/rules/removal_in_3.rs index 562f37230d..87026e4435 100644 --- a/crates/ruff_linter/src/rules/airflow/rules/removal_in_3.rs +++ b/crates/ruff_linter/src/rules/airflow/rules/removal_in_3.rs @@ -448,11 +448,10 @@ fn is_kwarg_parameter(semantic: &SemanticModel, name: &ExprName) -> bool { return false; }; let binding = semantic.binding(binding_id); - let Some(Stmt::FunctionDef(StmtFunctionDef { parameters, .. })) = binding.statement(semantic) - else { + let Some(Stmt::FunctionDef(node)) = binding.statement(semantic) else { return false; }; - parameters + node.parameters .kwarg .as_deref() .is_some_and(|kwarg| kwarg.name.as_str() == name.id.as_str()) diff --git a/crates/ruff_linter/src/rules/flake8_bandit/rules/suspicious_imports.rs b/crates/ruff_linter/src/rules/flake8_bandit/rules/suspicious_imports.rs index acc978d054..229f77d6c3 100644 --- a/crates/ruff_linter/src/rules/flake8_bandit/rules/suspicious_imports.rs +++ b/crates/ruff_linter/src/rules/flake8_bandit/rules/suspicious_imports.rs @@ -2,7 +2,7 @@ //! //! See: use ruff_macros::{ViolationMetadata, derive_message_formats}; -use ruff_python_ast::{self as ast, Stmt}; +use ruff_python_ast::Stmt; use ruff_text_size::Ranged; use crate::Violation; @@ -371,7 +371,8 @@ pub(crate) fn suspicious_imports(checker: &Checker, stmt: &Stmt) { } match stmt { - Stmt::Import(ast::StmtImport { names, .. }) => { + Stmt::Import(node) => { + let names = &node.names; for name in names { match name.name.as_str() { "telnetlib" => { @@ -421,8 +422,9 @@ pub(crate) fn suspicious_imports(checker: &Checker, stmt: &Stmt) { } } } - Stmt::ImportFrom(ast::StmtImportFrom { module, names, .. }) => { - let Some(identifier) = module else { return }; + Stmt::ImportFrom(node) => { + let Some(identifier) = &node.module else { return }; + let names = &node.names; match identifier.as_str() { "telnetlib" => { checker.report_diagnostic_if_enabled( diff --git a/crates/ruff_linter/src/rules/flake8_blind_except/rules/blind_except.rs b/crates/ruff_linter/src/rules/flake8_blind_except/rules/blind_except.rs index 704b628abf..9587c13a81 100644 --- a/crates/ruff_linter/src/rules/flake8_blind_except/rules/blind_except.rs +++ b/crates/ruff_linter/src/rules/flake8_blind_except/rules/blind_except.rs @@ -154,10 +154,12 @@ impl<'a> StatementVisitor<'a> for ReraiseVisitor<'a> { return; } match stmt { - Stmt::Raise(ast::StmtRaise { exc, cause, .. }) => { + Stmt::Raise(node) => { + let exc = node.exc.as_deref(); + let cause = node.cause.as_deref(); // except Exception [as ]: // raise [ [from ]] - let reraised = match (self.name, exc.as_deref(), cause.as_deref()) { + let reraised = match (self.name, exc, cause) { // `raise` (_, None, None) => true, // `raise SomeExc from ` diff --git a/crates/ruff_linter/src/rules/flake8_bugbear/rules/abstract_base_class.rs b/crates/ruff_linter/src/rules/flake8_bugbear/rules/abstract_base_class.rs index 2b57ee35e1..40634a5191 100644 --- a/crates/ruff_linter/src/rules/flake8_bugbear/rules/abstract_base_class.rs +++ b/crates/ruff_linter/src/rules/flake8_bugbear/rules/abstract_base_class.rs @@ -173,24 +173,21 @@ pub(crate) fn abstract_base_class( // If an ABC declares an attribute by providing a type annotation // but does not actually assign a value for that attribute, // assume it is intended to be an "abstract attribute" - if matches!( - stmt, - Stmt::AnnAssign(ast::StmtAnnAssign { value: None, .. }) - ) { - has_abstract_method = true; - continue; + if let Stmt::AnnAssign(node) = stmt { + if node.value.is_none() { + has_abstract_method = true; + continue; + } } - let Stmt::FunctionDef(ast::StmtFunctionDef { - decorator_list, - body, - name: method_name, - .. - }) = stmt - else { + let Stmt::FunctionDef(node) = stmt else { continue; }; + let decorator_list = &node.decorator_list; + let body = &node.body; + let method_name = &node.name; + let has_abstract_decorator = is_abstract(decorator_list, checker.semantic()); has_abstract_method |= has_abstract_decorator; diff --git a/crates/ruff_linter/src/rules/flake8_bugbear/rules/assert_false.rs b/crates/ruff_linter/src/rules/flake8_bugbear/rules/assert_false.rs index d114cc2432..0c46297569 100644 --- a/crates/ruff_linter/src/rules/flake8_bugbear/rules/assert_false.rs +++ b/crates/ruff_linter/src/rules/flake8_bugbear/rules/assert_false.rs @@ -51,7 +51,7 @@ impl AlwaysFixableViolation for AssertFalse { } fn assertion_error(msg: Option<&Expr>) -> Stmt { - Stmt::Raise(ast::StmtRaise { + Stmt::Raise(Box::new(ast::StmtRaise { range: TextRange::default(), node_index: ruff_python_ast::AtomicNodeIndex::NONE, exc: Some(Box::new(Expr::Call(ast::ExprCall { @@ -75,7 +75,7 @@ fn assertion_error(msg: Option<&Expr>) -> Stmt { node_index: ruff_python_ast::AtomicNodeIndex::NONE, }))), cause: None, - }) + })) } /// B011 diff --git a/crates/ruff_linter/src/rules/flake8_bugbear/rules/class_as_data_structure.rs b/crates/ruff_linter/src/rules/flake8_bugbear/rules/class_as_data_structure.rs index 5a0e2025b4..cd450f3c09 100644 --- a/crates/ruff_linter/src/rules/flake8_bugbear/rules/class_as_data_structure.rs +++ b/crates/ruff_linter/src/rules/flake8_bugbear/rules/class_as_data_structure.rs @@ -114,14 +114,14 @@ pub(crate) fn class_as_data_structure(checker: &Checker, class_def: &ast::StmtCl // assignment of a name to an attribute. fn is_simple_assignment_to_attribute(stmt: &ast::Stmt) -> bool { match stmt { - ast::Stmt::Assign(ast::StmtAssign { targets, value, .. }) => { - let [target] = targets.as_slice() else { + ast::Stmt::Assign(node) => { + let [target] = node.targets.as_slice() else { return false; }; - target.is_attribute_expr() && value.is_name_expr() + target.is_attribute_expr() && node.value.is_name_expr() } - ast::Stmt::AnnAssign(ast::StmtAnnAssign { target, value, .. }) => { - target.is_attribute_expr() && value.as_ref().is_some_and(|val| val.is_name_expr()) + ast::Stmt::AnnAssign(node) => { + node.target.is_attribute_expr() && node.value.as_ref().is_some_and(|val| val.is_name_expr()) } _ => false, } diff --git a/crates/ruff_linter/src/rules/flake8_bugbear/rules/function_uses_loop_variable.rs b/crates/ruff_linter/src/rules/flake8_bugbear/rules/function_uses_loop_variable.rs index f19a43e633..1931dab385 100644 --- a/crates/ruff_linter/src/rules/flake8_bugbear/rules/function_uses_loop_variable.rs +++ b/crates/ruff_linter/src/rules/flake8_bugbear/rules/function_uses_loop_variable.rs @@ -86,12 +86,10 @@ struct SuspiciousVariablesVisitor<'a> { impl<'a> Visitor<'a> for SuspiciousVariablesVisitor<'a> { fn visit_stmt(&mut self, stmt: &'a Stmt) { match stmt { - Stmt::FunctionDef(ast::StmtFunctionDef { - parameters, body, .. - }) => { + Stmt::FunctionDef(node) => { // Collect all loaded variable names. let mut visitor = LoadedNamesVisitor::default(); - visitor.visit_body(body); + visitor.visit_body(&node.body); // Treat any non-arguments as "suspicious". self.names @@ -100,7 +98,7 @@ impl<'a> Visitor<'a> for SuspiciousVariablesVisitor<'a> { return false; } - if parameters.includes(&loaded.id) { + if node.parameters.includes(&loaded.id) { return false; } @@ -242,18 +240,26 @@ impl<'a> Visitor<'a> for AssignedNamesVisitor<'a> { } match stmt { - Stmt::Assign(ast::StmtAssign { targets, .. }) => { + Stmt::Assign(node) => { let mut visitor = NamesFromAssignmentsVisitor::default(); - for expr in targets { + for expr in &node.targets { visitor.visit_expr(expr); } self.names.extend(visitor.names); } - Stmt::AugAssign(ast::StmtAugAssign { target, .. }) - | Stmt::AnnAssign(ast::StmtAnnAssign { target, .. }) - | Stmt::For(ast::StmtFor { target, .. }) => { + Stmt::AugAssign(node) => { let mut visitor = NamesFromAssignmentsVisitor::default(); - visitor.visit_expr(target); + visitor.visit_expr(&node.target); + self.names.extend(visitor.names); + } + Stmt::AnnAssign(node) => { + let mut visitor = NamesFromAssignmentsVisitor::default(); + visitor.visit_expr(&node.target); + self.names.extend(visitor.names); + } + Stmt::For(node) => { + let mut visitor = NamesFromAssignmentsVisitor::default(); + visitor.visit_expr(&node.target); self.names.extend(visitor.names); } _ => {} diff --git a/crates/ruff_linter/src/rules/flake8_bugbear/rules/jump_statement_in_finally.rs b/crates/ruff_linter/src/rules/flake8_bugbear/rules/jump_statement_in_finally.rs index 944efa0432..2addfc5477 100644 --- a/crates/ruff_linter/src/rules/flake8_bugbear/rules/jump_statement_in_finally.rs +++ b/crates/ruff_linter/src/rules/flake8_bugbear/rules/jump_statement_in_finally.rs @@ -1,4 +1,4 @@ -use ruff_python_ast::{self as ast, Stmt}; +use ruff_python_ast::Stmt; use ruff_macros::{ViolationMetadata, derive_message_formats}; use ruff_text_size::Ranged; @@ -71,15 +71,23 @@ fn walk_stmt(checker: &Checker, body: &[Stmt], f: fn(&Stmt) -> bool) { ); } match stmt { - Stmt::While(ast::StmtWhile { body, .. }) | Stmt::For(ast::StmtFor { body, .. }) => { - walk_stmt(checker, body, Stmt::is_return_stmt); + Stmt::While(node) => { + walk_stmt(checker, &node.body, Stmt::is_return_stmt); } - Stmt::If(ast::StmtIf { body, .. }) - | Stmt::Try(ast::StmtTry { body, .. }) - | Stmt::With(ast::StmtWith { body, .. }) => { - walk_stmt(checker, body, f); + Stmt::For(node) => { + walk_stmt(checker, &node.body, Stmt::is_return_stmt); } - Stmt::Match(ast::StmtMatch { cases, .. }) => { + Stmt::If(node) => { + walk_stmt(checker, &node.body, f); + } + Stmt::Try(node) => { + walk_stmt(checker, &node.body, f); + } + Stmt::With(node) => { + walk_stmt(checker, &node.body, f); + } + Stmt::Match(node) => { + let cases = &node.cases; for case in cases { walk_stmt(checker, &case.body, f); } diff --git a/crates/ruff_linter/src/rules/flake8_bugbear/rules/loop_iterator_mutation.rs b/crates/ruff_linter/src/rules/flake8_bugbear/rules/loop_iterator_mutation.rs index 14456024bb..fe778ab18b 100644 --- a/crates/ruff_linter/src/rules/flake8_bugbear/rules/loop_iterator_mutation.rs +++ b/crates/ruff_linter/src/rules/flake8_bugbear/rules/loop_iterator_mutation.rs @@ -5,8 +5,7 @@ use ruff_macros::{ViolationMetadata, derive_message_formats}; use ruff_python_ast::comparable::ComparableExpr; use ruff_python_ast::name::UnqualifiedName; use ruff_python_ast::{ - Expr, ExprAttribute, ExprCall, ExprSubscript, ExprTuple, Stmt, StmtAssign, StmtAugAssign, - StmtDelete, StmtFor, StmtIf, + self as ast, Expr, ExprAttribute, ExprCall, ExprSubscript, ExprTuple, Stmt, StmtFor, visitor::{self, Visitor}, }; use ruff_text_size::TextRange; @@ -242,43 +241,39 @@ impl<'a> Visitor<'a> for LoopMutationsVisitor<'a> { fn visit_stmt(&mut self, stmt: &'a Stmt) { match stmt { // Ex) `del items[0]` - Stmt::Delete(StmtDelete { - range, - targets, - node_index: _, - }) => { + Stmt::Delete(node) => { + let ast::StmtDelete { + range, + targets, + node_index: _, + } = &**node; self.handle_delete(*range, targets); visitor::walk_stmt(self, stmt); } // Ex) `items[0] = 1` - Stmt::Assign(StmtAssign { range, targets, .. }) => { - self.handle_assign(*range, targets); + Stmt::Assign(node) => { + self.handle_assign(node.range, &node.targets); visitor::walk_stmt(self, stmt); } // Ex) `items += [1]` - Stmt::AugAssign(StmtAugAssign { range, target, .. }) => { - self.handle_aug_assign(*range, target); + Stmt::AugAssign(node) => { + self.handle_aug_assign(node.range, &node.target); visitor::walk_stmt(self, stmt); } // Ex) `if True: items.append(1)` - Stmt::If(StmtIf { - test, - body, - elif_else_clauses, - .. - }) => { + Stmt::If(node) => { // Handle the `if` branch. self.branch += 1; self.branches.push(self.branch); - self.visit_expr(test); - self.visit_body(body); + self.visit_expr(&node.test); + self.visit_body(&node.body); self.branches.pop(); // Handle the `elif` and `else` branches. - for clause in elif_else_clauses { + for clause in &node.elif_else_clauses { self.branch += 1; self.branches.push(self.branch); if let Some(test) = &clause.test { diff --git a/crates/ruff_linter/src/rules/flake8_bugbear/rules/reuse_of_groupby_generator.rs b/crates/ruff_linter/src/rules/flake8_bugbear/rules/reuse_of_groupby_generator.rs index 6e88ccca40..c5098eb3f9 100644 --- a/crates/ruff_linter/src/rules/flake8_bugbear/rules/reuse_of_groupby_generator.rs +++ b/crates/ruff_linter/src/rules/flake8_bugbear/rules/reuse_of_groupby_generator.rs @@ -119,13 +119,11 @@ impl<'a> Visitor<'a> for GroupNameFinder<'a> { return; } match stmt { - Stmt::For(ast::StmtFor { - target, iter, body, .. - }) => { - if self.name_matches(target) { + Stmt::For(node) => { + if self.name_matches(&node.target) { self.overridden = true; } else { - if self.name_matches(iter) { + if self.name_matches(&node.iter) { self.increment_usage_count(1); // This could happen when the group is being looped // over multiple times: @@ -136,36 +134,30 @@ impl<'a> Visitor<'a> for GroupNameFinder<'a> { // for item in group: // ... if self.usage_count > 1 { - self.exprs.push(iter); + self.exprs.push(&node.iter); } } self.nested = true; - visitor::walk_body(self, body); + visitor::walk_body(self, &node.body); self.nested = false; } } - Stmt::While(ast::StmtWhile { body, .. }) => { + Stmt::While(node) => { self.nested = true; - visitor::walk_body(self, body); + visitor::walk_body(self, &node.body); self.nested = false; } - Stmt::If(ast::StmtIf { - test, - body, - elif_else_clauses, - range: _, - node_index: _, - }) => { + Stmt::If(node) => { // base if plus branches - let mut if_stack = Vec::with_capacity(1 + elif_else_clauses.len()); + let mut if_stack = Vec::with_capacity(1 + node.elif_else_clauses.len()); // Initialize the vector with the count for the if branch. if_stack.push(0); self.counter_stack.push(if_stack); - self.visit_expr(test); - self.visit_body(body); + self.visit_expr(&node.test); + self.visit_body(&node.body); - for clause in elif_else_clauses { + for clause in &node.elif_else_clauses { self.counter_stack.last_mut().unwrap().push(0); self.visit_elif_else_clause(clause); } @@ -177,15 +169,10 @@ impl<'a> Visitor<'a> for GroupNameFinder<'a> { self.increment_usage_count(max_count); } } - Stmt::Match(ast::StmtMatch { - subject, - cases, - range: _, - node_index: _, - }) => { - self.counter_stack.push(Vec::with_capacity(cases.len())); - self.visit_expr(subject); - for match_case in cases { + Stmt::Match(node) => { + self.counter_stack.push(Vec::with_capacity(node.cases.len())); + self.visit_expr(&node.subject); + for match_case in &node.cases { self.counter_stack.last_mut().unwrap().push(0); self.visit_match_case(match_case); } @@ -196,17 +183,17 @@ impl<'a> Visitor<'a> for GroupNameFinder<'a> { self.increment_usage_count(max_count); } } - Stmt::Assign(ast::StmtAssign { targets, value, .. }) => { - if targets.iter().any(|target| self.name_matches(target)) { + Stmt::Assign(node) => { + if node.targets.iter().any(|target| self.name_matches(target)) { self.overridden = true; } else { - self.visit_expr(value); + self.visit_expr(&node.value); } } - Stmt::AnnAssign(ast::StmtAnnAssign { target, value, .. }) => { - if self.name_matches(target) { + Stmt::AnnAssign(node) => { + if self.name_matches(&node.target) { self.overridden = true; - } else if let Some(expr) = value { + } else if let Some(expr) = &node.value { self.visit_expr(expr); } } diff --git a/crates/ruff_linter/src/rules/flake8_bugbear/rules/setattr_with_constant.rs b/crates/ruff_linter/src/rules/flake8_bugbear/rules/setattr_with_constant.rs index 51fee45110..0c392a5609 100644 --- a/crates/ruff_linter/src/rules/flake8_bugbear/rules/setattr_with_constant.rs +++ b/crates/ruff_linter/src/rules/flake8_bugbear/rules/setattr_with_constant.rs @@ -66,7 +66,7 @@ impl AlwaysFixableViolation for SetAttrWithConstant { } fn assignment(obj: &Expr, name: &str, value: &Expr, generator: Generator) -> String { - let stmt = Stmt::Assign(ast::StmtAssign { + let stmt = Stmt::Assign(Box::new(ast::StmtAssign { targets: vec![Expr::Attribute(ast::ExprAttribute { value: Box::new(obj.clone()), attr: Identifier::new(name.to_string(), TextRange::default()), @@ -77,7 +77,7 @@ fn assignment(obj: &Expr, name: &str, value: &Expr, generator: Generator) -> Str value: Box::new(value.clone()), range: TextRange::default(), node_index: ruff_python_ast::AtomicNodeIndex::NONE, - }); + })); generator.stmt(&stmt) } diff --git a/crates/ruff_linter/src/rules/flake8_django/rules/all_with_model_form.rs b/crates/ruff_linter/src/rules/flake8_django/rules/all_with_model_form.rs index 9a752afd92..1c24fcc7c8 100644 --- a/crates/ruff_linter/src/rules/flake8_django/rules/all_with_model_form.rs +++ b/crates/ruff_linter/src/rules/flake8_django/rules/all_with_model_form.rs @@ -59,16 +59,20 @@ pub(crate) fn all_with_model_form(checker: &Checker, class_def: &ast::StmtClassD } for element in &class_def.body { - let Stmt::ClassDef(ast::StmtClassDef { name, body, .. }) = element else { + let Stmt::ClassDef(class_def_inner) = element else { continue; }; + let name = &class_def_inner.name; + let body = &class_def_inner.body; if name != "Meta" { continue; } for element in body { - let Stmt::Assign(ast::StmtAssign { targets, value, .. }) = element else { + let Stmt::Assign(assign) = element else { continue; }; + let targets = &assign.targets; + let value = &assign.value; for target in targets { let Expr::Name(ast::ExprName { id, .. }) = target else { continue; diff --git a/crates/ruff_linter/src/rules/flake8_django/rules/exclude_with_model_form.rs b/crates/ruff_linter/src/rules/flake8_django/rules/exclude_with_model_form.rs index 2b1142f7bb..8cc4669107 100644 --- a/crates/ruff_linter/src/rules/flake8_django/rules/exclude_with_model_form.rs +++ b/crates/ruff_linter/src/rules/flake8_django/rules/exclude_with_model_form.rs @@ -57,16 +57,19 @@ pub(crate) fn exclude_with_model_form(checker: &Checker, class_def: &ast::StmtCl } for element in &class_def.body { - let Stmt::ClassDef(ast::StmtClassDef { name, body, .. }) = element else { + let Stmt::ClassDef(class_def_inner) = element else { continue; }; + let name = &class_def_inner.name; + let body = &class_def_inner.body; if name != "Meta" { continue; } for element in body { - let Stmt::Assign(ast::StmtAssign { targets, .. }) = element else { + let Stmt::Assign(assign) = element else { continue; }; + let targets = &assign.targets; for target in targets { let Expr::Name(ast::ExprName { id, .. }) = target else { continue; diff --git a/crates/ruff_linter/src/rules/flake8_django/rules/model_without_dunder_str.rs b/crates/ruff_linter/src/rules/flake8_django/rules/model_without_dunder_str.rs index e1cf9fda3f..7de5499a9f 100644 --- a/crates/ruff_linter/src/rules/flake8_django/rules/model_without_dunder_str.rs +++ b/crates/ruff_linter/src/rules/flake8_django/rules/model_without_dunder_str.rs @@ -72,7 +72,7 @@ pub(crate) fn model_without_dunder_str(checker: &Checker, class_def: &ast::StmtC fn has_dunder_method(class_def: &ast::StmtClassDef, semantic: &SemanticModel) -> bool { analyze::class::any_super_class(class_def, semantic, &|class_def| { class_def.body.iter().any(|val| match val { - Stmt::FunctionDef(ast::StmtFunctionDef { name, .. }) => name == "__str__", + Stmt::FunctionDef(node) => node.name.as_str() == "__str__", _ => false, }) }) @@ -90,24 +90,25 @@ fn is_non_abstract_model(class_def: &ast::StmtClassDef, semantic: &SemanticModel /// Check if class is abstract, in terms of Django model inheritance. fn is_model_abstract(class_def: &ast::StmtClassDef) -> bool { for element in &class_def.body { - let Stmt::ClassDef(ast::StmtClassDef { name, body, .. }) = element else { + let Stmt::ClassDef(node) = element else { continue; }; - if name != "Meta" { + if node.name.as_str() != "Meta" { continue; } - for element in body { + for element in &node.body { match element { - Stmt::Assign(ast::StmtAssign { targets, value, .. }) => { - if targets + Stmt::Assign(assign) => { + if assign + .targets .iter() - .any(|target| is_abstract_true_assignment(target, Some(value))) + .any(|target| is_abstract_true_assignment(target, Some(&assign.value))) { return true; } } - Stmt::AnnAssign(ast::StmtAnnAssign { target, value, .. }) => { - if is_abstract_true_assignment(target, value.as_deref()) { + Stmt::AnnAssign(ann_assign) => { + if is_abstract_true_assignment(&ann_assign.target, ann_assign.value.as_deref()) { return true; } } diff --git a/crates/ruff_linter/src/rules/flake8_django/rules/nullable_model_string_field.rs b/crates/ruff_linter/src/rules/flake8_django/rules/nullable_model_string_field.rs index 9ff80f0d52..7daf8b7e3d 100644 --- a/crates/ruff_linter/src/rules/flake8_django/rules/nullable_model_string_field.rs +++ b/crates/ruff_linter/src/rules/flake8_django/rules/nullable_model_string_field.rs @@ -1,4 +1,4 @@ -use ruff_python_ast::{self as ast, Expr, Stmt}; +use ruff_python_ast::{Expr, Stmt}; use ruff_macros::{ViolationMetadata, derive_message_formats}; use ruff_python_ast::helpers::is_const_true; @@ -62,10 +62,13 @@ pub(crate) fn nullable_model_string_field(checker: &Checker, body: &[Stmt]) { for statement in body { let value = match statement { - Stmt::Assign(ast::StmtAssign { value, .. }) => value, - Stmt::AnnAssign(ast::StmtAnnAssign { - value: Some(value), .. - }) => value, + Stmt::Assign(assign) => &assign.value, + Stmt::AnnAssign(ann_assign) => { + match &ann_assign.value { + Some(value) => value, + None => continue, + } + } _ => continue, }; diff --git a/crates/ruff_linter/src/rules/flake8_django/rules/unordered_body_content_in_model.rs b/crates/ruff_linter/src/rules/flake8_django/rules/unordered_body_content_in_model.rs index b3a0127343..1abf302714 100644 --- a/crates/ruff_linter/src/rules/flake8_django/rules/unordered_body_content_in_model.rs +++ b/crates/ruff_linter/src/rules/flake8_django/rules/unordered_body_content_in_model.rs @@ -153,13 +153,13 @@ impl fmt::Display for ContentType { fn get_element_type(element: &Stmt, semantic: &SemanticModel) -> Option { match element { - Stmt::Assign(ast::StmtAssign { targets, value, .. }) => { - if let Expr::Call(ast::ExprCall { func, .. }) = value.as_ref() { + Stmt::Assign(node) => { + if let Expr::Call(ast::ExprCall { func, .. }) = node.value.as_ref() { if helpers::is_model_field(func, semantic) { return Some(ContentType::FieldDeclaration); } } - let expr = targets.first()?; + let expr = node.targets.first()?; let Expr::Name(ast::ExprName { id, .. }) = expr else { return None; }; @@ -169,14 +169,14 @@ fn get_element_type(element: &Stmt, semantic: &SemanticModel) -> Option { - if name == "Meta" { + Stmt::ClassDef(node) => { + if node.name.as_str() == "Meta" { Some(ContentType::MetaClass) } else { None } } - Stmt::FunctionDef(ast::StmtFunctionDef { name, .. }) => match name.as_str() { + Stmt::FunctionDef(node) => match node.name.as_str() { name if is_dunder(name) => Some(ContentType::MagicMethod), "save" => Some(ContentType::SaveMethod), "get_absolute_url" => Some(ContentType::GetAbsoluteUrlMethod), diff --git a/crates/ruff_linter/src/rules/flake8_logging/helpers.rs b/crates/ruff_linter/src/rules/flake8_logging/helpers.rs index 47c57d14be..381b561094 100644 --- a/crates/ruff_linter/src/rules/flake8_logging/helpers.rs +++ b/crates/ruff_linter/src/rules/flake8_logging/helpers.rs @@ -1,4 +1,4 @@ -use ruff_python_ast::{Stmt, StmtTry}; +use ruff_python_ast::Stmt; use ruff_python_semantic::SemanticModel; use ruff_text_size::{Ranged, TextSize}; @@ -8,9 +8,10 @@ pub(super) fn outside_handlers(offset: TextSize, semantic: &SemanticModel) -> bo break; } - let Stmt::Try(StmtTry { handlers, .. }) = stmt else { + let Stmt::Try(try_stmt) = stmt else { continue; }; + let handlers = &try_stmt.handlers; if handlers .iter() diff --git a/crates/ruff_linter/src/rules/flake8_pie/rules/duplicate_class_field_definition.rs b/crates/ruff_linter/src/rules/flake8_pie/rules/duplicate_class_field_definition.rs index ed6777c0d4..8afedc5d2d 100644 --- a/crates/ruff_linter/src/rules/flake8_pie/rules/duplicate_class_field_definition.rs +++ b/crates/ruff_linter/src/rules/flake8_pie/rules/duplicate_class_field_definition.rs @@ -2,7 +2,7 @@ use rustc_hash::FxHashSet; use ruff_macros::{ViolationMetadata, derive_message_formats}; use ruff_python_ast::helpers::any_over_expr; -use ruff_python_ast::{self as ast, Expr, Stmt}; +use ruff_python_ast::{Expr, Stmt}; use ruff_text_size::Ranged; use crate::checkers::ast::Checker; @@ -59,15 +59,15 @@ pub(crate) fn duplicate_class_field_definition(checker: &Checker, body: &[Stmt]) for stmt in body { // Extract the property name from the assignment statement. let target = match stmt { - Stmt::Assign(ast::StmtAssign { targets, .. }) => { - if let [Expr::Name(id)] = targets.as_slice() { + Stmt::Assign(assign_stmt) => { + if let [Expr::Name(id)] = assign_stmt.targets.as_slice() { id } else { continue; } } - Stmt::AnnAssign(ast::StmtAnnAssign { target, .. }) => { - if let Expr::Name(id) = target.as_ref() { + Stmt::AnnAssign(ann_assign_stmt) => { + if let Expr::Name(id) = ann_assign_stmt.target.as_ref() { id } else { continue; @@ -78,20 +78,20 @@ pub(crate) fn duplicate_class_field_definition(checker: &Checker, body: &[Stmt]) // If this is an unrolled augmented assignment (e.g., `x = x + 1`), skip it. match stmt { - Stmt::Assign(ast::StmtAssign { value, .. }) => { - if any_over_expr(value.as_ref(), &|expr| { + Stmt::Assign(assign_stmt) => { + if any_over_expr(assign_stmt.value.as_ref(), &|expr| { expr.as_name_expr().is_some_and(|name| name.id == target.id) }) { continue; } } - Stmt::AnnAssign(ast::StmtAnnAssign { - value: Some(value), .. - }) => { - if any_over_expr(value.as_ref(), &|expr| { - expr.as_name_expr().is_some_and(|name| name.id == target.id) - }) { - continue; + Stmt::AnnAssign(ann_assign_stmt) => { + if let Some(value) = &ann_assign_stmt.value { + if any_over_expr(value.as_ref(), &|expr| { + expr.as_name_expr().is_some_and(|name| name.id == target.id) + }) { + continue; + } } } _ => continue, diff --git a/crates/ruff_linter/src/rules/flake8_pie/rules/non_unique_enums.rs b/crates/ruff_linter/src/rules/flake8_pie/rules/non_unique_enums.rs index c36234283b..b8f7d0e64d 100644 --- a/crates/ruff_linter/src/rules/flake8_pie/rules/non_unique_enums.rs +++ b/crates/ruff_linter/src/rules/flake8_pie/rules/non_unique_enums.rs @@ -58,11 +58,11 @@ impl Violation for NonUniqueEnums { pub(crate) fn non_unique_enums(checker: &Checker, parent: &Stmt, body: &[Stmt]) { let semantic = checker.semantic(); - let Stmt::ClassDef(parent) = parent else { + let Stmt::ClassDef(class_def) = parent else { return; }; - if !parent.bases().iter().any(|expr| { + if !class_def.bases().iter().any(|expr| { semantic .resolve_qualified_name(expr) .is_some_and(|qualified_name| matches!(qualified_name.segments(), ["enum", "Enum"])) @@ -72,9 +72,10 @@ pub(crate) fn non_unique_enums(checker: &Checker, parent: &Stmt, body: &[Stmt]) let mut seen_targets: FxHashSet = FxHashSet::default(); for stmt in body { - let Stmt::Assign(ast::StmtAssign { value, .. }) = stmt else { + let Stmt::Assign(assign_stmt) = stmt else { continue; }; + let value = &assign_stmt.value; if is_call_to_enum_auto(semantic, value) { continue; diff --git a/crates/ruff_linter/src/rules/flake8_pyi/rules/str_or_repr_defined_in_stub.rs b/crates/ruff_linter/src/rules/flake8_pyi/rules/str_or_repr_defined_in_stub.rs index 0bc8f55eec..d128e9f71f 100644 --- a/crates/ruff_linter/src/rules/flake8_pyi/rules/str_or_repr_defined_in_stub.rs +++ b/crates/ruff_linter/src/rules/flake8_pyi/rules/str_or_repr_defined_in_stub.rs @@ -1,4 +1,3 @@ -use ruff_python_ast as ast; use ruff_python_ast::Stmt; use ruff_macros::{ViolationMetadata, derive_message_formats}; @@ -44,17 +43,15 @@ impl AlwaysFixableViolation for StrOrReprDefinedInStub { /// PYI029 pub(crate) fn str_or_repr_defined_in_stub(checker: &Checker, stmt: &Stmt) { - let Stmt::FunctionDef(ast::StmtFunctionDef { - name, - decorator_list, - returns, - parameters, - .. - }) = stmt - else { + let Stmt::FunctionDef(func_def) = stmt else { return; }; + let name = &func_def.name; + let decorator_list = &func_def.decorator_list; + let returns = &func_def.returns; + let parameters = &func_def.parameters; + let Some(returns) = returns else { return; }; diff --git a/crates/ruff_linter/src/rules/flake8_pyi/rules/unused_private_type_definition.rs b/crates/ruff_linter/src/rules/flake8_pyi/rules/unused_private_type_definition.rs index 82f3d93519..cf4cc28225 100644 --- a/crates/ruff_linter/src/rules/flake8_pyi/rules/unused_private_type_definition.rs +++ b/crates/ruff_linter/src/rules/flake8_pyi/rules/unused_private_type_definition.rs @@ -196,15 +196,14 @@ pub(crate) fn unused_private_type_var(checker: &Checker, scope: &Scope) { let Some(source) = binding.source else { continue; }; - let stmt @ Stmt::Assign(ast::StmtAssign { targets, value, .. }) = - checker.semantic().statement(source) - else { + let stmt = checker.semantic().statement(source); + let Stmt::Assign(assign) = stmt else { continue; }; - let [Expr::Name(ast::ExprName { id, .. })] = &targets[..] else { + let [Expr::Name(ast::ExprName { id, .. })] = &assign.targets[..] else { continue; }; - let Expr::Call(ast::ExprCall { func, .. }) = value.as_ref() else { + let Expr::Call(ast::ExprCall { func, .. }) = assign.value.as_ref() else { continue; }; @@ -317,18 +316,16 @@ pub(crate) fn unused_private_type_alias(checker: &Checker, scope: &Scope) { fn extract_type_alias_name<'a>(stmt: &'a ast::Stmt, semantic: &SemanticModel) -> Option<&'a str> { match stmt { - ast::Stmt::AnnAssign(ast::StmtAnnAssign { - target, annotation, .. - }) => { - let ast::ExprName { id, .. } = target.as_name_expr()?; - if semantic.match_typing_expr(annotation, "TypeAlias") { + ast::Stmt::AnnAssign(ann_assign) => { + let ast::ExprName { id, .. } = ann_assign.target.as_name_expr()?; + if semantic.match_typing_expr(&ann_assign.annotation, "TypeAlias") { Some(id) } else { None } } - ast::Stmt::TypeAlias(ast::StmtTypeAlias { name, .. }) => { - let ast::ExprName { id, .. } = name.as_name_expr()?; + ast::Stmt::TypeAlias(type_alias) => { + let ast::ExprName { id, .. } = type_alias.name.as_name_expr()?; Some(id) } _ => None, @@ -388,9 +385,9 @@ fn extract_typeddict_name<'a>(stmt: &'a Stmt, semantic: &SemanticModel) -> Optio // class Bar(typing.TypedDict, typing.Generic[T]): // y: T // ``` - Stmt::ClassDef(class_def @ ast::StmtClassDef { name, .. }) => { + Stmt::ClassDef(class_def) => { if class_def.bases().iter().any(is_typeddict) { - Some(name) + Some(&class_def.name) } else { None } @@ -402,12 +399,12 @@ fn extract_typeddict_name<'a>(stmt: &'a Stmt, semantic: &SemanticModel) -> Optio // import typing // Baz = typing.TypedDict("Baz", {"z": bytes}) // ``` - Stmt::Assign(ast::StmtAssign { targets, value, .. }) => { - let [target] = targets.as_slice() else { + Stmt::Assign(assign) => { + let [target] = assign.targets.as_slice() else { return None; }; let ast::ExprName { id, .. } = target.as_name_expr()?; - let ast::ExprCall { func, .. } = value.as_call_expr()?; + let ast::ExprCall { func, .. } = assign.value.as_call_expr()?; if is_typeddict(func) { Some(id) } else { None } } _ => None, diff --git a/crates/ruff_linter/src/rules/flake8_pytest_style/rules/assertion.rs b/crates/ruff_linter/src/rules/flake8_pytest_style/rules/assertion.rs index c97efd8b05..e67c882681 100644 --- a/crates/ruff_linter/src/rules/flake8_pytest_style/rules/assertion.rs +++ b/crates/ruff_linter/src/rules/flake8_pytest_style/rules/assertion.rs @@ -369,10 +369,10 @@ impl Violation for PytestUnittestRaisesAssertion { /// PT027 pub(crate) fn unittest_raises_assertion_call(checker: &Checker, call: &ast::ExprCall) { // Bindings in `with` statements are handled by `unittest_raises_assertion_bindings`. - if let Stmt::With(ast::StmtWith { items, .. }) = checker.semantic().current_statement() { + if let Stmt::With(with_stmt) = checker.semantic().current_statement() { let call_ref = AnyNodeRef::from(call); - if items.iter().any(|item| { + if with_stmt.items.iter().any(|item| { AnyNodeRef::from(&item.context_expr).ptr_eq(call_ref) && item.optional_vars.is_some() }) { return; @@ -390,7 +390,11 @@ pub(crate) fn unittest_raises_assertion_binding(checker: &Checker, binding: &Bin let semantic = checker.semantic(); - let Some(Stmt::With(with)) = binding.statement(semantic) else { + let Some(stmt) = binding.statement(semantic) else { + return; + }; + + let Stmt::With(with) = stmt else { return; }; diff --git a/crates/ruff_linter/src/rules/flake8_pytest_style/rules/raises.rs b/crates/ruff_linter/src/rules/flake8_pytest_style/rules/raises.rs index 04c9a8c372..a43612e1f8 100644 --- a/crates/ruff_linter/src/rules/flake8_pytest_style/rules/raises.rs +++ b/crates/ruff_linter/src/rules/flake8_pytest_style/rules/raises.rs @@ -220,11 +220,11 @@ pub(crate) fn complex_raises(checker: &Checker, stmt: &Stmt, items: &[WithItem], if raises_called { let is_too_complex = if let [stmt] = body { match stmt { - Stmt::With(ast::StmtWith { body, .. }) => is_non_trivial_with_body(body), + Stmt::With(with_stmt) => is_non_trivial_with_body(&with_stmt.body), // Allow function and class definitions to test decorators. Stmt::ClassDef(_) | Stmt::FunctionDef(_) => false, // Allow empty `for` loops to test iterators. - Stmt::For(ast::StmtFor { body, .. }) => match &body[..] { + Stmt::For(for_stmt) => match &for_stmt.body[..] { [Stmt::Pass(_)] => false, [Stmt::Expr(ast::StmtExpr { value, .. })] => !value.is_ellipsis_literal_expr(), _ => true, diff --git a/crates/ruff_linter/src/rules/flake8_pytest_style/rules/unittest_assert.rs b/crates/ruff_linter/src/rules/flake8_pytest_style/rules/unittest_assert.rs index 6a5f89a6af..92ec29ec4a 100644 --- a/crates/ruff_linter/src/rules/flake8_pytest_style/rules/unittest_assert.rs +++ b/crates/ruff_linter/src/rules/flake8_pytest_style/rules/unittest_assert.rs @@ -162,12 +162,12 @@ impl TryFrom<&str> for UnittestAssert { } fn assert(expr: &Expr, msg: Option<&Expr>) -> Stmt { - Stmt::Assert(ast::StmtAssert { + Stmt::Assert(Box::new(ast::StmtAssert { test: Box::new(expr.clone()), msg: msg.map(|msg| Box::new(msg.clone())), range: TextRange::default(), node_index: ruff_python_ast::AtomicNodeIndex::NONE, - }) + })) } fn compare(left: &Expr, cmp_op: CmpOp, right: &Expr) -> Expr { diff --git a/crates/ruff_linter/src/rules/flake8_pytest_style/rules/warns.rs b/crates/ruff_linter/src/rules/flake8_pytest_style/rules/warns.rs index 3ee1436118..77ae359cd1 100644 --- a/crates/ruff_linter/src/rules/flake8_pytest_style/rules/warns.rs +++ b/crates/ruff_linter/src/rules/flake8_pytest_style/rules/warns.rs @@ -206,11 +206,11 @@ pub(crate) fn complex_warns(checker: &Checker, stmt: &Stmt, items: &[WithItem], if warns_called { let is_too_complex = if let [stmt] = body { match stmt { - Stmt::With(ast::StmtWith { body, .. }) => is_non_trivial_with_body(body), + Stmt::With(with_stmt) => is_non_trivial_with_body(&with_stmt.body), // Allow function and class definitions to test decorators. Stmt::ClassDef(_) | Stmt::FunctionDef(_) => false, // Allow empty `for` loops to test iterators. - Stmt::For(ast::StmtFor { body, .. }) => match &body[..] { + Stmt::For(for_stmt) => match &for_stmt.body[..] { [Stmt::Pass(_)] => false, [Stmt::Expr(ast::StmtExpr { value, .. })] => !value.is_ellipsis_literal_expr(), _ => true, diff --git a/crates/ruff_linter/src/rules/flake8_return/rules/function.rs b/crates/ruff_linter/src/rules/flake8_return/rules/function.rs index 448cbd51a7..6377c4bd6b 100644 --- a/crates/ruff_linter/src/rules/flake8_return/rules/function.rs +++ b/crates/ruff_linter/src/rules/flake8_return/rules/function.rs @@ -448,12 +448,12 @@ fn is_noreturn_func(func: &Expr, semantic: &SemanticModel) -> bool { return false; }; - let Stmt::FunctionDef(ast::StmtFunctionDef { returns, .. }) = semantic.statement(node_id) + let Stmt::FunctionDef(node) = semantic.statement(node_id) else { return false; }; - let Some(returns) = returns.as_ref() else { + let Some(returns) = node.returns.as_ref() else { return false; }; @@ -481,19 +481,16 @@ fn add_return_none(checker: &Checker, stmt: &Stmt, range: TextRange) { fn has_implicit_return(checker: &Checker, stmt: &Stmt) -> bool { match stmt { - Stmt::If(ast::StmtIf { - body, - elif_else_clauses, - .. - }) => { - if body + Stmt::If(node) => { + if node + .body .last() .is_some_and(|last| has_implicit_return(checker, last)) { return true; } - if elif_else_clauses.iter().any(|clause| { + if node.elif_else_clauses.iter().any(|clause| { clause .body .last() @@ -504,25 +501,33 @@ fn has_implicit_return(checker: &Checker, stmt: &Stmt) -> bool { // Check if we don't have an else clause matches!( - elif_else_clauses.last(), + node.elif_else_clauses.last(), None | Some(ast::ElifElseClause { test: Some(_), .. }) ) } - Stmt::Assert(ast::StmtAssert { test, .. }) if is_const_false(test) => false, - Stmt::While(ast::StmtWhile { test, .. }) if is_const_true(test) => false, - Stmt::For(ast::StmtFor { orelse, .. }) | Stmt::While(ast::StmtWhile { orelse, .. }) => { - if let Some(last_stmt) = orelse.last() { + Stmt::Assert(node) if is_const_false(&node.test) => false, + Stmt::While(node) if is_const_true(&node.test) => false, + Stmt::For(node) => { + if let Some(last_stmt) = node.orelse.last() { has_implicit_return(checker, last_stmt) } else { true } } - Stmt::Match(ast::StmtMatch { cases, .. }) => cases.iter().any(|case| { + Stmt::While(node) => { + if let Some(last_stmt) = node.orelse.last() { + has_implicit_return(checker, last_stmt) + } else { + true + } + } + Stmt::Match(node) => node.cases.iter().any(|case| { case.body .last() .is_some_and(|last| has_implicit_return(checker, last)) }), - Stmt::With(ast::StmtWith { body, .. }) => body + Stmt::With(node) => node + .body .last() .is_some_and(|last_stmt| has_implicit_return(checker, last_stmt)), Stmt::Return(_) | Stmt::Raise(_) | Stmt::Try(_) => false, diff --git a/crates/ruff_linter/src/rules/flake8_return/visitor.rs b/crates/ruff_linter/src/rules/flake8_return/visitor.rs index e939209f53..a1e8e1c6b5 100644 --- a/crates/ruff_linter/src/rules/flake8_return/visitor.rs +++ b/crates/ruff_linter/src/rules/flake8_return/visitor.rs @@ -62,11 +62,11 @@ impl<'semantic, 'data> ReturnVisitor<'semantic, 'data> { impl<'a> Visitor<'a> for ReturnVisitor<'_, 'a> { fn visit_stmt(&mut self, stmt: &'a Stmt) { match stmt { - Stmt::ClassDef(ast::StmtClassDef { decorator_list, .. }) => { + Stmt::ClassDef(node) => { // Visit the decorators, etc. self.sibling = Some(stmt); self.parents.push(stmt); - for decorator in decorator_list { + for decorator in &node.decorator_list { visitor::walk_decorator(self, decorator); } self.parents.pop(); @@ -74,12 +74,15 @@ impl<'a> Visitor<'a> for ReturnVisitor<'_, 'a> { // But don't recurse into the body. return; } - Stmt::FunctionDef(ast::StmtFunctionDef { - parameters, - decorator_list, - returns, - .. - }) => { + Stmt::FunctionDef(node) => { + let ast::StmtFunctionDef { + parameters, + decorator_list, + returns, + range: _, + node_index: _, + .. + } = &**node; // Visit the decorators, etc. self.sibling = Some(stmt); self.parents.push(stmt); @@ -95,24 +98,30 @@ impl<'a> Visitor<'a> for ReturnVisitor<'_, 'a> { // But don't recurse into the body. return; } - Stmt::Global(ast::StmtGlobal { - names, - range: _, - node_index: _, - }) - | Stmt::Nonlocal(ast::StmtNonlocal { - names, - range: _, - node_index: _, - }) => { + Stmt::Global(node) => { + let ast::StmtGlobal { + names, + range: _, + node_index: _, + } = &**node; self.stack .non_locals .extend(names.iter().map(Identifier::as_str)); } - Stmt::AnnAssign(ast::StmtAnnAssign { target, value, .. }) => { + Stmt::Nonlocal(node) => { + let ast::StmtNonlocal { + names, + range: _, + node_index: _, + } = &**node; + self.stack + .non_locals + .extend(names.iter().map(Identifier::as_str)); + } + Stmt::AnnAssign(node) => { // Ex) `x: int` - if value.is_none() { - if let Expr::Name(name) = target.as_ref() { + if node.value.is_none() { + if let Expr::Name(name) = node.target.as_ref() { self.stack.annotations.insert(name.id.as_str()); } } @@ -140,11 +149,11 @@ impl<'a> Visitor<'a> for ReturnVisitor<'_, 'a> { // x = f.read() // return x // ``` - Stmt::With(with) => { + Stmt::With(with_node) => { if let Some(stmt_assign) = - with.body.last().and_then(Stmt::as_assign_stmt) + with_node.body.last().and_then(Stmt::as_assign_stmt) { - if !has_conditional_body(with, self.semantic) { + if !has_conditional_body(with_node, self.semantic) { self.stack.assignment_return.push(( stmt_assign, stmt_return, @@ -159,11 +168,14 @@ impl<'a> Visitor<'a> for ReturnVisitor<'_, 'a> { self.stack.returns.push(stmt_return); } - Stmt::If(ast::StmtIf { - body, - elif_else_clauses, - .. - }) => { + Stmt::If(node) => { + let ast::StmtIf { + body, + elif_else_clauses, + range: _, + node_index: _, + .. + } = &**node; if let Some(first) = elif_else_clauses.first() { self.stack.elifs_elses.push((body, first)); } diff --git a/crates/ruff_linter/src/rules/flake8_simplify/rules/ast_unary_op.rs b/crates/ruff_linter/src/rules/flake8_simplify/rules/ast_unary_op.rs index f7977066cf..e20354ed06 100644 --- a/crates/ruff_linter/src/rules/flake8_simplify/rules/ast_unary_op.rs +++ b/crates/ruff_linter/src/rules/flake8_simplify/rules/ast_unary_op.rs @@ -140,10 +140,10 @@ fn is_dunder_method(name: &str) -> bool { } fn is_exception_check(stmt: &Stmt) -> bool { - let Stmt::If(ast::StmtIf { body, .. }) = stmt else { + let Stmt::If(node) = stmt else { return false; }; - matches!(body.as_slice(), [Stmt::Raise(_)]) + matches!(node.body.as_slice(), [Stmt::Raise(_)]) } /// SIM201 diff --git a/crates/ruff_linter/src/rules/flake8_simplify/rules/ast_with.rs b/crates/ruff_linter/src/rules/flake8_simplify/rules/ast_with.rs index c115122453..115a295187 100644 --- a/crates/ruff_linter/src/rules/flake8_simplify/rules/ast_with.rs +++ b/crates/ruff_linter/src/rules/flake8_simplify/rules/ast_with.rs @@ -68,18 +68,10 @@ impl Violation for MultipleWithStatements { /// Returns a boolean indicating whether it's an async with statement, the items /// and body. fn next_with(body: &[Stmt]) -> Option<(bool, &[WithItem], &[Stmt])> { - let [ - Stmt::With(ast::StmtWith { - is_async, - items, - body, - .. - }), - ] = body - else { + let [Stmt::With(node)] = body else { return None; }; - Some((*is_async, items, body)) + Some((node.is_async, &node.items, &node.body)) } /// Check if `with_items` contains a single item which should not necessarily be @@ -139,8 +131,8 @@ pub(crate) fn multiple_with_statements( // with B(), C(): // print("hello") // ``` - if let Some(Stmt::With(ast::StmtWith { body, .. })) = with_parent { - if body.len() == 1 { + if let Some(Stmt::With(node)) = with_parent { + if node.body.len() == 1 { return; } } diff --git a/crates/ruff_linter/src/rules/flake8_simplify/rules/collapsible_if.rs b/crates/ruff_linter/src/rules/flake8_simplify/rules/collapsible_if.rs index f0d726cc52..fe361382cc 100644 --- a/crates/ruff_linter/src/rules/flake8_simplify/rules/collapsible_if.rs +++ b/crates/ruff_linter/src/rules/flake8_simplify/rules/collapsible_if.rs @@ -230,21 +230,13 @@ fn nested_if_body(stmt_if: &ast::StmtIf) -> Option> { /// ... /// ``` fn find_last_nested_if(body: &[Stmt]) -> Option<&Expr> { - let [ - Stmt::If(ast::StmtIf { - test, - body: inner_body, - elif_else_clauses, - .. - }), - ] = body - else { + let [Stmt::If(node)] = body else { return None; }; - if !elif_else_clauses.is_empty() { + if !node.elif_else_clauses.is_empty() { return None; } - find_last_nested_if(inner_body).or(Some(test)) + find_last_nested_if(&node.body).or(Some(&node.test)) } /// Returns `true` if an expression is an `if __name__ == "__main__":` check. diff --git a/crates/ruff_linter/src/rules/flake8_simplify/rules/enumerate_for_loop.rs b/crates/ruff_linter/src/rules/flake8_simplify/rules/enumerate_for_loop.rs index a35513de85..4ef3877bf3 100644 --- a/crates/ruff_linter/src/rules/flake8_simplify/rules/enumerate_for_loop.rs +++ b/crates/ruff_linter/src/rules/flake8_simplify/rules/enumerate_for_loop.rs @@ -165,20 +165,18 @@ pub(crate) fn enumerate_for_loop(checker: &Checker, for_stmt: &ast::StmtFor) { /// If the statement is an index increment statement (e.g., `i += 1`), return /// the name of the index variable. fn match_index_increment(stmt: &Stmt) -> Option<&ast::ExprName> { - let Stmt::AugAssign(ast::StmtAugAssign { - target, - op: Operator::Add, - value, - .. - }) = stmt - else { + let Stmt::AugAssign(node) = stmt else { return None; }; - let name = target.as_name_expr()?; + if !matches!(node.op, Operator::Add) { + return None; + } + + let name = node.target.as_name_expr()?; if matches!( - value.as_ref(), + node.value.as_ref(), Expr::NumberLiteral(ast::ExprNumberLiteral { value: Number::Int(Int::ONE), .. diff --git a/crates/ruff_linter/src/rules/flake8_simplify/rules/if_else_block_instead_of_dict_get.rs b/crates/ruff_linter/src/rules/flake8_simplify/rules/if_else_block_instead_of_dict_get.rs index 91f837647f..a21786d2e5 100644 --- a/crates/ruff_linter/src/rules/flake8_simplify/rules/if_else_block_instead_of_dict_get.rs +++ b/crates/ruff_linter/src/rules/flake8_simplify/rules/if_else_block_instead_of_dict_get.rs @@ -98,26 +98,16 @@ pub(crate) fn if_else_block_instead_of_dict_get(checker: &Checker, stmt_if: &ast let [else_body_stmt] = else_body.as_slice() else { return; }; - let Stmt::Assign(ast::StmtAssign { - targets: body_var, - value: body_value, - .. - }) = &body_stmt - else { + let Stmt::Assign(body_node) = &body_stmt else { return; }; - let [body_var] = body_var.as_slice() else { + let [body_var] = body_node.targets.as_slice() else { return; }; - let Stmt::Assign(ast::StmtAssign { - targets: orelse_var, - value: orelse_value, - .. - }) = &else_body_stmt - else { + let Stmt::Assign(orelse_node) = &else_body_stmt else { return; }; - let [orelse_var] = orelse_var.as_slice() else { + let [orelse_var] = orelse_node.targets.as_slice() else { return; }; @@ -143,8 +133,8 @@ pub(crate) fn if_else_block_instead_of_dict_get(checker: &Checker, stmt_if: &ast } let (expected_var, expected_value, default_var, default_value) = match ops[..] { - [CmpOp::In] => (body_var, body_value, orelse_var, orelse_value.as_ref()), - [CmpOp::NotIn] => (orelse_var, orelse_value, body_var, body_value.as_ref()), + [CmpOp::In] => (body_var, &body_node.value, orelse_var, orelse_node.value.as_ref()), + [CmpOp::NotIn] => (orelse_var, &orelse_node.value, body_var, body_node.value.as_ref()), _ => { return; } diff --git a/crates/ruff_linter/src/rules/flake8_simplify/rules/if_else_block_instead_of_if_exp.rs b/crates/ruff_linter/src/rules/flake8_simplify/rules/if_else_block_instead_of_if_exp.rs index f69098f697..5c04f64240 100644 --- a/crates/ruff_linter/src/rules/flake8_simplify/rules/if_else_block_instead_of_if_exp.rs +++ b/crates/ruff_linter/src/rules/flake8_simplify/rules/if_else_block_instead_of_if_exp.rs @@ -112,27 +112,13 @@ pub(crate) fn if_else_block_instead_of_if_exp(checker: &Checker, stmt_if: &ast:: else { return; }; - let [ - Stmt::Assign(ast::StmtAssign { - targets: body_targets, - value: body_value, - .. - }), - ] = body.as_slice() - else { + let [Stmt::Assign(body_node)] = body.as_slice() else { return; }; - let [ - Stmt::Assign(ast::StmtAssign { - targets: else_targets, - value: else_value, - .. - }), - ] = else_body.as_slice() - else { + let [Stmt::Assign(else_node)] = else_body.as_slice() else { return; }; - let ([body_target], [else_target]) = (body_targets.as_slice(), else_targets.as_slice()) else { + let ([body_target], [else_target]) = (body_node.targets.as_slice(), else_node.targets.as_slice()) else { return; }; let Expr::Name(ast::ExprName { id: body_id, .. }) = body_target else { @@ -148,13 +134,13 @@ pub(crate) fn if_else_block_instead_of_if_exp(checker: &Checker, stmt_if: &ast:: // Avoid suggesting ternary for `if (yield ...)`-style checks. // TODO(charlie): Fix precedence handling for yields in generator. if matches!( - body_value.as_ref(), + body_node.value.as_ref(), Expr::Yield(_) | Expr::YieldFrom(_) | Expr::Await(_) ) { return; } if matches!( - else_value.as_ref(), + else_node.value.as_ref(), Expr::Yield(_) | Expr::YieldFrom(_) | Expr::Await(_) ) { return; @@ -190,20 +176,20 @@ pub(crate) fn if_else_block_instead_of_if_exp(checker: &Checker, stmt_if: &ast:: // - If `test == not body_value`, replace with `target_var = body_value and else_value` // - If `not test == body_value`, replace with `target_var = body_value and else_value` // - Otherwise, replace with `target_var = body_value if test else else_value` - let (contents, assignment_kind) = match (test, body_value) { - (test_node, body_node) - if ComparableExpr::from(test_node) == ComparableExpr::from(body_node) + let (contents, assignment_kind) = match (test, &body_node.value) { + (test_node, body_val_node) + if ComparableExpr::from(test_node) == ComparableExpr::from(body_val_node.as_ref()) && !contains_effect(test_node, |id| checker.semantic().has_builtin_binding(id)) => { let target_var = &body_target; - let binary = assignment_binary_or(target_var, body_value, else_value); + let binary = assignment_binary_or(target_var, &body_node.value, &else_node.value); (checker.generator().stmt(&binary), AssignmentKind::Binary) } - (test_node, body_node) + (test_node, body_val_node) if (test_node.as_unary_op_expr().is_some_and(|op_expr| { op_expr.op.is_not() - && ComparableExpr::from(&op_expr.operand) == ComparableExpr::from(body_node) - }) || body_node.as_unary_op_expr().is_some_and(|op_expr| { + && ComparableExpr::from(&op_expr.operand) == ComparableExpr::from(body_val_node.as_ref()) + }) || body_val_node.as_ref().as_unary_op_expr().is_some_and(|op_expr| { op_expr.op.is_not() && ComparableExpr::from(&op_expr.operand) == ComparableExpr::from(test_node) })) && !contains_effect(test_node, |id| { @@ -211,12 +197,12 @@ pub(crate) fn if_else_block_instead_of_if_exp(checker: &Checker, stmt_if: &ast:: }) => { let target_var = &body_target; - let binary = assignment_binary_and(target_var, body_value, else_value); + let binary = assignment_binary_and(target_var, &body_node.value, &else_node.value); (checker.generator().stmt(&binary), AssignmentKind::Binary) } _ => { let target_var = &body_target; - let ternary = assignment_ternary(target_var, body_value, test, else_value); + let ternary = assignment_ternary(target_var, &body_node.value, test, &else_node.value); (checker.generator().stmt(&ternary), AssignmentKind::Ternary) } }; diff --git a/crates/ruff_linter/src/rules/flake8_simplify/rules/needless_bool.rs b/crates/ruff_linter/src/rules/flake8_simplify/rules/needless_bool.rs index 4b2dbb7d9a..856ff4e939 100644 --- a/crates/ruff_linter/src/rules/flake8_simplify/rules/needless_bool.rs +++ b/crates/ruff_linter/src/rules/flake8_simplify/rules/needless_bool.rs @@ -92,15 +92,9 @@ impl Violation for NeedlessBool { /// SIM103 pub(crate) fn needless_bool(checker: &Checker, stmt: &Stmt) { let Stmt::If(stmt_if) = stmt else { return }; - let ast::StmtIf { - test: if_test, - body: if_body, - elif_else_clauses, - .. - } = stmt_if; // Extract an `if` or `elif` (that returns) followed by an else (that returns the same value) - let (if_test, if_body, else_body, range) = match elif_else_clauses.as_slice() { + let (if_test, if_body, else_body, range) = match stmt_if.elif_else_clauses.as_slice() { // if-else case: // ```python // if x > 0: @@ -115,8 +109,8 @@ pub(crate) fn needless_bool(checker: &Checker, stmt: &Stmt) { .. }, ] => ( - if_test.as_ref(), - if_body, + stmt_if.test.as_ref(), + stmt_if.body.as_slice(), else_body.as_slice(), stmt_if.range(), ), @@ -143,7 +137,7 @@ pub(crate) fn needless_bool(checker: &Checker, stmt: &Stmt) { }, ] => ( elif_test, - elif_body, + elif_body.as_slice(), else_body.as_slice(), TextRange::new(elif_range.start(), else_range.end()), ), @@ -155,7 +149,7 @@ pub(crate) fn needless_bool(checker: &Checker, stmt: &Stmt) { // ``` [] => { // Fetching the next sibling is expensive, so do some validation early. - if is_one_line_return_bool(if_body).is_none() { + if is_one_line_return_bool(&stmt_if.body).is_none() { return; } @@ -175,8 +169,8 @@ pub(crate) fn needless_bool(checker: &Checker, stmt: &Stmt) { } ( - if_test.as_ref(), - if_body, + stmt_if.test.as_ref(), + stmt_if.body.as_slice(), std::slice::from_ref(next_stmt), TextRange::new(stmt_if.start(), next_stmt.end()), ) @@ -231,7 +225,7 @@ pub(crate) fn needless_bool(checker: &Checker, stmt: &Stmt) { op: ast::UnaryOp::Not, operand, .. - }) => Some((**operand).clone()), + }) => Some(operand.clone()), Expr::Compare(ast::ExprCompare { ops, @@ -252,26 +246,26 @@ pub(crate) fn needless_bool(checker: &Checker, stmt: &Stmt) { unreachable!("Single comparison with multiple comparators"); }; - Some(Expr::Compare(ast::ExprCompare { + Some(Box::new(Expr::Compare(ast::ExprCompare { ops: Box::new([op.negate()]), left: left.clone(), comparators: Box::new([right.clone()]), range: TextRange::default(), node_index: ruff_python_ast::AtomicNodeIndex::NONE, - })) + }))) } - _ => Some(Expr::UnaryOp(ast::ExprUnaryOp { + _ => Some(Box::new(Expr::UnaryOp(ast::ExprUnaryOp { op: ast::UnaryOp::Not, operand: Box::new(if_test.clone()), range: TextRange::default(), node_index: ruff_python_ast::AtomicNodeIndex::NONE, - })), + }))), } } else if if_test.is_compare_expr() { // If the condition is a comparison, we can replace it with the condition, since we // know it's a boolean. - Some(if_test.clone()) + Some(Box::new(if_test.clone())) } else if checker.semantic().has_builtin_binding("bool") { // Otherwise, we need to wrap the condition in a call to `bool`. let func_node = ast::ExprName { @@ -291,7 +285,7 @@ pub(crate) fn needless_bool(checker: &Checker, stmt: &Stmt) { range: TextRange::default(), node_index: ruff_python_ast::AtomicNodeIndex::NONE, }; - Some(Expr::Call(call_node)) + Some(Box::new(Expr::Call(call_node))) } else { None } @@ -300,7 +294,7 @@ pub(crate) fn needless_bool(checker: &Checker, stmt: &Stmt) { // Generate the replacement `return` statement. let replacement = condition.as_ref().map(|expr| { Stmt::Return(ast::StmtReturn { - value: Some(Box::new(expr.clone())), + value: Some(expr.clone()), range: TextRange::default(), node_index: ruff_python_ast::AtomicNodeIndex::NONE, }) diff --git a/crates/ruff_linter/src/rules/flake8_simplify/rules/open_file_with_context_handler.rs b/crates/ruff_linter/src/rules/flake8_simplify/rules/open_file_with_context_handler.rs index a21af6475f..0bcc65fa7d 100644 --- a/crates/ruff_linter/src/rules/flake8_simplify/rules/open_file_with_context_handler.rs +++ b/crates/ruff_linter/src/rules/flake8_simplify/rules/open_file_with_context_handler.rs @@ -68,8 +68,8 @@ fn match_async_exit_stack(semantic: &SemanticModel) -> bool { return false; } for parent in semantic.current_statements() { - if let Stmt::With(ast::StmtWith { items, .. }) = parent { - for item in items { + if let Stmt::With(node) = parent { + for item in &node.items { if let Expr::Call(ast::ExprCall { func, .. }) = &item.context_expr { if semantic .resolve_qualified_name(func) @@ -102,8 +102,8 @@ fn match_exit_stack(semantic: &SemanticModel) -> bool { return false; } for parent in semantic.current_statements() { - if let Stmt::With(ast::StmtWith { items, .. }) = parent { - for item in items { + if let Stmt::With(node) = parent { + for item in &node.items { if let Expr::Call(ast::ExprCall { func, .. }) = &item.context_expr { if semantic .resolve_qualified_name(func) diff --git a/crates/ruff_linter/src/rules/flake8_simplify/rules/reimplemented_builtin.rs b/crates/ruff_linter/src/rules/flake8_simplify/rules/reimplemented_builtin.rs index 4c858fb799..d9ab1e177e 100644 --- a/crates/ruff_linter/src/rules/flake8_simplify/rules/reimplemented_builtin.rs +++ b/crates/ruff_linter/src/rules/flake8_simplify/rules/reimplemented_builtin.rs @@ -269,27 +269,25 @@ struct Terminal<'a> { } fn match_loop(stmt: &Stmt) -> Option> { - let Stmt::For(ast::StmtFor { - body, target, iter, .. - }) = stmt - else { + let Stmt::For(for_stmt) = stmt else { return None; }; + let ast::StmtFor { + body, target, iter, .. + } = &**for_stmt; // The loop itself should contain a single `if` statement, with a single `return` statement in // the body. - let [ - Stmt::If(ast::StmtIf { - body: nested_body, - test: nested_test, - elif_else_clauses: nested_elif_else_clauses, - range: _, - node_index: _, - }), - ] = body.as_slice() - else { + let [Stmt::If(if_stmt)] = body.as_slice() else { return None; }; + let ast::StmtIf { + body: nested_body, + test: nested_test, + elif_else_clauses: nested_elif_else_clauses, + range: _, + node_index: _, + } = &**if_stmt; if !nested_elif_else_clauses.is_empty() { return None; } @@ -326,9 +324,10 @@ fn match_loop(stmt: &Stmt) -> Option> { /// return False /// ``` fn match_else_return(stmt: &Stmt) -> Option> { - let Stmt::For(ast::StmtFor { orelse, .. }) = stmt else { + let Stmt::For(for_stmt) = stmt else { return None; }; + let ast::StmtFor { orelse, .. } = &**for_stmt; // The `else` block has to contain a single `return True` or `return False`. let [ @@ -366,9 +365,10 @@ fn match_else_return(stmt: &Stmt) -> Option> { /// return False /// ``` fn match_sibling_return<'a>(stmt: &'a Stmt, sibling: &'a Stmt) -> Option> { - let Stmt::For(ast::StmtFor { orelse, .. }) = stmt else { + let Stmt::For(for_stmt) = stmt else { return None; }; + let ast::StmtFor { orelse, .. } = &**for_stmt; // The loop itself shouldn't have an `else` block. if !orelse.is_empty() { diff --git a/crates/ruff_linter/src/rules/flake8_slots/helpers.rs b/crates/ruff_linter/src/rules/flake8_slots/helpers.rs index f949e3a278..17e32ebbe3 100644 --- a/crates/ruff_linter/src/rules/flake8_slots/helpers.rs +++ b/crates/ruff_linter/src/rules/flake8_slots/helpers.rs @@ -4,7 +4,8 @@ use ruff_python_ast::{self as ast, Expr, Stmt}; pub(super) fn has_slots(body: &[Stmt]) -> bool { for stmt in body { match stmt { - Stmt::Assign(ast::StmtAssign { targets, .. }) => { + Stmt::Assign(assign) => { + let targets = &assign.targets; for target in targets { if let Expr::Name(ast::ExprName { id, .. }) = target { if id.as_str() == "__slots__" { @@ -13,7 +14,8 @@ pub(super) fn has_slots(body: &[Stmt]) -> bool { } } } - Stmt::AnnAssign(ast::StmtAnnAssign { target, .. }) => { + Stmt::AnnAssign(ann_assign) => { + let target = &ann_assign.target; if let Expr::Name(ast::ExprName { id, .. }) = target.as_ref() { if id.as_str() == "__slots__" { return true; diff --git a/crates/ruff_linter/src/rules/flake8_tidy_imports/rules/banned_module_level_imports.rs b/crates/ruff_linter/src/rules/flake8_tidy_imports/rules/banned_module_level_imports.rs index 23d250f7a9..b69ae6ae41 100644 --- a/crates/ruff_linter/src/rules/flake8_tidy_imports/rules/banned_module_level_imports.rs +++ b/crates/ruff_linter/src/rules/flake8_tidy_imports/rules/banned_module_level_imports.rs @@ -92,9 +92,11 @@ impl<'a> BannedModuleImportPolicies<'a> { pub(crate) fn new(stmt: &'a Stmt, checker: &Checker) -> Self { match stmt { Stmt::Import(import) => Self::Import(import), - Stmt::ImportFrom(import @ StmtImportFrom { module, level, .. }) => { + Stmt::ImportFrom(import) => { + let module = &import.module; + let level = import.level; let module = resolve_imported_module_path( - *level, + level, module.as_deref(), checker.module.qualified_name(), ); diff --git a/crates/ruff_linter/src/rules/flake8_tidy_imports/rules/relative_imports.rs b/crates/ruff_linter/src/rules/flake8_tidy_imports/rules/relative_imports.rs index bc77a55716..e8a9aeb56c 100644 --- a/crates/ruff_linter/src/rules/flake8_tidy_imports/rules/relative_imports.rs +++ b/crates/ruff_linter/src/rules/flake8_tidy_imports/rules/relative_imports.rs @@ -91,9 +91,10 @@ fn fix_banned_relative_import( return None; } - let Stmt::ImportFrom(ast::StmtImportFrom { names, .. }) = stmt else { + let Stmt::ImportFrom(import_from) = stmt else { panic!("Expected Stmt::ImportFrom"); }; + let names = &import_from.names; let node = ast::StmtImportFrom { module: Some(Identifier::new( module_path.to_string(), diff --git a/crates/ruff_linter/src/rules/flake8_type_checking/rules/type_alias_quotes.rs b/crates/ruff_linter/src/rules/flake8_type_checking/rules/type_alias_quotes.rs index c07de4b813..40f9519088 100644 --- a/crates/ruff_linter/src/rules/flake8_type_checking/rules/type_alias_quotes.rs +++ b/crates/ruff_linter/src/rules/flake8_type_checking/rules/type_alias_quotes.rs @@ -158,10 +158,10 @@ pub(crate) fn unquoted_type_alias(checker: &Checker, binding: &Binding) { return; } - let Some(Stmt::AnnAssign(ast::StmtAnnAssign { - value: Some(expr), .. - })) = binding.statement(checker.semantic()) - else { + let Some(Stmt::AnnAssign(node)) = binding.statement(checker.semantic()) else { + return; + }; + let Some(expr) = &node.value else { return; }; diff --git a/crates/ruff_linter/src/rules/flake8_unused_arguments/rules/unused_arguments.rs b/crates/ruff_linter/src/rules/flake8_unused_arguments/rules/unused_arguments.rs index 884bb00a3e..aa0faaf280 100644 --- a/crates/ruff_linter/src/rules/flake8_unused_arguments/rules/unused_arguments.rs +++ b/crates/ruff_linter/src/rules/flake8_unused_arguments/rules/unused_arguments.rs @@ -1,5 +1,5 @@ use ruff_python_ast as ast; -use ruff_python_ast::{Parameter, Parameters, Stmt, StmtExpr, StmtFunctionDef, StmtRaise}; +use ruff_python_ast::{Parameter, Parameters, Stmt, StmtExpr, StmtFunctionDef}; use ruff_macros::{ViolationMetadata, derive_message_formats}; use ruff_python_semantic::analyze::{function_type, visibility}; @@ -389,14 +389,20 @@ pub(crate) fn is_not_implemented_stub_with_variable( _ => &function_def.body, }; - let [ - Stmt::Assign(ast::StmtAssign { targets, value, .. }), - Stmt::Raise(StmtRaise { - exc: Some(exception), - .. - }), - ] = statements - else { + let [stmt1, stmt2] = statements else { + return false; + }; + + let Stmt::Assign(assign_node) = stmt1 else { + return false; + }; + let targets = &assign_node.targets; + let value = &assign_node.value; + + let Stmt::Raise(raise_node) = stmt2 else { + return false; + }; + let Some(exception) = &raise_node.exc else { return false; }; diff --git a/crates/ruff_linter/src/rules/isort/annotate.rs b/crates/ruff_linter/src/rules/isort/annotate.rs index 585b58e651..516f90150f 100644 --- a/crates/ruff_linter/src/rules/isort/annotate.rs +++ b/crates/ruff_linter/src/rules/isort/annotate.rs @@ -23,11 +23,13 @@ pub(crate) fn annotate_imports<'a>( .iter() .map(|import| { match import { - Stmt::Import(ast::StmtImport { - names, - range, - node_index: _, - }) => { + Stmt::Import(import_stmt) => { + let ast::StmtImport { + names, + range, + node_index: _, + } = &**import_stmt; + // Find comments above. let mut atop = vec![]; while let Some(comment) = @@ -58,13 +60,15 @@ pub(crate) fn annotate_imports<'a>( inline, } } - Stmt::ImportFrom(ast::StmtImportFrom { - module, - names, - level, - range: _, - node_index: _, - }) => { + Stmt::ImportFrom(import_from_stmt) => { + let ast::StmtImportFrom { + module, + names, + level, + range: _, + node_index: _, + } = &**import_from_stmt; + // Find comments above. let mut atop = vec![]; while let Some(comment) = diff --git a/crates/ruff_linter/src/rules/isort/block.rs b/crates/ruff_linter/src/rules/isort/block.rs index 1dfe96dc51..76ed70bbb4 100644 --- a/crates/ruff_linter/src/rules/isort/block.rs +++ b/crates/ruff_linter/src/rules/isort/block.rs @@ -183,87 +183,77 @@ impl<'a> StatementVisitor<'a> for BlockBuilder<'a> { let prev_nested = self.nested; self.nested = true; match stmt { - Stmt::FunctionDef(ast::StmtFunctionDef { body, .. }) => { - for stmt in body { + Stmt::FunctionDef(node) => { + for stmt in &node.body { self.visit_stmt(stmt); } self.finalize(None); } - Stmt::ClassDef(ast::StmtClassDef { body, .. }) => { - for stmt in body { + Stmt::ClassDef(node) => { + for stmt in &node.body { self.visit_stmt(stmt); } self.finalize(None); } - Stmt::For(ast::StmtFor { body, orelse, .. }) => { - for stmt in body { + Stmt::For(node) => { + for stmt in &node.body { self.visit_stmt(stmt); } self.finalize(None); - for stmt in orelse { + for stmt in &node.orelse { self.visit_stmt(stmt); } self.finalize(None); } - Stmt::While(ast::StmtWhile { body, orelse, .. }) => { - for stmt in body { + Stmt::While(node) => { + for stmt in &node.body { self.visit_stmt(stmt); } self.finalize(None); - for stmt in orelse { + for stmt in &node.orelse { self.visit_stmt(stmt); } self.finalize(None); } - Stmt::If(ast::StmtIf { - body, - elif_else_clauses, - .. - }) => { - for stmt in body { + Stmt::If(node) => { + for stmt in &node.body { self.visit_stmt(stmt); } self.finalize(None); - for clause in elif_else_clauses { + for clause in &node.elif_else_clauses { self.visit_elif_else_clause(clause); } } - Stmt::With(ast::StmtWith { body, .. }) => { - for stmt in body { + Stmt::With(node) => { + for stmt in &node.body { self.visit_stmt(stmt); } self.finalize(None); } - Stmt::Match(ast::StmtMatch { cases, .. }) => { - for match_case in cases { + Stmt::Match(node) => { + for match_case in &node.cases { self.visit_match_case(match_case); } } - Stmt::Try(ast::StmtTry { - body, - handlers, - orelse, - finalbody, - .. - }) => { - for except_handler in handlers { + Stmt::Try(node) => { + for except_handler in &node.handlers { self.visit_except_handler(except_handler); } - for stmt in body { + for stmt in &node.body { self.visit_stmt(stmt); } self.finalize(None); - for stmt in orelse { + for stmt in &node.orelse { self.visit_stmt(stmt); } self.finalize(None); - for stmt in finalbody { + for stmt in &node.finalbody { self.visit_stmt(stmt); } self.finalize(None); diff --git a/crates/ruff_linter/src/rules/isort/rules/add_required_imports.rs b/crates/ruff_linter/src/rules/isort/rules/add_required_imports.rs index b887e1c2a0..cb93f7b12d 100644 --- a/crates/ruff_linter/src/rules/isort/rules/add_required_imports.rs +++ b/crates/ruff_linter/src/rules/isort/rules/add_required_imports.rs @@ -59,30 +59,30 @@ impl AlwaysFixableViolation for MissingRequiredImport { fn includes_import(stmt: &Stmt, target: &NameImport) -> bool { match target { NameImport::Import(target) => { - let Stmt::Import(ast::StmtImport { + let Stmt::Import(import_stmt) = &stmt else { + return false; + }; + let ast::StmtImport { names, range: _, node_index: _, - }) = &stmt - else { - return false; - }; + } = &**import_stmt; names.iter().any(|alias| { alias.name == target.name.name && alias.asname.as_deref() == target.name.as_name.as_deref() }) } NameImport::ImportFrom(target) => { - let Stmt::ImportFrom(ast::StmtImportFrom { + let Stmt::ImportFrom(import_from_stmt) = &stmt else { + return false; + }; + let ast::StmtImportFrom { module, names, level, range: _, node_index: _, - }) = &stmt - else { - return false; - }; + } = &**import_from_stmt; module.as_deref() == target.module.as_deref() && *level == target.level && names.iter().any(|alias| { diff --git a/crates/ruff_linter/src/rules/mccabe/rules/function_is_too_complex.rs b/crates/ruff_linter/src/rules/mccabe/rules/function_is_too_complex.rs index bc1ca1e278..eeb68dba01 100644 --- a/crates/ruff_linter/src/rules/mccabe/rules/function_is_too_complex.rs +++ b/crates/ruff_linter/src/rules/mccabe/rules/function_is_too_complex.rs @@ -71,39 +71,35 @@ fn get_complexity_number(stmts: &[Stmt]) -> usize { let mut complexity = 0; for stmt in stmts { match stmt { - Stmt::If(ast::StmtIf { - body, - elif_else_clauses, - .. - }) => { + Stmt::If(if_stmt) => { complexity += 1; - complexity += get_complexity_number(body); - for clause in elif_else_clauses { + complexity += get_complexity_number(&if_stmt.body); + for clause in &if_stmt.elif_else_clauses { if clause.test.is_some() { complexity += 1; } complexity += get_complexity_number(&clause.body); } } - Stmt::For(ast::StmtFor { body, orelse, .. }) => { + Stmt::For(for_stmt) => { complexity += 1; - complexity += get_complexity_number(body); - complexity += get_complexity_number(orelse); + complexity += get_complexity_number(&for_stmt.body); + complexity += get_complexity_number(&for_stmt.orelse); } - Stmt::With(ast::StmtWith { body, .. }) => { - complexity += get_complexity_number(body); + Stmt::With(with_stmt) => { + complexity += get_complexity_number(&with_stmt.body); } - Stmt::While(ast::StmtWhile { body, orelse, .. }) => { + Stmt::While(while_stmt) => { complexity += 1; - complexity += get_complexity_number(body); - complexity += get_complexity_number(orelse); + complexity += get_complexity_number(&while_stmt.body); + complexity += get_complexity_number(&while_stmt.orelse); } - Stmt::Match(ast::StmtMatch { cases, .. }) => { - for case in cases { + Stmt::Match(match_stmt) => { + for case in &match_stmt.cases { complexity += 1; complexity += get_complexity_number(&case.body); } - if let Some(last_case) = cases.last() { + if let Some(last_case) = match_stmt.cases.last() { // The complexity of an irrefutable pattern is similar to an `else` block of an `if` statement. // // For example: @@ -121,20 +117,14 @@ fn get_complexity_number(stmts: &[Stmt]) -> usize { } } } - Stmt::Try(ast::StmtTry { - body, - handlers, - orelse, - finalbody, - .. - }) => { - complexity += get_complexity_number(body); - if !orelse.is_empty() { + Stmt::Try(try_stmt) => { + complexity += get_complexity_number(&try_stmt.body); + if !try_stmt.orelse.is_empty() { complexity += 1; } - complexity += get_complexity_number(orelse); - complexity += get_complexity_number(finalbody); - for handler in handlers { + complexity += get_complexity_number(&try_stmt.orelse); + complexity += get_complexity_number(&try_stmt.finalbody); + for handler in &try_stmt.handlers { complexity += 1; let ExceptHandler::ExceptHandler(ast::ExceptHandlerExceptHandler { body, .. @@ -142,12 +132,12 @@ fn get_complexity_number(stmts: &[Stmt]) -> usize { complexity += get_complexity_number(body); } } - Stmt::FunctionDef(ast::StmtFunctionDef { body, .. }) => { + Stmt::FunctionDef(func_def) => { complexity += 1; - complexity += get_complexity_number(body); + complexity += get_complexity_number(&func_def.body); } - Stmt::ClassDef(ast::StmtClassDef { body, .. }) => { - complexity += get_complexity_number(body); + Stmt::ClassDef(class_def) => { + complexity += get_complexity_number(&class_def.body); } _ => {} } diff --git a/crates/ruff_linter/src/rules/numpy/helpers.rs b/crates/ruff_linter/src/rules/numpy/helpers.rs index a2e5eb6a15..d996f584f0 100644 --- a/crates/ruff_linter/src/rules/numpy/helpers.rs +++ b/crates/ruff_linter/src/rules/numpy/helpers.rs @@ -3,7 +3,7 @@ use ruff_python_ast::name::QualifiedName; use ruff_python_ast::statement_visitor::StatementVisitor; use ruff_python_ast::visitor::Visitor; use ruff_python_ast::visitor::{walk_expr, walk_stmt}; -use ruff_python_ast::{Alias, Stmt, StmtImportFrom, statement_visitor}; +use ruff_python_ast::{Alias, Stmt, statement_visitor}; use ruff_python_semantic::SemanticModel; /// AST visitor that searches an AST tree for [`ast::StmtImportFrom`] nodes @@ -28,7 +28,9 @@ impl StatementVisitor<'_> for ImportSearcher<'_> { if self.found_import { return; } - if let Stmt::ImportFrom(StmtImportFrom { module, names, .. }) = stmt { + if let Stmt::ImportFrom(import_from) = stmt { + let module = &import_from.module; + let names = &import_from.names; if module.as_ref().is_some_and(|module| module == self.module) && names.iter().any(|Alias { name, .. }| name == self.name) { diff --git a/crates/ruff_linter/src/rules/pep8_naming/helpers.rs b/crates/ruff_linter/src/rules/pep8_naming/helpers.rs index 5f29169759..14883df021 100644 --- a/crates/ruff_linter/src/rules/pep8_naming/helpers.rs +++ b/crates/ruff_linter/src/rules/pep8_naming/helpers.rs @@ -25,10 +25,10 @@ pub(super) fn is_acronym(name: &str, asname: &str) -> bool { /// Returns `true` if the statement is an assignment to a named tuple. pub(super) fn is_named_tuple_assignment(stmt: &Stmt, semantic: &SemanticModel) -> bool { - let Stmt::Assign(ast::StmtAssign { value, .. }) = stmt else { + let Stmt::Assign(node) = stmt else { return false; }; - let Expr::Call(ast::ExprCall { func, .. }) = value.as_ref() else { + let Expr::Call(ast::ExprCall { func, .. }) = node.value.as_ref() else { return false; }; semantic @@ -45,10 +45,10 @@ pub(super) fn is_typed_dict_assignment(stmt: &Stmt, semantic: &SemanticModel) -> return false; } - let Stmt::Assign(ast::StmtAssign { value, .. }) = stmt else { + let Stmt::Assign(node) = stmt else { return false; }; - let Expr::Call(ast::ExprCall { func, .. }) = value.as_ref() else { + let Expr::Call(ast::ExprCall { func, .. }) = node.value.as_ref() else { return false; }; semantic.match_typing_expr(func, "TypedDict") @@ -60,10 +60,10 @@ pub(super) fn is_type_var_assignment(stmt: &Stmt, semantic: &SemanticModel) -> b return false; } - let Stmt::Assign(ast::StmtAssign { value, .. }) = stmt else { + let Stmt::Assign(node) = stmt else { return false; }; - let Expr::Call(ast::ExprCall { func, .. }) = value.as_ref() else { + let Expr::Call(ast::ExprCall { func, .. }) = node.value.as_ref() else { return false; }; semantic @@ -77,8 +77,8 @@ pub(super) fn is_type_var_assignment(stmt: &Stmt, semantic: &SemanticModel) -> b /// Returns `true` if the statement is an assignment to a `TypeAlias`. pub(super) fn is_type_alias_assignment(stmt: &Stmt, semantic: &SemanticModel) -> bool { match stmt { - Stmt::AnnAssign(ast::StmtAnnAssign { annotation, .. }) => { - semantic.match_typing_expr(annotation, "TypeAlias") + Stmt::AnnAssign(node) => { + semantic.match_typing_expr(&node.annotation, "TypeAlias") } Stmt::TypeAlias(_) => true, _ => false, @@ -157,11 +157,15 @@ pub(super) fn is_django_model_import(name: &str, stmt: &Stmt, semantic: &Semanti } match stmt { - Stmt::AnnAssign(ast::StmtAnnAssign { - value: Some(value), .. - }) => match_model_import(name, value.as_ref(), semantic), - Stmt::Assign(ast::StmtAssign { value, .. }) => { - match_model_import(name, value.as_ref(), semantic) + Stmt::AnnAssign(node) => { + if let Some(value) = &node.value { + match_model_import(name, value.as_ref(), semantic) + } else { + false + } + } + Stmt::Assign(node) => { + match_model_import(name, node.value.as_ref(), semantic) } _ => false, } diff --git a/crates/ruff_linter/src/rules/perflint/rules/manual_dict_comprehension.rs b/crates/ruff_linter/src/rules/perflint/rules/manual_dict_comprehension.rs index 560aac29a8..cdf0090fb1 100644 --- a/crates/ruff_linter/src/rules/perflint/rules/manual_dict_comprehension.rs +++ b/crates/ruff_linter/src/rules/perflint/rules/manual_dict_comprehension.rs @@ -92,23 +92,16 @@ pub(crate) fn manual_dict_comprehension(checker: &Checker, for_stmt: &ast::StmtF // if idx % 2 == 0: // result[name] = idx // ``` - [ - Stmt::If(ast::StmtIf { - body, - elif_else_clauses, - test, - .. - }), - ] => { + [Stmt::If(node)] => { // TODO(charlie): If there's an `else` clause, verify that the `else` has the // same structure. - if !elif_else_clauses.is_empty() { + if !node.elif_else_clauses.is_empty() { return; } - let [stmt] = body.as_slice() else { + let [stmt] = node.body.as_slice() else { return; }; - (stmt, Some(test)) + (stmt, Some(&node.test)) } // ```python // for idx, name in enumerate(names): @@ -118,15 +111,12 @@ pub(crate) fn manual_dict_comprehension(checker: &Checker, for_stmt: &ast::StmtF _ => return, }; - let Stmt::Assign(ast::StmtAssign { - targets, - value, - range, - node_index: _, - }) = stmt - else { + let Stmt::Assign(node) = stmt else { return; }; + let targets = &node.targets; + let value = &node.value; + let range = &node.range; let [ Expr::Subscript(ast::ExprSubscript { @@ -212,8 +202,8 @@ pub(crate) fn manual_dict_comprehension(checker: &Checker, for_stmt: &ast::StmtF if is_fix_manual_dict_comprehension_enabled(checker.settings()) { let binding_stmt = binding.statement(checker.semantic()); let binding_value = binding_stmt.and_then(|binding_stmt| match binding_stmt { - ast::Stmt::AnnAssign(assign) => assign.value.as_deref(), - ast::Stmt::Assign(assign) => Some(&assign.value), + ast::Stmt::AnnAssign(node) => node.value.as_deref(), + ast::Stmt::Assign(node) => Some(&node.value), _ => None, }); @@ -243,7 +233,7 @@ pub(crate) fn manual_dict_comprehension(checker: &Checker, for_stmt: &ast::StmtF // but not necessarily, so this needs to be manually fixed. This does not apply when using an update. let binding_has_one_target = binding_stmt.is_some_and(|binding_stmt| match binding_stmt { ast::Stmt::AnnAssign(_) => true, - ast::Stmt::Assign(assign) => assign.targets.len() == 1, + ast::Stmt::Assign(node) => node.targets.len() == 1, _ => false, }); // If the binding gets used in between the assignment and the for loop, a comprehension is no longer safe diff --git a/crates/ruff_linter/src/rules/perflint/rules/manual_list_comprehension.rs b/crates/ruff_linter/src/rules/perflint/rules/manual_list_comprehension.rs index 84b7290e51..d5b2389376 100644 --- a/crates/ruff_linter/src/rules/perflint/rules/manual_list_comprehension.rs +++ b/crates/ruff_linter/src/rules/perflint/rules/manual_list_comprehension.rs @@ -109,21 +109,14 @@ pub(crate) fn manual_list_comprehension(checker: &Checker, for_stmt: &ast::StmtF // if z: // filtered.append(x) // ``` - [ - ast::Stmt::If(ast::StmtIf { - body, - elif_else_clauses, - test, - .. - }), - ] => { - if !elif_else_clauses.is_empty() { + [ast::Stmt::If(node)] => { + if !node.elif_else_clauses.is_empty() { return; } - let [stmt] = body.as_slice() else { + let [stmt] = node.body.as_slice() else { return; }; - (stmt, Some(test)) + (stmt, Some(&node.test)) } // ```python // for x in y: @@ -267,8 +260,8 @@ pub(crate) fn manual_list_comprehension(checker: &Checker, for_stmt: &ast::StmtF let list_binding_stmt = list_binding.statement(checker.semantic()); let list_binding_value = list_binding_stmt.and_then(|binding_stmt| match binding_stmt { - ast::Stmt::AnnAssign(assign) => assign.value.as_deref(), - ast::Stmt::Assign(assign) => Some(&assign.value), + ast::Stmt::AnnAssign(node) => node.value.as_deref(), + ast::Stmt::Assign(node) => Some(&node.value), _ => None, }); @@ -304,7 +297,7 @@ pub(crate) fn manual_list_comprehension(checker: &Checker, for_stmt: &ast::StmtF // but not necessarily, so this needs to be manually fixed. This does not apply when using an extend. let binding_has_one_target = list_binding_stmt.is_some_and(|binding_stmt| match binding_stmt { ast::Stmt::AnnAssign(_) => true, - ast::Stmt::Assign(assign) => assign.targets.len() == 1, + ast::Stmt::Assign(node) => node.targets.len() == 1, _ => false, }); @@ -464,8 +457,8 @@ fn convert_to_list_extend( let binding_stmt = binding.statement(semantic); let binding_stmt_range = binding_stmt .and_then(|stmt| match stmt { - ast::Stmt::AnnAssign(assign) => Some(assign.range), - ast::Stmt::Assign(assign) => Some(assign.range), + ast::Stmt::AnnAssign(node) => Some(node.range), + ast::Stmt::Assign(node) => Some(node.range), _ => None, }) .ok_or(anyhow!( diff --git a/crates/ruff_linter/src/rules/perflint/rules/try_except_in_loop.rs b/crates/ruff_linter/src/rules/perflint/rules/try_except_in_loop.rs index 9fdcc29446..3394390091 100644 --- a/crates/ruff_linter/src/rules/perflint/rules/try_except_in_loop.rs +++ b/crates/ruff_linter/src/rules/perflint/rules/try_except_in_loop.rs @@ -1,6 +1,6 @@ use ruff_macros::{ViolationMetadata, derive_message_formats}; use ruff_python_ast::statement_visitor::{StatementVisitor, walk_stmt}; -use ruff_python_ast::{self as ast, PythonVersion, Stmt}; +use ruff_python_ast::{PythonVersion, Stmt}; use ruff_text_size::Ranged; use crate::Violation; @@ -93,9 +93,11 @@ pub(crate) fn try_except_in_loop(checker: &Checker, body: &[Stmt]) { return; } - let [Stmt::Try(ast::StmtTry { handlers, body, .. })] = body else { + let [Stmt::Try(try_stmt)] = body else { return; }; + let handlers = &try_stmt.handlers; + let body = &try_stmt.body; let Some(handler) = handlers.first() else { return; diff --git a/crates/ruff_linter/src/rules/perflint/rules/unnecessary_list_cast.rs b/crates/ruff_linter/src/rules/perflint/rules/unnecessary_list_cast.rs index f616e0f541..dfaf380277 100644 --- a/crates/ruff_linter/src/rules/perflint/rules/unnecessary_list_cast.rs +++ b/crates/ruff_linter/src/rules/perflint/rules/unnecessary_list_cast.rs @@ -207,7 +207,7 @@ fn match_mutation(stmt: &Stmt, id: &str) -> bool { target_id == id } // Ex) `foo[0] = bar` - Stmt::Assign(ast::StmtAssign { targets, .. }) => targets.iter().any(|target| { + Stmt::Assign(node) => node.targets.iter().any(|target| { if let Some(ast::ExprSubscript { value: target, .. }) = target.as_subscript_expr() { if let Some(ast::ExprName { id: target_id, .. }) = target.as_name_expr() { return target_id == id; @@ -216,16 +216,16 @@ fn match_mutation(stmt: &Stmt, id: &str) -> bool { false }), // Ex) `foo += bar` - Stmt::AugAssign(ast::StmtAugAssign { target, .. }) => { - if let Some(ast::ExprName { id: target_id, .. }) = target.as_name_expr() { + Stmt::AugAssign(node) => { + if let Some(ast::ExprName { id: target_id, .. }) = node.target.as_name_expr() { target_id == id } else { false } } // Ex) `foo[0]: int = bar` - Stmt::AnnAssign(ast::StmtAnnAssign { target, .. }) => { - if let Some(ast::ExprSubscript { value: target, .. }) = target.as_subscript_expr() { + Stmt::AnnAssign(node) => { + if let Some(ast::ExprSubscript { value: target, .. }) = node.target.as_subscript_expr() { if let Some(ast::ExprName { id: target_id, .. }) = target.as_name_expr() { return target_id == id; } @@ -233,7 +233,7 @@ fn match_mutation(stmt: &Stmt, id: &str) -> bool { false } // Ex) `del foo[0]` - Stmt::Delete(ast::StmtDelete { targets, .. }) => targets.iter().any(|target| { + Stmt::Delete(node) => node.targets.iter().any(|target| { if let Some(ast::ExprSubscript { value: target, .. }) = target.as_subscript_expr() { if let Some(ast::ExprName { id: target_id, .. }) = target.as_name_expr() { return target_id == id; diff --git a/crates/ruff_linter/src/rules/pycodestyle/rules/bare_except.rs b/crates/ruff_linter/src/rules/pycodestyle/rules/bare_except.rs index 82bbea5beb..cc930006a4 100644 --- a/crates/ruff_linter/src/rules/pycodestyle/rules/bare_except.rs +++ b/crates/ruff_linter/src/rules/pycodestyle/rules/bare_except.rs @@ -1,6 +1,6 @@ use ruff_macros::{ViolationMetadata, derive_message_formats}; use ruff_python_ast::identifier::except; -use ruff_python_ast::{self as ast, ExceptHandler, Expr, Stmt}; +use ruff_python_ast::{ExceptHandler, Expr, Stmt}; use crate::Violation; use crate::checkers::ast::Checker; @@ -65,7 +65,7 @@ pub(crate) fn bare_except( if type_.is_none() && !body .iter() - .any(|stmt| matches!(stmt, Stmt::Raise(ast::StmtRaise { exc: None, .. }))) + .any(|stmt| matches!(stmt, Stmt::Raise(raise_stmt) if raise_stmt.exc.is_none())) { checker.report_diagnostic(BareExcept, except(handler, checker.locator().contents())); } diff --git a/crates/ruff_linter/src/rules/pycodestyle/rules/lambda_assignment.rs b/crates/ruff_linter/src/rules/pycodestyle/rules/lambda_assignment.rs index 9b437aa279..1e485df343 100644 --- a/crates/ruff_linter/src/rules/pycodestyle/rules/lambda_assignment.rs +++ b/crates/ruff_linter/src/rules/pycodestyle/rules/lambda_assignment.rs @@ -223,7 +223,7 @@ fn function( ..parameter.clone() }) .collect::>(); - let func = Stmt::FunctionDef(ast::StmtFunctionDef { + let func = Stmt::FunctionDef(Box::new(ast::StmtFunctionDef { is_async: false, name: Identifier::new(name.to_string(), TextRange::default()), parameters: Box::new(Parameters { @@ -237,13 +237,13 @@ fn function( type_params: None, range: TextRange::default(), node_index: ruff_python_ast::AtomicNodeIndex::NONE, - }); + })); let generated = checker.generator().stmt(&func); return replace_trailing_ellipsis_with_original_expr(generated, lambda, stmt, checker); } } - let function = Stmt::FunctionDef(ast::StmtFunctionDef { + let function = Stmt::FunctionDef(Box::new(ast::StmtFunctionDef { is_async: false, name: Identifier::new(name.to_string(), TextRange::default()), parameters: Box::new(parameters), @@ -253,7 +253,7 @@ fn function( type_params: None, range: TextRange::default(), node_index: ruff_python_ast::AtomicNodeIndex::NONE, - }); + })); let generated = checker.generator().stmt(&function); replace_trailing_ellipsis_with_original_expr(generated, lambda, stmt, checker) diff --git a/crates/ruff_linter/src/rules/pydoclint/rules/check_docstring.rs b/crates/ruff_linter/src/rules/pydoclint/rules/check_docstring.rs index 2f84e8501d..83f6774ab4 100644 --- a/crates/ruff_linter/src/rules/pydoclint/rules/check_docstring.rs +++ b/crates/ruff_linter/src/rules/pydoclint/rules/check_docstring.rs @@ -942,8 +942,8 @@ impl<'a> Visitor<'a> for BodyVisitor<'a> { fn visit_stmt(&mut self, stmt: &'a Stmt) { match stmt { - Stmt::Raise(ast::StmtRaise { exc, .. }) => { - if let Some(exc) = exc.as_ref() { + Stmt::Raise(node) => { + if let Some(exc) = node.exc.as_ref() { // First try to resolve the exception directly if let Some(qualified_name) = self.semantic.resolve_qualified_name(map_callable(exc)) diff --git a/crates/ruff_linter/src/rules/pyflakes/rules/unused_import.rs b/crates/ruff_linter/src/rules/pyflakes/rules/unused_import.rs index 88936e22ab..bee67a730a 100644 --- a/crates/ruff_linter/src/rules/pyflakes/rules/unused_import.rs +++ b/crates/ruff_linter/src/rules/pyflakes/rules/unused_import.rs @@ -300,9 +300,9 @@ fn find_dunder_all_exprs<'a>(semantic: &'a SemanticModel) -> Vec<&'a ast::Expr> _ => None, }?; match stmt { - Stmt::Assign(ast::StmtAssign { value, .. }) => Some(&**value), - Stmt::AnnAssign(ast::StmtAnnAssign { value, .. }) => value.as_deref(), - Stmt::AugAssign(ast::StmtAugAssign { value, .. }) => Some(&**value), + Stmt::Assign(node) => Some(&*node.value), + Stmt::AnnAssign(node) => node.value.as_deref(), + Stmt::AugAssign(node) => Some(&*node.value), _ => None, } }) diff --git a/crates/ruff_linter/src/rules/pyflakes/rules/unused_variable.rs b/crates/ruff_linter/src/rules/pyflakes/rules/unused_variable.rs index 59dcbf5c22..d39f968271 100644 --- a/crates/ruff_linter/src/rules/pyflakes/rules/unused_variable.rs +++ b/crates/ruff_linter/src/rules/pyflakes/rules/unused_variable.rs @@ -4,7 +4,7 @@ use ruff_macros::{ViolationMetadata, derive_message_formats}; use ruff_python_ast::helpers::contains_effect; use ruff_python_ast::token::parenthesized_range; use ruff_python_ast::token::{TokenKind, Tokens}; -use ruff_python_ast::{self as ast, Stmt}; +use ruff_python_ast::Stmt; use ruff_python_semantic::Binding; use ruff_text_size::{Ranged, TextRange, TextSize}; @@ -161,14 +161,14 @@ fn remove_unused_variable(binding: &Binding, checker: &Checker) -> Option { let isolation = Checker::isolation(checker.semantic().parent_statement_id(node_id)); // First case: simple assignment (`x = 1`) - if let Stmt::Assign(ast::StmtAssign { targets, value, .. }) = statement { - if let Some(target) = targets + if let Stmt::Assign(node) = statement { + if let Some(target) = node.targets .iter() .find(|target| binding.range() == target.range()) { if target.is_name_expr() { - return if targets.len() > 1 - || contains_effect(value, |id| checker.semantic().has_builtin_binding(id)) + return if node.targets.len() > 1 + || contains_effect(&node.value, |id| checker.semantic().has_builtin_binding(id)) { // If the expression is complex (`x = foo()`), remove the assignment, // but preserve the right-hand side. @@ -200,35 +200,32 @@ fn remove_unused_variable(binding: &Binding, checker: &Checker) -> Option { } // Second case: simple annotated assignment (`x: int = 1`) - if let Stmt::AnnAssign(ast::StmtAnnAssign { - target, - value: Some(value), - .. - }) = statement - { - if target.is_name_expr() { - return if contains_effect(value, |id| checker.semantic().has_builtin_binding(id)) { - // If the expression is complex (`x = foo()`), remove the assignment, - // but preserve the right-hand side. - let start = statement.start(); - let end = - match_token_after(checker.tokens(), start, |token| token == TokenKind::Equal)? - .start(); - let edit = Edit::deletion(start, end); - Some(Fix::unsafe_edit(edit)) - } else { - // If (e.g.) assigning to a constant (`x = 1`), delete the entire statement. - let edit = delete_stmt(statement, parent, checker.locator(), checker.indexer()); - Some(Fix::unsafe_edit(edit).isolate(isolation)) - }; + if let Stmt::AnnAssign(node) = statement { + if let Some(value) = &node.value { + if node.target.is_name_expr() { + return if contains_effect(value, |id| checker.semantic().has_builtin_binding(id)) { + // If the expression is complex (`x = foo()`), remove the assignment, + // but preserve the right-hand side. + let start = statement.start(); + let end = + match_token_after(checker.tokens(), start, |token| token == TokenKind::Equal)? + .start(); + let edit = Edit::deletion(start, end); + Some(Fix::unsafe_edit(edit)) + } else { + // If (e.g.) assigning to a constant (`x = 1`), delete the entire statement. + let edit = delete_stmt(statement, parent, checker.locator(), checker.indexer()); + Some(Fix::unsafe_edit(edit).isolate(isolation)) + }; + } } } // Third case: with_item (`with foo() as x:`) - if let Stmt::With(ast::StmtWith { items, .. }) = statement { + if let Stmt::With(node) = statement { // Find the binding that matches the given `Range`. // TODO(charlie): Store the `WithItem` in the `Binding`. - for item in items { + for item in &node.items { if let Some(optional_vars) = &item.optional_vars { if optional_vars.range() == binding.range() { // Find the first token before the `as` keyword. diff --git a/crates/ruff_linter/src/rules/pylint/helpers.rs b/crates/ruff_linter/src/rules/pylint/helpers.rs index 056d27b602..2fb35e4e5f 100644 --- a/crates/ruff_linter/src/rules/pylint/helpers.rs +++ b/crates/ruff_linter/src/rules/pylint/helpers.rs @@ -119,22 +119,22 @@ impl Visitor<'_> for SequenceIndexVisitor<'_> { return; } match stmt { - Stmt::Assign(ast::StmtAssign { targets, value, .. }) => { - self.modified = targets.iter().any(|target| self.is_assignment(target)); - self.visit_expr(value); + Stmt::Assign(node) => { + self.modified = node.targets.iter().any(|target| self.is_assignment(target)); + self.visit_expr(&node.value); } - Stmt::AnnAssign(ast::StmtAnnAssign { target, value, .. }) => { - if let Some(value) = value { - self.modified = self.is_assignment(target); + Stmt::AnnAssign(node) => { + if let Some(value) = &node.value { + self.modified = self.is_assignment(&node.target); self.visit_expr(value); } } - Stmt::AugAssign(ast::StmtAugAssign { target, value, .. }) => { - self.modified = self.is_assignment(target); - self.visit_expr(value); + Stmt::AugAssign(node) => { + self.modified = self.is_assignment(&node.target); + self.visit_expr(&node.value); } - Stmt::Delete(ast::StmtDelete { targets, .. }) => { - self.modified = targets.iter().any(|target| self.is_assignment(target)); + Stmt::Delete(node) => { + self.modified = node.targets.iter().any(|target| self.is_assignment(target)); } _ => visitor::walk_stmt(self, stmt), } diff --git a/crates/ruff_linter/src/rules/pylint/rules/collapsible_else_if.rs b/crates/ruff_linter/src/rules/pylint/rules/collapsible_else_if.rs index 1e68c246fb..068106f636 100644 --- a/crates/ruff_linter/src/rules/pylint/rules/collapsible_else_if.rs +++ b/crates/ruff_linter/src/rules/pylint/rules/collapsible_else_if.rs @@ -64,12 +64,10 @@ impl Violation for CollapsibleElseIf { /// PLR5501 pub(crate) fn collapsible_else_if(checker: &Checker, stmt: &Stmt) { - let Stmt::If(ast::StmtIf { - elif_else_clauses, .. - }) = stmt - else { + let Stmt::If(if_stmt) = stmt else { return; }; + let elif_else_clauses = &if_stmt.elif_else_clauses; let Some( else_clause @ ElifElseClause { @@ -79,7 +77,10 @@ pub(crate) fn collapsible_else_if(checker: &Checker, stmt: &Stmt) { else { return; }; - let [first @ Stmt::If(ast::StmtIf { .. })] = body.as_slice() else { + let [first] = body.as_slice() else { + return; + }; + let Stmt::If(_) = first else { return; }; diff --git a/crates/ruff_linter/src/rules/pylint/rules/continue_in_finally.rs b/crates/ruff_linter/src/rules/pylint/rules/continue_in_finally.rs index 476dae54ed..e52733134b 100644 --- a/crates/ruff_linter/src/rules/pylint/rules/continue_in_finally.rs +++ b/crates/ruff_linter/src/rules/pylint/rules/continue_in_finally.rs @@ -1,4 +1,4 @@ -use ruff_python_ast::{self as ast, Stmt}; +use ruff_python_ast::Stmt; use ruff_macros::{ViolationMetadata, derive_message_formats}; use ruff_text_size::Ranged; @@ -54,28 +54,27 @@ fn traverse_body(checker: &Checker, body: &[Stmt]) { } match stmt { - Stmt::If(ast::StmtIf { - body, - elif_else_clauses, - .. - }) => { - traverse_body(checker, body); - for clause in elif_else_clauses { + Stmt::If(if_stmt) => { + traverse_body(checker, &if_stmt.body); + for clause in &if_stmt.elif_else_clauses { traverse_body(checker, &clause.body); } } - Stmt::Try(ast::StmtTry { body, orelse, .. }) => { - traverse_body(checker, body); - traverse_body(checker, orelse); + Stmt::Try(try_stmt) => { + traverse_body(checker, &try_stmt.body); + traverse_body(checker, &try_stmt.orelse); } - Stmt::For(ast::StmtFor { orelse, .. }) | Stmt::While(ast::StmtWhile { orelse, .. }) => { - traverse_body(checker, orelse); + Stmt::For(for_stmt) => { + traverse_body(checker, &for_stmt.orelse); } - Stmt::With(ast::StmtWith { body, .. }) => { - traverse_body(checker, body); + Stmt::While(while_stmt) => { + traverse_body(checker, &while_stmt.orelse); } - Stmt::Match(ast::StmtMatch { cases, .. }) => { - for case in cases { + Stmt::With(with_stmt) => { + traverse_body(checker, &with_stmt.body); + } + Stmt::Match(match_stmt) => { + for case in &match_stmt.cases { traverse_body(checker, &case.body); } } diff --git a/crates/ruff_linter/src/rules/pylint/rules/dict_iter_missing_items.rs b/crates/ruff_linter/src/rules/pylint/rules/dict_iter_missing_items.rs index 0becdaee36..a41702f3bd 100644 --- a/crates/ruff_linter/src/rules/pylint/rules/dict_iter_missing_items.rs +++ b/crates/ruff_linter/src/rules/pylint/rules/dict_iter_missing_items.rs @@ -1,4 +1,4 @@ -use ruff_python_ast::{self as ast, Expr, Stmt}; +use ruff_python_ast::{Expr, Stmt}; use ruff_macros::{ViolationMetadata, derive_message_formats}; use ruff_python_semantic::analyze::typing::is_dict; @@ -109,12 +109,14 @@ fn is_dict_key_tuple_with_two_elements(binding: &Binding, semantic: &SemanticMod }; let (value, annotation) = match statement { - Stmt::Assign(assign_stmt) => (assign_stmt.value.as_ref(), None), - Stmt::AnnAssign(ast::StmtAnnAssign { - value: Some(value), - annotation, - .. - }) => (value.as_ref(), Some(annotation.as_ref())), + Stmt::Assign(node) => (node.value.as_ref(), None), + Stmt::AnnAssign(node) => { + if let Some(value) = &node.value { + (value.as_ref(), Some(node.annotation.as_ref())) + } else { + return false; + } + } _ => return false, }; diff --git a/crates/ruff_linter/src/rules/pylint/rules/if_stmt_min_max.rs b/crates/ruff_linter/src/rules/pylint/rules/if_stmt_min_max.rs index b2e3f83467..f461449503 100644 --- a/crates/ruff_linter/src/rules/pylint/rules/if_stmt_min_max.rs +++ b/crates/ruff_linter/src/rules/pylint/rules/if_stmt_min_max.rs @@ -95,16 +95,11 @@ pub(crate) fn if_stmt_min_max(checker: &Checker, stmt_if: &ast::StmtIf) { return; } - let [ - body @ Stmt::Assign(ast::StmtAssign { - targets: body_targets, - value: body_value, - .. - }), - ] = body.as_slice() - else { + let [body @ Stmt::Assign(node)] = body.as_slice() else { return; }; + let body_targets = &node.targets; + let body_value = &node.value; let [body_target] = body_targets.as_slice() else { return; }; diff --git a/crates/ruff_linter/src/rules/pylint/rules/no_method_decorator.rs b/crates/ruff_linter/src/rules/pylint/rules/no_method_decorator.rs index 334cd47bc8..8977274383 100644 --- a/crates/ruff_linter/src/rules/pylint/rules/no_method_decorator.rs +++ b/crates/ruff_linter/src/rules/pylint/rules/no_method_decorator.rs @@ -113,7 +113,9 @@ fn get_undecorated_methods(checker: &Checker, class_stmt: &Stmt, method_type: &M // gather all explicit *method calls for stmt in &class_def.body { - if let Stmt::Assign(ast::StmtAssign { targets, value, .. }) = stmt { + if let Stmt::Assign(node) = stmt { + let targets = &node.targets; + let value = &node.value; if let Expr::Call(ast::ExprCall { func, arguments, .. }) = value.as_ref() @@ -149,12 +151,10 @@ fn get_undecorated_methods(checker: &Checker, class_stmt: &Stmt, method_type: &M } for stmt in &class_def.body { - if let Stmt::FunctionDef(ast::StmtFunctionDef { - name, - decorator_list, - .. - }) = stmt - { + if let Stmt::FunctionDef(node) = stmt { + let name = &node.name; + let decorator_list = &node.decorator_list; + let Some(decorator_call_statement) = explicit_decorator_calls.get(name.id()) else { continue; }; diff --git a/crates/ruff_linter/src/rules/pylint/rules/non_slot_assignment.rs b/crates/ruff_linter/src/rules/pylint/rules/non_slot_assignment.rs index 42964f1efa..6cb995691a 100644 --- a/crates/ruff_linter/src/rules/pylint/rules/non_slot_assignment.rs +++ b/crates/ruff_linter/src/rules/pylint/rules/non_slot_assignment.rs @@ -107,13 +107,13 @@ fn is_attributes_not_in_slots(body: &[Stmt]) -> Vec> { for statement in body { match statement { // Ex) `__slots__ = ("name",)` - Stmt::Assign(ast::StmtAssign { targets, value, .. }) => { - let [Expr::Name(ast::ExprName { id, .. })] = targets.as_slice() else { + Stmt::Assign(node) => { + let [Expr::Name(ast::ExprName { id, .. })] = node.targets.as_slice() else { continue; }; if id == "__slots__" { - for attribute in slots_attributes(value) { + for attribute in slots_attributes(&node.value) { if let Some(attribute) = attribute { slots.insert(attribute); } else { @@ -124,12 +124,12 @@ fn is_attributes_not_in_slots(body: &[Stmt]) -> Vec> { } // Ex) `__slots__: Tuple[str, ...] = ("name",)` - Stmt::AnnAssign(ast::StmtAnnAssign { - target, - value: Some(value), - .. - }) => { - let Expr::Name(ast::ExprName { id, .. }) = target.as_ref() else { + Stmt::AnnAssign(node) => { + let Some(value) = &node.value else { + continue; + }; + + let Expr::Name(ast::ExprName { id, .. }) = node.target.as_ref() else { continue; }; @@ -145,13 +145,13 @@ fn is_attributes_not_in_slots(body: &[Stmt]) -> Vec> { } // Ex) `__slots__ += ("name",)` - Stmt::AugAssign(ast::StmtAugAssign { target, value, .. }) => { - let Expr::Name(ast::ExprName { id, .. }) = target.as_ref() else { + Stmt::AugAssign(node) => { + let Expr::Name(ast::ExprName { id, .. }) = node.target.as_ref() else { continue; }; if id == "__slots__" { - for attribute in slots_attributes(value) { + for attribute in slots_attributes(&node.value) { if let Some(attribute) = attribute { slots.insert(attribute); } else { @@ -170,11 +170,11 @@ fn is_attributes_not_in_slots(body: &[Stmt]) -> Vec> { // And, collect all the property name with setter. for statement in body { - let Stmt::FunctionDef(ast::StmtFunctionDef { decorator_list, .. }) = statement else { + let Stmt::FunctionDef(node) = statement else { continue; }; - for decorator in decorator_list { + for decorator in &node.decorator_list { let Some(ast::ExprAttribute { value, attr, .. }) = decorator.expression.as_attribute_expr() else { @@ -193,16 +193,16 @@ fn is_attributes_not_in_slots(body: &[Stmt]) -> Vec> { // Second, find any assignments that aren't included in `__slots__`. let mut assignments = vec![]; for statement in body { - let Stmt::FunctionDef(ast::StmtFunctionDef { name, body, .. }) = statement else { + let Stmt::FunctionDef(node) = statement else { continue; }; - if name == "__init__" { - for statement in body { + if node.name.as_str() == "__init__" { + for statement in &node.body { match statement { // Ex) `self.name = name` - Stmt::Assign(ast::StmtAssign { targets, .. }) => { - let [Expr::Attribute(attribute)] = targets.as_slice() else { + Stmt::Assign(assign_node) => { + let [Expr::Attribute(attribute)] = assign_node.targets.as_slice() else { continue; }; let Expr::Name(ast::ExprName { id, .. }) = attribute.value.as_ref() else { @@ -217,8 +217,8 @@ fn is_attributes_not_in_slots(body: &[Stmt]) -> Vec> { } // Ex) `self.name: str = name` - Stmt::AnnAssign(ast::StmtAnnAssign { target, .. }) => { - let Expr::Attribute(attribute) = target.as_ref() else { + Stmt::AnnAssign(ann_node) => { + let Expr::Attribute(attribute) = ann_node.target.as_ref() else { continue; }; let Expr::Name(ast::ExprName { id, .. }) = attribute.value.as_ref() else { @@ -233,8 +233,8 @@ fn is_attributes_not_in_slots(body: &[Stmt]) -> Vec> { } // Ex) `self.name += name` - Stmt::AugAssign(ast::StmtAugAssign { target, .. }) => { - let Expr::Attribute(attribute) = target.as_ref() else { + Stmt::AugAssign(aug_node) => { + let Expr::Attribute(attribute) = aug_node.target.as_ref() else { continue; }; let Expr::Name(ast::ExprName { id, .. }) = attribute.value.as_ref() else { diff --git a/crates/ruff_linter/src/rules/pylint/rules/redefined_loop_name.rs b/crates/ruff_linter/src/rules/pylint/rules/redefined_loop_name.rs index 3abd675316..0caff88d7b 100644 --- a/crates/ruff_linter/src/rules/pylint/rules/redefined_loop_name.rs +++ b/crates/ruff_linter/src/rules/pylint/rules/redefined_loop_name.rs @@ -149,9 +149,9 @@ impl<'b> StatementVisitor<'b> for InnerForWithAssignTargetsVisitor<'_, 'b> { fn visit_stmt(&mut self, stmt: &'b Stmt) { // Collect target expressions. match stmt { - Stmt::For(ast::StmtFor { target, .. }) => { + Stmt::For(node) => { self.assignment_targets.extend( - assignment_targets_from_expr(target, self.dummy_variable_rgx).map(|expr| { + assignment_targets_from_expr(&node.target, self.dummy_variable_rgx).map(|expr| { ExprWithInnerBindingKind { expr, binding_kind: InnerBindingKind::For, @@ -159,9 +159,9 @@ impl<'b> StatementVisitor<'b> for InnerForWithAssignTargetsVisitor<'_, 'b> { }), ); } - Stmt::With(ast::StmtWith { items, .. }) => { + Stmt::With(node) => { self.assignment_targets.extend( - assignment_targets_from_with_items(items, self.dummy_variable_rgx).map( + assignment_targets_from_with_items(&node.items, self.dummy_variable_rgx).map( |expr| ExprWithInnerBindingKind { expr, binding_kind: InnerBindingKind::With, @@ -169,17 +169,18 @@ impl<'b> StatementVisitor<'b> for InnerForWithAssignTargetsVisitor<'_, 'b> { ), ); } - Stmt::Assign(ast::StmtAssign { targets, value, .. }) => { + Stmt::Assign(node) => { // Check for single-target assignments which are of the // form `x = cast(..., x)`. - if targets + if node + .targets .first() - .is_some_and(|target| assignment_is_cast_expr(value, target, self.context)) + .is_some_and(|target| assignment_is_cast_expr(&node.value, target, self.context)) { return; } self.assignment_targets.extend( - assignment_targets_from_assign_targets(targets, self.dummy_variable_rgx).map( + assignment_targets_from_assign_targets(&node.targets, self.dummy_variable_rgx).map( |expr| ExprWithInnerBindingKind { expr, binding_kind: InnerBindingKind::Assignment, @@ -187,9 +188,9 @@ impl<'b> StatementVisitor<'b> for InnerForWithAssignTargetsVisitor<'_, 'b> { ), ); } - Stmt::AugAssign(ast::StmtAugAssign { target, .. }) => { + Stmt::AugAssign(node) => { self.assignment_targets.extend( - assignment_targets_from_expr(target, self.dummy_variable_rgx).map(|expr| { + assignment_targets_from_expr(&node.target, self.dummy_variable_rgx).map(|expr| { ExprWithInnerBindingKind { expr, binding_kind: InnerBindingKind::Assignment, @@ -197,12 +198,12 @@ impl<'b> StatementVisitor<'b> for InnerForWithAssignTargetsVisitor<'_, 'b> { }), ); } - Stmt::AnnAssign(ast::StmtAnnAssign { target, value, .. }) => { - if value.is_none() { + Stmt::AnnAssign(node) => { + if node.value.is_none() { return; } self.assignment_targets.extend( - assignment_targets_from_expr(target, self.dummy_variable_rgx).map(|expr| { + assignment_targets_from_expr(&node.target, self.dummy_variable_rgx).map(|expr| { ExprWithInnerBindingKind { expr, binding_kind: InnerBindingKind::Assignment, @@ -345,9 +346,9 @@ fn assignment_targets_from_assign_targets<'a>( /// PLW2901 pub(crate) fn redefined_loop_name(checker: &Checker, stmt: &Stmt) { let (outer_assignment_targets, inner_assignment_targets) = match stmt { - Stmt::With(ast::StmtWith { items, body, .. }) => { + Stmt::With(node) => { let outer_assignment_targets: Vec = - assignment_targets_from_with_items(items, &checker.settings().dummy_variable_rgx) + assignment_targets_from_with_items(&node.items, &checker.settings().dummy_variable_rgx) .map(|expr| ExprWithOuterBindingKind { expr, binding_kind: OuterBindingKind::With, @@ -358,14 +359,14 @@ pub(crate) fn redefined_loop_name(checker: &Checker, stmt: &Stmt) { dummy_variable_rgx: &checker.settings().dummy_variable_rgx, assignment_targets: vec![], }; - for stmt in body { + for stmt in &node.body { visitor.visit_stmt(stmt); } (outer_assignment_targets, visitor.assignment_targets) } - Stmt::For(ast::StmtFor { target, body, .. }) => { + Stmt::For(node) => { let outer_assignment_targets: Vec = - assignment_targets_from_expr(target, &checker.settings().dummy_variable_rgx) + assignment_targets_from_expr(&node.target, &checker.settings().dummy_variable_rgx) .map(|expr| ExprWithOuterBindingKind { expr, binding_kind: OuterBindingKind::For, @@ -376,7 +377,7 @@ pub(crate) fn redefined_loop_name(checker: &Checker, stmt: &Stmt) { dummy_variable_rgx: &checker.settings().dummy_variable_rgx, assignment_targets: vec![], }; - for stmt in body { + for stmt in &node.body { visitor.visit_stmt(stmt); } (outer_assignment_targets, visitor.assignment_targets) diff --git a/crates/ruff_linter/src/rules/pylint/rules/redefined_slots_in_subclass.rs b/crates/ruff_linter/src/rules/pylint/rules/redefined_slots_in_subclass.rs index 21ccdc83da..d70f462db9 100644 --- a/crates/ruff_linter/src/rules/pylint/rules/redefined_slots_in_subclass.rs +++ b/crates/ruff_linter/src/rules/pylint/rules/redefined_slots_in_subclass.rs @@ -121,23 +121,23 @@ fn slots_members(body: &[Stmt]) -> FxHashSet> { for stmt in body { match stmt { // Ex) `__slots__ = ("name",)` - Stmt::Assign(ast::StmtAssign { targets, value, .. }) => { - let [Expr::Name(ast::ExprName { id, .. })] = targets.as_slice() else { + Stmt::Assign(node) => { + let [Expr::Name(ast::ExprName { id, .. })] = node.targets.as_slice() else { continue; }; if id == "__slots__" { - members.extend(slots_attributes(value)); + members.extend(slots_attributes(&node.value)); } } // Ex) `__slots__: Tuple[str, ...] = ("name",)` - Stmt::AnnAssign(ast::StmtAnnAssign { - target, - value: Some(value), - .. - }) => { - let Expr::Name(ast::ExprName { id, .. }) = target.as_ref() else { + Stmt::AnnAssign(node) => { + let Some(value) = &node.value else { + continue; + }; + + let Expr::Name(ast::ExprName { id, .. }) = node.target.as_ref() else { continue; }; @@ -147,13 +147,13 @@ fn slots_members(body: &[Stmt]) -> FxHashSet> { } // Ex) `__slots__ += ("name",)` - Stmt::AugAssign(ast::StmtAugAssign { target, value, .. }) => { - let Expr::Name(ast::ExprName { id, .. }) = target.as_ref() else { + Stmt::AugAssign(node) => { + let Expr::Name(ast::ExprName { id, .. }) = node.target.as_ref() else { continue; }; if id == "__slots__" { - members.extend(slots_attributes(value)); + members.extend(slots_attributes(&node.value)); } } _ => {} diff --git a/crates/ruff_linter/src/rules/pylint/rules/single_string_slots.rs b/crates/ruff_linter/src/rules/pylint/rules/single_string_slots.rs index 0c6f94a483..9c6d6fc1a3 100644 --- a/crates/ruff_linter/src/rules/pylint/rules/single_string_slots.rs +++ b/crates/ruff_linter/src/rules/pylint/rules/single_string_slots.rs @@ -62,9 +62,20 @@ impl Violation for SingleStringSlots { pub(crate) fn single_string_slots(checker: &Checker, class: &StmtClassDef) { for stmt in &class.body { match stmt { - Stmt::Assign(ast::StmtAssign { targets, value, .. }) => { - for target in targets { + Stmt::Assign(assign) => { + for target in &assign.targets { if let Expr::Name(ast::ExprName { id, .. }) = target { + if id.as_str() == "__slots__" { + if matches!(assign.value.as_ref(), Expr::StringLiteral(_) | Expr::FString(_)) { + checker.report_diagnostic(SingleStringSlots, stmt.identifier()); + } + } + } + } + } + Stmt::AnnAssign(ann_assign) => { + if let Some(value) = &ann_assign.value { + if let Expr::Name(ast::ExprName { id, .. }) = ann_assign.target.as_ref() { if id.as_str() == "__slots__" { if matches!(value.as_ref(), Expr::StringLiteral(_) | Expr::FString(_)) { checker.report_diagnostic(SingleStringSlots, stmt.identifier()); @@ -73,19 +84,6 @@ pub(crate) fn single_string_slots(checker: &Checker, class: &StmtClassDef) { } } } - Stmt::AnnAssign(ast::StmtAnnAssign { - target, - value: Some(value), - .. - }) => { - if let Expr::Name(ast::ExprName { id, .. }) = target.as_ref() { - if id.as_str() == "__slots__" { - if matches!(value.as_ref(), Expr::StringLiteral(_) | Expr::FString(_)) { - checker.report_diagnostic(SingleStringSlots, stmt.identifier()); - } - } - } - } _ => {} } } diff --git a/crates/ruff_linter/src/rules/pylint/rules/stop_iteration_return.rs b/crates/ruff_linter/src/rules/pylint/rules/stop_iteration_return.rs index 6ae47fe66b..d9a52438ef 100644 --- a/crates/ruff_linter/src/rules/pylint/rules/stop_iteration_return.rs +++ b/crates/ruff_linter/src/rules/pylint/rules/stop_iteration_return.rs @@ -79,13 +79,15 @@ impl<'a> Visitor<'a> for GeneratorAnalyzer<'a, '_> { fn visit_stmt(&mut self, stmt: &'a ast::Stmt) { match stmt { ast::Stmt::FunctionDef(_) => {} - ast::Stmt::Raise(raise @ ast::StmtRaise { exc: Some(exc), .. }) => { - if self - .checker - .semantic() - .match_builtin_expr(map_callable(exc), "StopIteration") - { - self.stop_iteration_raises.push(raise); + ast::Stmt::Raise(raise) => { + if let Some(exc) = &raise.exc { + if self + .checker + .semantic() + .match_builtin_expr(map_callable(exc), "StopIteration") + { + self.stop_iteration_raises.push(raise); + } } walk_stmt(self, stmt); } diff --git a/crates/ruff_linter/src/rules/pylint/rules/too_many_branches.rs b/crates/ruff_linter/src/rules/pylint/rules/too_many_branches.rs index fd9d90cd38..8032fea8f2 100644 --- a/crates/ruff_linter/src/rules/pylint/rules/too_many_branches.rs +++ b/crates/ruff_linter/src/rules/pylint/rules/too_many_branches.rs @@ -166,57 +166,54 @@ fn num_branches(stmts: &[Stmt]) -> usize { stmts .iter() .map(|stmt| match stmt { - Stmt::If(ast::StmtIf { - body, - elif_else_clauses, - .. - }) => { - 1 + num_branches(body) - + elif_else_clauses.len() - + elif_else_clauses + Stmt::If(node) => { + 1 + num_branches(&node.body) + + node.elif_else_clauses.len() + + node.elif_else_clauses .iter() .map(|clause| num_branches(&clause.body)) .sum::() } - Stmt::Match(ast::StmtMatch { cases, .. }) => { - cases.len() - + cases + Stmt::Match(node) => { + node.cases.len() + + node.cases .iter() .map(|case| num_branches(&case.body)) .sum::() } // The `with` statement is not considered a branch but the statements inside the `with` should be counted. - Stmt::With(ast::StmtWith { body, .. }) => num_branches(body), - Stmt::For(ast::StmtFor { body, orelse, .. }) - | Stmt::While(ast::StmtWhile { body, orelse, .. }) => { - 1 + num_branches(body) - + (if orelse.is_empty() { + Stmt::With(node) => num_branches(&node.body), + Stmt::For(node) => { + 1 + num_branches(&node.body) + + (if node.orelse.is_empty() { 0 } else { - 1 + num_branches(orelse) + 1 + num_branches(&node.orelse) }) } - Stmt::Try(ast::StmtTry { - body, - handlers, - orelse, - finalbody, - .. - }) => { + Stmt::While(node) => { + 1 + num_branches(&node.body) + + (if node.orelse.is_empty() { + 0 + } else { + 1 + num_branches(&node.orelse) + }) + } + Stmt::Try(node) => { // Count each `except` clause as a branch; the `else` and `finally` clauses also // count, but the `try` clause itself does not. - num_branches(body) - + (if orelse.is_empty() { + num_branches(&node.body) + + (if node.orelse.is_empty() { 0 } else { - 1 + num_branches(orelse) + 1 + num_branches(&node.orelse) }) - + (if finalbody.is_empty() { + + (if node.finalbody.is_empty() { 0 } else { - 1 + num_branches(finalbody) + 1 + num_branches(&node.finalbody) }) - + handlers + + node.handlers .iter() .map(|handler| { 1 + { diff --git a/crates/ruff_linter/src/rules/pylint/rules/too_many_nested_blocks.rs b/crates/ruff_linter/src/rules/pylint/rules/too_many_nested_blocks.rs index b07828832d..1ac64d8ba2 100644 --- a/crates/ruff_linter/src/rules/pylint/rules/too_many_nested_blocks.rs +++ b/crates/ruff_linter/src/rules/pylint/rules/too_many_nested_blocks.rs @@ -95,39 +95,29 @@ fn is_nested_block(stmt: &Stmt) -> bool { /// Returns `true` if the given statement is a leaf node. fn has_nested_block(stmt: &Stmt) -> bool { match stmt { - Stmt::If(ast::StmtIf { - body, - elif_else_clauses, - .. - }) => { - body.iter().any(is_nested_block) - || elif_else_clauses + Stmt::If(node) => { + node.body.iter().any(is_nested_block) + || node.elif_else_clauses .iter() .any(|elif_else| elif_else.body.iter().any(is_nested_block)) } - Stmt::While(ast::StmtWhile { body, orelse, .. }) => { - body.iter().any(is_nested_block) || orelse.iter().any(is_nested_block) + Stmt::While(node) => { + node.body.iter().any(is_nested_block) || node.orelse.iter().any(is_nested_block) } - Stmt::For(ast::StmtFor { body, orelse, .. }) => { - body.iter().any(is_nested_block) || orelse.iter().any(is_nested_block) + Stmt::For(node) => { + node.body.iter().any(is_nested_block) || node.orelse.iter().any(is_nested_block) } - Stmt::Try(ast::StmtTry { - body, - handlers, - orelse, - finalbody, - .. - }) => { - body.iter().any(is_nested_block) - || handlers.iter().any(|handler| match handler { + Stmt::Try(node) => { + node.body.iter().any(is_nested_block) + || node.handlers.iter().any(|handler| match handler { ExceptHandler::ExceptHandler(ast::ExceptHandlerExceptHandler { body, .. }) => body.iter().any(is_nested_block), }) - || orelse.iter().any(is_nested_block) - || finalbody.iter().any(is_nested_block) + || node.orelse.iter().any(is_nested_block) + || node.finalbody.iter().any(is_nested_block) } - Stmt::With(ast::StmtWith { body, .. }) => body.iter().any(is_nested_block), + Stmt::With(node) => node.body.iter().any(is_nested_block), _ => false, } } diff --git a/crates/ruff_linter/src/rules/pylint/rules/too_many_statements.rs b/crates/ruff_linter/src/rules/pylint/rules/too_many_statements.rs index 7be3f6ac0c..6f2daaf684 100644 --- a/crates/ruff_linter/src/rules/pylint/rules/too_many_statements.rs +++ b/crates/ruff_linter/src/rules/pylint/rules/too_many_statements.rs @@ -69,54 +69,44 @@ fn num_statements(stmts: &[Stmt]) -> usize { let mut count = 0; for stmt in stmts { match stmt { - Stmt::If(ast::StmtIf { - body, - elif_else_clauses, - .. - }) => { + Stmt::If(node) => { count += 1; - count += num_statements(body); - for clause in elif_else_clauses { + count += num_statements(&node.body); + for clause in &node.elif_else_clauses { count += 1; count += num_statements(&clause.body); } } - Stmt::For(ast::StmtFor { body, orelse, .. }) => { - count += num_statements(body); - count += num_statements(orelse); + Stmt::For(node) => { + count += num_statements(&node.body); + count += num_statements(&node.orelse); } - Stmt::While(ast::StmtWhile { body, orelse, .. }) => { + Stmt::While(node) => { count += 1; - count += num_statements(body); - count += num_statements(orelse); + count += num_statements(&node.body); + count += num_statements(&node.orelse); } - Stmt::Match(ast::StmtMatch { cases, .. }) => { + Stmt::Match(node) => { count += 1; - for case in cases { + for case in &node.cases { count += 1; count += num_statements(&case.body); } } - Stmt::Try(ast::StmtTry { - body, - handlers, - orelse, - finalbody, - .. - }) => { + Stmt::Try(node) => { count += 1; - count += num_statements(body); - if !orelse.is_empty() { - count += 1 + num_statements(orelse); + count += num_statements(&node.body); + if !node.orelse.is_empty() { + count += 1 + num_statements(&node.orelse); } - if !finalbody.is_empty() { + if !node.finalbody.is_empty() { // Unclear why, but follow Pylint's convention. - count += 2 + num_statements(finalbody); + count += 2 + num_statements(&node.finalbody); } - if handlers.len() > 1 { + if node.handlers.len() > 1 { count += 1; } - for handler in handlers { + for handler in &node.handlers { count += 1; let ExceptHandler::ExceptHandler(ast::ExceptHandlerExceptHandler { body, .. @@ -124,10 +114,13 @@ fn num_statements(stmts: &[Stmt]) -> usize { count += num_statements(body); } } - Stmt::FunctionDef(ast::StmtFunctionDef { body, .. }) - | Stmt::With(ast::StmtWith { body, .. }) => { + Stmt::FunctionDef(node) => { count += 1; - count += num_statements(body); + count += num_statements(&node.body); + } + Stmt::With(node) => { + count += 1; + count += num_statements(&node.body); } Stmt::Return(_) => {} _ => { diff --git a/crates/ruff_linter/src/rules/pylint/rules/useless_else_on_loop.rs b/crates/ruff_linter/src/rules/pylint/rules/useless_else_on_loop.rs index e1a1a20505..15c5516584 100644 --- a/crates/ruff_linter/src/rules/pylint/rules/useless_else_on_loop.rs +++ b/crates/ruff_linter/src/rules/pylint/rules/useless_else_on_loop.rs @@ -87,39 +87,28 @@ pub(crate) fn useless_else_on_loop(checker: &Checker, stmt: &Stmt, body: &[Stmt] /// Returns `true` if the given body contains a `break` statement. fn loop_exits_early(body: &[Stmt]) -> bool { body.iter().any(|stmt| match stmt { - Stmt::If(ast::StmtIf { - body, - elif_else_clauses, - .. - }) => { - loop_exits_early(body) - || elif_else_clauses + Stmt::If(node) => { + loop_exits_early(&node.body) + || node.elif_else_clauses .iter() .any(|clause| loop_exits_early(&clause.body)) } - Stmt::With(ast::StmtWith { body, .. }) => loop_exits_early(body), - Stmt::Match(ast::StmtMatch { cases, .. }) => cases + Stmt::With(node) => loop_exits_early(&node.body), + Stmt::Match(node) => node.cases .iter() .any(|MatchCase { body, .. }| loop_exits_early(body)), - Stmt::Try(ast::StmtTry { - body, - handlers, - orelse, - finalbody, - .. - }) => { - loop_exits_early(body) - || loop_exits_early(orelse) - || loop_exits_early(finalbody) - || handlers.iter().any(|handler| match handler { + Stmt::Try(node) => { + loop_exits_early(&node.body) + || loop_exits_early(&node.orelse) + || loop_exits_early(&node.finalbody) + || node.handlers.iter().any(|handler| match handler { ExceptHandler::ExceptHandler(ast::ExceptHandlerExceptHandler { body, .. }) => loop_exits_early(body), }) } - Stmt::For(ast::StmtFor { orelse, .. }) | Stmt::While(ast::StmtWhile { orelse, .. }) => { - loop_exits_early(orelse) - } + Stmt::For(node) => loop_exits_early(&node.orelse), + Stmt::While(node) => loop_exits_early(&node.orelse), Stmt::Break(_) => true, _ => false, }) diff --git a/crates/ruff_linter/src/rules/pyupgrade/rules/deprecated_c_element_tree.rs b/crates/ruff_linter/src/rules/pyupgrade/rules/deprecated_c_element_tree.rs index 1ce34e2f15..282493127a 100644 --- a/crates/ruff_linter/src/rules/pyupgrade/rules/deprecated_c_element_tree.rs +++ b/crates/ruff_linter/src/rules/pyupgrade/rules/deprecated_c_element_tree.rs @@ -55,11 +55,12 @@ where /// UP023 pub(crate) fn deprecated_c_element_tree(checker: &Checker, stmt: &Stmt) { match stmt { - Stmt::Import(ast::StmtImport { - names, - range: _, - node_index: _, - }) => { + Stmt::Import(import_stmt) => { + let ast::StmtImport { + names, + range: _, + node_index: _, + } = &**import_stmt; // Ex) `import xml.etree.cElementTree as ET` for name in names { if &name.name == "xml.etree.cElementTree" && name.asname.is_some() { @@ -67,13 +68,15 @@ pub(crate) fn deprecated_c_element_tree(checker: &Checker, stmt: &Stmt) { } } } - Stmt::ImportFrom(ast::StmtImportFrom { - module, - names, - level, - range: _, - node_index: _, - }) => { + Stmt::ImportFrom(import_from_stmt) => { + let ast::StmtImportFrom { + module, + names, + level, + range: _, + node_index: _, + } = &**import_from_stmt; + if *level > 0 { // Ex) `import .xml.etree.cElementTree as ET` } else if let Some(module) = module { diff --git a/crates/ruff_linter/src/rules/pyupgrade/rules/deprecated_mock_import.rs b/crates/ruff_linter/src/rules/pyupgrade/rules/deprecated_mock_import.rs index 5c13bbd07f..29f32f67af 100644 --- a/crates/ruff_linter/src/rules/pyupgrade/rules/deprecated_mock_import.rs +++ b/crates/ruff_linter/src/rules/pyupgrade/rules/deprecated_mock_import.rs @@ -277,11 +277,12 @@ pub(crate) fn deprecated_mock_attribute(checker: &Checker, attribute: &ast::Expr /// UP026 pub(crate) fn deprecated_mock_import(checker: &Checker, stmt: &Stmt) { match stmt { - Stmt::Import(ast::StmtImport { - names, - range: _, - node_index: _, - }) => { + Stmt::Import(node) => { + let ast::StmtImport { + names, + range: _, + node_index: _, + } = &**node; // Find all `mock` imports. if names .iter() @@ -326,12 +327,16 @@ pub(crate) fn deprecated_mock_import(checker: &Checker, stmt: &Stmt) { } } } - Stmt::ImportFrom(ast::StmtImportFrom { - module: Some(module), - level, - names, - .. - }) => { + Stmt::ImportFrom(node) => { + let ast::StmtImportFrom { + module: Some(module), + level, + names, + range: _, + node_index: _, + } = &**node else { + return; + }; if *level > 0 { return; } diff --git a/crates/ruff_linter/src/rules/pyupgrade/rules/super_call_with_parameters.rs b/crates/ruff_linter/src/rules/pyupgrade/rules/super_call_with_parameters.rs index cc7cbef8e0..9c6a8f7445 100644 --- a/crates/ruff_linter/src/rules/pyupgrade/rules/super_call_with_parameters.rs +++ b/crates/ruff_linter/src/rules/pyupgrade/rules/super_call_with_parameters.rs @@ -97,15 +97,13 @@ pub(crate) fn super_call_with_parameters(checker: &Checker, call: &ast::ExprCall }; // Find the enclosing function definition (if any). - let Some( - func_stmt @ Stmt::FunctionDef(ast::StmtFunctionDef { - parameters: parent_parameters, - .. - }), - ) = parents.find(|stmt| stmt.is_function_def_stmt()) - else { + let Some(func_stmt) = parents.find(|stmt| stmt.is_function_def_stmt()) else { return; }; + let Stmt::FunctionDef(func_def) = func_stmt else { + return; + }; + let parent_parameters = &func_def.parameters; if is_builtins_super(checker.semantic(), call) && !has_local_dunder_class_var_ref(checker.semantic(), func_stmt) @@ -119,14 +117,14 @@ pub(crate) fn super_call_with_parameters(checker: &Checker, call: &ast::ExprCall }; // Find the enclosing class definition (if any). - let Some(Stmt::ClassDef(ast::StmtClassDef { - name: parent_name, - decorator_list, - .. - })) = parents.find(|stmt| stmt.is_class_def_stmt()) - else { + let Some(class_stmt) = parents.find(|stmt| stmt.is_class_def_stmt()) else { return; }; + let Stmt::ClassDef(class_def) = class_stmt else { + return; + }; + let parent_name = &class_def.name; + let decorator_list = &class_def.decorator_list; let ( Expr::Name(ast::ExprName { @@ -252,13 +250,11 @@ impl ClassCellReferenceFinder { impl<'a> Visitor<'a> for ClassCellReferenceFinder { fn visit_stmt(&mut self, stmt: &'a Stmt) { - match stmt { - Stmt::ClassDef(_) => {} - _ => { - if !self.has_class_cell { - walk_stmt(self, stmt); - } - } + if stmt.is_class_def_stmt() { + return; + } + if !self.has_class_cell { + walk_stmt(self, stmt); } } diff --git a/crates/ruff_linter/src/rules/pyupgrade/rules/unnecessary_future_import.rs b/crates/ruff_linter/src/rules/pyupgrade/rules/unnecessary_future_import.rs index 8b5bd03bef..fdcdd49f11 100644 --- a/crates/ruff_linter/src/rules/pyupgrade/rules/unnecessary_future_import.rs +++ b/crates/ruff_linter/src/rules/pyupgrade/rules/unnecessary_future_import.rs @@ -140,7 +140,8 @@ pub(crate) fn unnecessary_future_import(checker: &Checker, scope: &Scope) { }; let stmt = checker.semantic().statement(node_id); - if let Stmt::ImportFrom(ast::StmtImportFrom { names, .. }) = stmt { + if let Stmt::ImportFrom(node) = stmt { + let names = &node.names; let Some(alias) = names .iter() .find(|alias| alias.name.as_str() == binding.name(checker.source())) diff --git a/crates/ruff_linter/src/rules/refurb/rules/read_whole_file.rs b/crates/ruff_linter/src/rules/refurb/rules/read_whole_file.rs index 2b43af89a8..6ff42e7856 100644 --- a/crates/ruff_linter/src/rules/refurb/rules/read_whole_file.rs +++ b/crates/ruff_linter/src/rules/refurb/rules/read_whole_file.rs @@ -203,8 +203,8 @@ fn generate_fix( // The assignment's RHS must also be the same as the `read` call in `expr`, otherwise this fix // would remove the rest of the expression. let replacement = match with_stmt.body.as_slice() { - [Stmt::Assign(ast::StmtAssign { targets, value, .. })] if value.range() == expr.range() => { - match targets.as_slice() { + [Stmt::Assign(assign_node)] if assign_node.value.range() == expr.range() => { + match assign_node.targets.as_slice() { [Expr::Name(name)] => { format!( "{name} = {binding}({filename_code}).{suggestion}", @@ -214,23 +214,18 @@ fn generate_fix( _ => return None, } } - [ - Stmt::AnnAssign(ast::StmtAnnAssign { - target, - annotation, - value: Some(value), - .. - }), - ] if value.range() == expr.range() => match target.as_ref() { - Expr::Name(name) => { - format!( - "{var}: {ann} = {binding}({filename_code}).{suggestion}", - var = name.id, - ann = locator.slice(annotation.range()) - ) + [Stmt::AnnAssign(ann_assign_node)] if ann_assign_node.value.as_ref().is_some_and(|v| v.range() == expr.range()) => { + match ann_assign_node.target.as_ref() { + Expr::Name(name) => { + format!( + "{var}: {ann} = {binding}({filename_code}).{suggestion}", + var = name.id, + ann = locator.slice(ann_assign_node.annotation.range()) + ) + } + _ => return None, } - _ => return None, - }, + } _ => return None, }; diff --git a/crates/ruff_linter/src/rules/refurb/rules/reimplemented_operator.rs b/crates/ruff_linter/src/rules/refurb/rules/reimplemented_operator.rs index 963a094498..18f9b74e4b 100644 --- a/crates/ruff_linter/src/rules/refurb/rules/reimplemented_operator.rs +++ b/crates/ruff_linter/src/rules/refurb/rules/reimplemented_operator.rs @@ -100,7 +100,7 @@ pub(crate) fn reimplemented_operator(checker: &Checker, target: &FunctionLike) { || checker .semantic() .current_statements() - .any(|stmt| matches!(stmt, Stmt::AnnAssign(_) | Stmt::Assign(_)))) + .any(|stmt| stmt.is_ann_assign_stmt() || stmt.is_assign_stmt())) { return; } @@ -179,7 +179,7 @@ impl FunctionLike<'_> { match self { Self::Lambda(expr) => Some(&expr.body), Self::Function(stmt) => match stmt.body.as_slice() { - [Stmt::Return(ast::StmtReturn { value, .. })] => value.as_deref(), + [Stmt::Return(return_node)] => return_node.value.as_deref(), _ => None, }, } diff --git a/crates/ruff_linter/src/rules/ruff/rules/assert_with_print_message.rs b/crates/ruff_linter/src/rules/ruff/rules/assert_with_print_message.rs index 186b09cb4a..fb8370a147 100644 --- a/crates/ruff_linter/src/rules/ruff/rules/assert_with_print_message.rs +++ b/crates/ruff_linter/src/rules/ruff/rules/assert_with_print_message.rs @@ -65,12 +65,12 @@ pub(crate) fn assert_with_print_message(checker: &Checker, stmt: &ast::StmtAsser // This is the confirmed rule condition let mut diagnostic = checker.report_diagnostic(AssertWithPrintMessage, call.range()); diagnostic.set_fix(Fix::unsafe_edit(Edit::range_replacement( - checker.generator().stmt(&Stmt::Assert(ast::StmtAssert { + checker.generator().stmt(&Stmt::Assert(Box::new(ast::StmtAssert { test: stmt.test.clone(), msg: print_arguments::to_expr(&call.arguments, checker).map(Box::new), range: TextRange::default(), node_index: ruff_python_ast::AtomicNodeIndex::NONE, - })), + }))), // We have to replace the entire statement, // as the `print` could be empty and thus `call.range()` // will cease to exist. diff --git a/crates/ruff_linter/src/rules/ruff/rules/asyncio_dangling_task.rs b/crates/ruff_linter/src/rules/ruff/rules/asyncio_dangling_task.rs index e93433377f..00e46e0c40 100644 --- a/crates/ruff_linter/src/rules/ruff/rules/asyncio_dangling_task.rs +++ b/crates/ruff_linter/src/rules/ruff/rules/asyncio_dangling_task.rs @@ -153,12 +153,14 @@ pub(crate) fn asyncio_dangling_binding(scope: &Scope, checker: &Checker) { }; match semantic.statement(source) { - Stmt::Assign(ast::StmtAssign { value, targets, .. }) if targets.len() == 1 => { - asyncio_dangling_task(checker, value, semantic); + Stmt::Assign(node) if node.targets.len() == 1 => { + asyncio_dangling_task(checker, &node.value, semantic); + } + Stmt::AnnAssign(node) => { + if let Some(value) = &node.value { + asyncio_dangling_task(checker, value, semantic); + } } - Stmt::AnnAssign(ast::StmtAnnAssign { - value: Some(value), .. - }) => asyncio_dangling_task(checker, value, semantic), _ => {} } } diff --git a/crates/ruff_linter/src/rules/ruff/rules/function_call_in_dataclass_default.rs b/crates/ruff_linter/src/rules/ruff/rules/function_call_in_dataclass_default.rs index 000dd2587a..72f3749716 100644 --- a/crates/ruff_linter/src/rules/ruff/rules/function_call_in_dataclass_default.rs +++ b/crates/ruff_linter/src/rules/ruff/rules/function_call_in_dataclass_default.rs @@ -121,17 +121,16 @@ pub(crate) fn function_call_in_dataclass_default( .collect(); for statement in &class_def.body { - let Stmt::AnnAssign(ast::StmtAnnAssign { - annotation, - value: Some(expr), - .. - }) = statement - else { + let Stmt::AnnAssign(node) = statement else { + continue; + }; + let Some(expr) = &node.value else { continue; }; let Expr::Call(ast::ExprCall { func, .. }) = expr.as_ref() else { continue; }; + let annotation = &node.annotation; let is_field = is_dataclass_field(func, checker.semantic(), dataclass_kind); @@ -179,7 +178,10 @@ fn is_frozen_dataclass_instantiation( .lookup_attribute_in_scope(func, scope_id) .is_some_and(|id| { let binding = &semantic.binding(id); - let Some(Stmt::ClassDef(class_def)) = binding.statement(semantic) else { + let Some(stmt) = binding.statement(semantic) else { + return false; + }; + let Stmt::ClassDef(class_def) = stmt else { return false; }; diff --git a/crates/ruff_linter/src/rules/ruff/rules/implicit_classvar_in_dataclass.rs b/crates/ruff_linter/src/rules/ruff/rules/implicit_classvar_in_dataclass.rs index 46a29cd05e..ea16c15b71 100644 --- a/crates/ruff_linter/src/rules/ruff/rules/implicit_classvar_in_dataclass.rs +++ b/crates/ruff_linter/src/rules/ruff/rules/implicit_classvar_in_dataclass.rs @@ -1,6 +1,6 @@ use ruff_macros::{ViolationMetadata, derive_message_formats}; use ruff_python_ast::helpers::is_dunder; -use ruff_python_ast::{Expr, ExprName, Stmt, StmtAssign, StmtClassDef}; +use ruff_python_ast::{Expr, ExprName, Stmt, StmtClassDef}; use ruff_text_size::Ranged; use crate::Violation; @@ -75,10 +75,11 @@ pub(crate) fn implicit_class_var_in_dataclass(checker: &mut Checker, class_def: } for statement in &class_def.body { - let Stmt::Assign(StmtAssign { targets, .. }) = statement else { + let Stmt::Assign(assign_stmt) = statement else { continue; }; + let targets = &assign_stmt.targets; if targets.len() > 1 { continue; } diff --git a/crates/ruff_linter/src/rules/ruff/rules/legacy_form_pytest_raises.rs b/crates/ruff_linter/src/rules/ruff/rules/legacy_form_pytest_raises.rs index b48a2520f2..fdb3f0ddab 100644 --- a/crates/ruff_linter/src/rules/ruff/rules/legacy_form_pytest_raises.rs +++ b/crates/ruff_linter/src/rules/ruff/rules/legacy_form_pytest_raises.rs @@ -139,7 +139,7 @@ pub(crate) fn legacy_raises_warns_deprecated_call(checker: &Checker, call: &ast: && !has_trailing_content(stmt.end(), checker.source()) { if let Some(with_stmt) = try_fix_legacy_call(context_type, stmt, semantic) { - let generated = checker.generator().stmt(&Stmt::With(with_stmt)); + let generated = checker.generator().stmt(&Stmt::With(Box::new(with_stmt))); let first_line = checker.locator().line_str(stmt.start()); let indentation = leading_indentation(first_line); let mut indented = String::new(); @@ -196,19 +196,19 @@ fn try_fix_legacy_call( None } } - Stmt::Assign(ast::StmtAssign { targets, value, .. }) => { - let call = value.as_call_expr().filter(|call| { + Stmt::Assign(node) => { + let call = node.value.as_call_expr().filter(|call| { PytestContextType::from_expr_name(&call.func, semantic) == Some(context_type) })?; let (optional_vars, assign_targets) = match context_type { PytestContextType::Raises => { - let [target] = targets.as_slice() else { + let [target] = node.targets.as_slice() else { return None; }; (Some(target), None) } PytestContextType::Warns | PytestContextType::DeprecatedCall => { - (None, Some(targets.as_slice())) + (None, Some(node.targets.as_slice())) } }; @@ -280,12 +280,12 @@ fn generate_with_statement( }; let body = if let Some(assign_targets) = assign_targets { - Stmt::Assign(ast::StmtAssign { + Stmt::Assign(Box::new(ast::StmtAssign { node_index: AtomicNodeIndex::NONE, range: TextRange::default(), targets: assign_targets.to_vec(), value: Box::new(func_call.into()), - }) + })) } else { Stmt::Expr(StmtExpr { node_index: AtomicNodeIndex::NONE, diff --git a/crates/ruff_linter/src/rules/ruff/rules/mutable_class_default.rs b/crates/ruff_linter/src/rules/ruff/rules/mutable_class_default.rs index 991008f662..42d1701145 100644 --- a/crates/ruff_linter/src/rules/ruff/rules/mutable_class_default.rs +++ b/crates/ruff_linter/src/rules/ruff/rules/mutable_class_default.rs @@ -102,50 +102,47 @@ pub(crate) fn mutable_class_default(checker: &Checker, class_def: &ast::StmtClas for statement in &class_def.body { match statement { - Stmt::AnnAssign(ast::StmtAnnAssign { - annotation, - target, - value: Some(value), - .. - }) => { - if let ast::Expr::Name(ast::ExprName { id, .. }) = target.as_ref() { - if is_class_var_annotation(annotation, checker.semantic()) { + Stmt::AnnAssign(node) => { + if let ast::Expr::Name(ast::ExprName { id, .. }) = node.target.as_ref() { + if is_class_var_annotation(&node.annotation, checker.semantic()) { class_var_targets.insert(id); } } - if !is_special_attribute(target) - && is_mutable_expr(value, checker.semantic()) - && !is_class_var_annotation(annotation, checker.semantic()) - && !is_final_annotation(annotation, checker.semantic()) - && !is_immutable_annotation(annotation, checker.semantic(), &[]) - { - if dataclass_kind(class_def, checker.semantic()).is_some() { - continue; - } + if let Some(value) = &node.value { + if !is_special_attribute(&node.target) + && is_mutable_expr(value, checker.semantic()) + && !is_class_var_annotation(&node.annotation, checker.semantic()) + && !is_final_annotation(&node.annotation, checker.semantic()) + && !is_immutable_annotation(&node.annotation, checker.semantic(), &[]) + { + if dataclass_kind(class_def, checker.semantic()).is_some() { + continue; + } - // Avoid, e.g., Pydantic and msgspec models, which end up copying defaults on instance creation. - if has_default_copy_semantics(class_def, checker.semantic()) { - return; - } + // Avoid, e.g., Pydantic and msgspec models, which end up copying defaults on instance creation. + if has_default_copy_semantics(class_def, checker.semantic()) { + return; + } - checker.report_diagnostic(MutableClassDefault, value.range()); + checker.report_diagnostic(MutableClassDefault, value.range()); + } } } - Stmt::Assign(ast::StmtAssign { value, targets, .. }) => { - if !targets.iter().all(|target| { + Stmt::Assign(node) => { + if !node.targets.iter().all(|target| { is_special_attribute(target) || target .as_name_expr() .is_some_and(|name| class_var_targets.contains(&name.id)) - }) && is_mutable_expr(value, checker.semantic()) + }) && is_mutable_expr(&node.value, checker.semantic()) { // Avoid, e.g., Pydantic and msgspec models, which end up copying defaults on instance creation. if has_default_copy_semantics(class_def, checker.semantic()) { return; } - checker.report_diagnostic(MutableClassDefault, value.range()); + checker.report_diagnostic(MutableClassDefault, node.value.range()); } } _ => (), diff --git a/crates/ruff_linter/src/rules/ruff/rules/mutable_dataclass_default.rs b/crates/ruff_linter/src/rules/ruff/rules/mutable_dataclass_default.rs index f5ad048a9d..52668fb79d 100644 --- a/crates/ruff_linter/src/rules/ruff/rules/mutable_dataclass_default.rs +++ b/crates/ruff_linter/src/rules/ruff/rules/mutable_dataclass_default.rs @@ -74,12 +74,12 @@ pub(crate) fn mutable_dataclass_default(checker: &Checker, class_def: &ast::Stmt } for statement in &class_def.body { - let Stmt::AnnAssign(ast::StmtAnnAssign { - annotation, - value: Some(value), - .. - }) = statement - else { + let Stmt::AnnAssign(ann_assign_stmt) = statement else { + continue; + }; + + let annotation = &ann_assign_stmt.annotation; + let Some(value) = &ann_assign_stmt.value else { continue; }; diff --git a/crates/ruff_linter/src/rules/ruff/rules/sort_dunder_slots.rs b/crates/ruff_linter/src/rules/ruff/rules/sort_dunder_slots.rs index 4038a9508a..de7b468561 100644 --- a/crates/ruff_linter/src/rules/ruff/rules/sort_dunder_slots.rs +++ b/crates/ruff_linter/src/rules/ruff/rules/sort_dunder_slots.rs @@ -120,15 +120,16 @@ pub(crate) fn sort_dunder_slots(checker: &Checker, binding: &Binding) { }; let (target, value) = match stmt { - ast::Stmt::Assign(ast::StmtAssign { targets, value, .. }) => match targets.as_slice() { - [target] => (target, &**value), + ast::Stmt::Assign(node) => match node.targets.as_slice() { + [target] => (target, &*node.value), _ => return, }, - ast::Stmt::AnnAssign(ast::StmtAnnAssign { - target, - value: Some(value), - .. - }) => (&**target, &**value), + ast::Stmt::AnnAssign(node) => { + let Some(value) = &node.value else { + return; + }; + (&*node.target, &**value) + } _ => return, }; diff --git a/crates/ruff_linter/src/rules/ruff/rules/unused_async.rs b/crates/ruff_linter/src/rules/ruff/rules/unused_async.rs index ca442de8ce..cb521def35 100644 --- a/crates/ruff_linter/src/rules/ruff/rules/unused_async.rs +++ b/crates/ruff_linter/src/rules/ruff/rules/unused_async.rs @@ -64,11 +64,19 @@ impl<'a> source_order::SourceOrderVisitor<'a> for AsyncExprVisitor { } fn visit_stmt(&mut self, stmt: &'a Stmt) { match stmt { - Stmt::With(ast::StmtWith { is_async: true, .. }) => { - self.found_await_or_async = true; + Stmt::With(with_stmt) => { + if with_stmt.is_async { + self.found_await_or_async = true; + } else { + source_order::walk_stmt(self, stmt); + } } - Stmt::For(ast::StmtFor { is_async: true, .. }) => { - self.found_await_or_async = true; + Stmt::For(for_stmt) => { + if for_stmt.is_async { + self.found_await_or_async = true; + } else { + source_order::walk_stmt(self, stmt); + } } // avoid counting inner classes' or functions' bodies toward the search Stmt::FunctionDef(function_def) => { diff --git a/crates/ruff_linter/src/rules/tryceratops/rules/raise_within_try.rs b/crates/ruff_linter/src/rules/tryceratops/rules/raise_within_try.rs index cacab604c7..3646f89f8f 100644 --- a/crates/ruff_linter/src/rules/tryceratops/rules/raise_within_try.rs +++ b/crates/ruff_linter/src/rules/tryceratops/rules/raise_within_try.rs @@ -1,4 +1,4 @@ -use ruff_python_ast::{self as ast, ExceptHandler, Stmt}; +use ruff_python_ast::{ExceptHandler, Stmt}; use ruff_macros::{ViolationMetadata, derive_message_formats}; use ruff_python_ast::{ @@ -98,14 +98,15 @@ pub(crate) fn raise_within_try(checker: &Checker, body: &[Stmt], handlers: &[Exc .collect(); for stmt in raises { - let Stmt::Raise(ast::StmtRaise { - exc: Some(exception), - .. - }) = stmt + let Stmt::Raise(node) = stmt else { continue; }; + let Some(exception) = &node.exc else { + continue; + }; + // We can't check exception sub-classes without a type-checker implementation, so let's // just catch the blanket `Exception` for now. if comparables.contains(&ComparableExpr::from(map_callable(exception))) diff --git a/crates/ruff_linter/src/rules/tryceratops/rules/type_check_without_type_error.rs b/crates/ruff_linter/src/rules/tryceratops/rules/type_check_without_type_error.rs index ee8b06cd90..01221701cf 100644 --- a/crates/ruff_linter/src/rules/tryceratops/rules/type_check_without_type_error.rs +++ b/crates/ruff_linter/src/rules/tryceratops/rules/type_check_without_type_error.rs @@ -105,8 +105,10 @@ fn check_body(checker: &Checker, body: &[Stmt]) { if has_control_flow(item) { return; } - if let Stmt::Raise(ast::StmtRaise { exc: Some(exc), .. }) = &item { - check_raise(checker, exc, item); + if let Stmt::Raise(node) = &item { + if let Some(exc) = &node.exc { + check_raise(checker, exc, item); + } } } } @@ -155,8 +157,8 @@ pub(crate) fn type_check_without_type_error( .. } = stmt_if; - if let Some(Stmt::If(ast::StmtIf { test, .. })) = parent { - if !check_type_check_test(checker.semantic(), test) { + if let Some(Stmt::If(node)) = parent { + if !check_type_check_test(checker.semantic(), &node.test) { return; } } diff --git a/crates/ruff_linter/src/rules/tryceratops/rules/useless_try_except.rs b/crates/ruff_linter/src/rules/tryceratops/rules/useless_try_except.rs index d9023378ec..1643171111 100644 --- a/crates/ruff_linter/src/rules/tryceratops/rules/useless_try_except.rs +++ b/crates/ruff_linter/src/rules/tryceratops/rules/useless_try_except.rs @@ -43,12 +43,15 @@ impl Violation for UselessTryExcept { pub(crate) fn useless_try_except(checker: &Checker, handlers: &[ExceptHandler]) { if handlers.iter().all(|handler| { let ExceptHandler::ExceptHandler(ExceptHandlerExceptHandler { name, body, .. }) = handler; - let Some(Stmt::Raise(ast::StmtRaise { - exc, cause: None, .. - })) = &body.first() - else { + let Some(Stmt::Raise(raise_stmt)) = &body.first() else { return false; }; + + if raise_stmt.cause.is_some() { + return false; + } + + let exc = &raise_stmt.exc; if let Some(expr) = exc { // E.g., `except ... as e: raise e` if let Expr::Name(ast::ExprName { id, .. }) = expr.as_ref() { diff --git a/crates/ruff_linter/src/rules/tryceratops/rules/verbose_raise.rs b/crates/ruff_linter/src/rules/tryceratops/rules/verbose_raise.rs index 033f9a93f9..57d419ea8d 100644 --- a/crates/ruff_linter/src/rules/tryceratops/rules/verbose_raise.rs +++ b/crates/ruff_linter/src/rules/tryceratops/rules/verbose_raise.rs @@ -95,13 +95,11 @@ struct RaiseStatementVisitor<'a> { impl<'a> StatementVisitor<'a> for RaiseStatementVisitor<'a> { fn visit_stmt(&mut self, stmt: &'a Stmt) { match stmt { - Stmt::Raise(raise @ ast::StmtRaise { .. }) => { + Stmt::Raise(raise) => { self.raises.push(raise); } - Stmt::Try(ast::StmtTry { - body, finalbody, .. - }) => { - for stmt in body.iter().chain(finalbody) { + Stmt::Try(try_stmt) => { + for stmt in try_stmt.body.iter().chain(&try_stmt.finalbody) { walk_stmt(self, stmt); } } diff --git a/crates/ruff_python_ast/ast.toml b/crates/ruff_python_ast/ast.toml index cbcde4e213..a223f76ece 100644 --- a/crates/ruff_python_ast/ast.toml +++ b/crates/ruff_python_ast/ast.toml @@ -77,6 +77,10 @@ fields = [{ name = "body", type = "Box" }] add_suffix_to_is_methods = true anynode_is_label = "statement" doc = "See also [stmt](https://docs.python.org/3/library/ast.html#ast.stmt)" +# Box all variants except the smallest ones (Pass, Break, Continue, Return, Expr) +# to reduce enum size from 128 bytes to ~24 bytes +box_variants = true +unboxed_variants = ["StmtPass", "StmtBreak", "StmtContinue", "StmtReturn", "StmtExpr"] [Stmt.nodes.StmtFunctionDef] doc = """See also [FunctionDef](https://docs.python.org/3/library/ast.html#ast.FunctionDef) diff --git a/crates/ruff_python_ast/generate.py b/crates/ruff_python_ast/generate.py index 7ed8646f5b..1e8baa27bd 100644 --- a/crates/ruff_python_ast/generate.py +++ b/crates/ruff_python_ast/generate.py @@ -122,6 +122,8 @@ class Group: add_suffix_to_is_methods: bool anynode_is_label: str doc: str | None + box_variants: bool + unboxed_variants: set[str] def __init__(self, group_name: str, group: dict[str, Any]) -> None: self.name = group_name @@ -130,10 +132,16 @@ class Group: self.add_suffix_to_is_methods = group.get("add_suffix_to_is_methods", False) self.anynode_is_label = group.get("anynode_is_label", to_snake_case(group_name)) self.doc = group.get("doc") + self.box_variants = group.get("box_variants", False) + self.unboxed_variants = set(group.get("unboxed_variants", [])) self.nodes = [ Node(self, node_name, node) for node_name, node in group["nodes"].items() ] + def is_boxed(self, node_name: str) -> bool: + """Returns True if this node should be boxed in the owned enum.""" + return self.box_variants and node_name not in self.unboxed_variants + @dataclass class Node: @@ -321,17 +329,29 @@ def write_owned_enum(out: list[str], ast: Ast) -> None: out.append('#[cfg_attr(feature = "get-size", derive(get_size2::GetSize))]') out.append(f"pub enum {group.owned_enum_ty} {{") for node in group.nodes: - out.append(f"{node.variant}({node.ty}),") + if group.is_boxed(node.name): + out.append(f"{node.variant}(Box<{node.ty}>),") + else: + out.append(f"{node.variant}({node.ty}),") out.append("}") for node in group.nodes: - out.append(f""" - impl From<{node.ty}> for {group.owned_enum_ty} {{ - fn from(node: {node.ty}) -> Self {{ - Self::{node.variant}(node) + if group.is_boxed(node.name): + out.append(f""" + impl From<{node.ty}> for {group.owned_enum_ty} {{ + fn from(node: {node.ty}) -> Self {{ + Self::{node.variant}(Box::new(node)) + }} }} - }} - """) + """) + else: + out.append(f""" + impl From<{node.ty}> for {group.owned_enum_ty} {{ + fn from(node: {node.ty}) -> Self {{ + Self::{node.variant}(node) + }} + }} + """) out.append(f""" impl ruff_text_size::Ranged for {group.owned_enum_ty} {{ @@ -369,6 +389,9 @@ def write_owned_enum(out: list[str], ast: Ast) -> None: match_arm = f"Self::{variant_name}" if group.add_suffix_to_is_methods: is_name = to_snake_case(node.variant + group.name) + is_boxed = group.is_boxed(node.name) + # For boxed variants, we need to dereference the box + unbox = "*" if is_boxed else "" if len(group.nodes) > 1: out.append(f""" #[inline] @@ -379,7 +402,7 @@ def write_owned_enum(out: list[str], ast: Ast) -> None: #[inline] pub fn {is_name}(self) -> Option<{node.ty}> {{ match self {{ - {match_arm}(val) => Some(val), + {match_arm}(val) => Some({unbox}val), _ => None, }} }} @@ -387,7 +410,7 @@ def write_owned_enum(out: list[str], ast: Ast) -> None: #[inline] pub fn expect_{is_name}(self) -> {node.ty} {{ match self {{ - {match_arm}(val) => val, + {match_arm}(val) => {unbox}val, _ => panic!("called expect on {{self:?}}"), }} }} @@ -418,14 +441,14 @@ def write_owned_enum(out: list[str], ast: Ast) -> None: #[inline] pub fn {is_name}(self) -> Option<{node.ty}> {{ match self {{ - {match_arm}(val) => Some(val), + {match_arm}(val) => Some({unbox}val), }} }} #[inline] pub fn expect_{is_name}(self) -> {node.ty} {{ match self {{ - {match_arm}(val) => val, + {match_arm}(val) => {unbox}val, }} }} diff --git a/crates/ruff_python_ast/src/comparable.rs b/crates/ruff_python_ast/src/comparable.rs index c9ba3ac398..1bc390978f 100644 --- a/crates/ruff_python_ast/src/comparable.rs +++ b/crates/ruff_python_ast/src/comparable.rs @@ -1594,233 +1594,297 @@ pub enum ComparableStmt<'a> { impl<'a> From<&'a ast::Stmt> for ComparableStmt<'a> { fn from(stmt: &'a ast::Stmt) -> Self { match stmt { - ast::Stmt::FunctionDef(ast::StmtFunctionDef { - is_async, - name, - parameters, - body, - decorator_list, - returns, - type_params, - range: _, - node_index: _, - }) => Self::FunctionDef(StmtFunctionDef { - is_async: *is_async, - name: name.as_str(), - parameters: parameters.into(), - body: body.iter().map(Into::into).collect(), - decorator_list: decorator_list.iter().map(Into::into).collect(), - returns: returns.as_ref().map(Into::into), - type_params: type_params.as_ref().map(Into::into), - }), - ast::Stmt::ClassDef(ast::StmtClassDef { - name, - arguments, - body, - decorator_list, - type_params, - range: _, - node_index: _, - }) => Self::ClassDef(StmtClassDef { - name: name.as_str(), - arguments: arguments.as_ref().map(Into::into).unwrap_or_default(), - body: body.iter().map(Into::into).collect(), - decorator_list: decorator_list.iter().map(Into::into).collect(), - type_params: type_params.as_ref().map(Into::into), - }), + ast::Stmt::FunctionDef(node) => { + let ast::StmtFunctionDef { + is_async, + name, + parameters, + body, + decorator_list, + returns, + type_params, + range: _, + node_index: _, + } = &**node; + Self::FunctionDef(StmtFunctionDef { + is_async: *is_async, + name: name.as_str(), + parameters: parameters.into(), + body: body.iter().map(Into::into).collect(), + decorator_list: decorator_list.iter().map(Into::into).collect(), + returns: returns.as_ref().map(Into::into), + type_params: type_params.as_ref().map(Into::into), + }) + } + ast::Stmt::ClassDef(node) => { + let ast::StmtClassDef { + name, + arguments, + body, + decorator_list, + type_params, + range: _, + node_index: _, + } = &**node; + Self::ClassDef(StmtClassDef { + name: name.as_str(), + arguments: arguments.as_ref().map(Into::into).unwrap_or_default(), + body: body.iter().map(Into::into).collect(), + decorator_list: decorator_list.iter().map(Into::into).collect(), + type_params: type_params.as_ref().map(Into::into), + }) + } ast::Stmt::Return(ast::StmtReturn { value, range: _, node_index: _, - }) => Self::Return(StmtReturn { - value: value.as_ref().map(Into::into), - }), - ast::Stmt::Delete(ast::StmtDelete { - targets, - range: _, - node_index: _, - }) => Self::Delete(StmtDelete { - targets: targets.iter().map(Into::into).collect(), - }), - ast::Stmt::TypeAlias(ast::StmtTypeAlias { - range: _, - node_index: _, - name, - type_params, - value, - }) => Self::TypeAlias(StmtTypeAlias { - name: name.into(), - type_params: type_params.as_ref().map(Into::into), - value: value.into(), - }), - ast::Stmt::Assign(ast::StmtAssign { - targets, - value, - range: _, - node_index: _, - }) => Self::Assign(StmtAssign { - targets: targets.iter().map(Into::into).collect(), - value: value.into(), - }), - ast::Stmt::AugAssign(ast::StmtAugAssign { - target, - op, - value, - range: _, - node_index: _, - }) => Self::AugAssign(StmtAugAssign { - target: target.into(), - op: (*op).into(), - value: value.into(), - }), - ast::Stmt::AnnAssign(ast::StmtAnnAssign { - target, - annotation, - value, - simple, - range: _, - node_index: _, - }) => Self::AnnAssign(StmtAnnAssign { - target: target.into(), - annotation: annotation.into(), - value: value.as_ref().map(Into::into), - simple: *simple, - }), - ast::Stmt::For(ast::StmtFor { - is_async, - target, - iter, - body, - orelse, - range: _, - node_index: _, - }) => Self::For(StmtFor { - is_async: *is_async, - target: target.into(), - iter: iter.into(), - body: body.iter().map(Into::into).collect(), - orelse: orelse.iter().map(Into::into).collect(), - }), - ast::Stmt::While(ast::StmtWhile { - test, - body, - orelse, - range: _, - node_index: _, - }) => Self::While(StmtWhile { - test: test.into(), - body: body.iter().map(Into::into).collect(), - orelse: orelse.iter().map(Into::into).collect(), - }), - ast::Stmt::If(ast::StmtIf { - test, - body, - elif_else_clauses, - range: _, - node_index: _, - }) => Self::If(StmtIf { - test: test.into(), - body: body.iter().map(Into::into).collect(), - elif_else_clauses: elif_else_clauses.iter().map(Into::into).collect(), - }), - ast::Stmt::With(ast::StmtWith { - is_async, - items, - body, - range: _, - node_index: _, - }) => Self::With(StmtWith { - is_async: *is_async, - items: items.iter().map(Into::into).collect(), - body: body.iter().map(Into::into).collect(), - }), - ast::Stmt::Match(ast::StmtMatch { - subject, - cases, - range: _, - node_index: _, - }) => Self::Match(StmtMatch { - subject: subject.into(), - cases: cases.iter().map(Into::into).collect(), - }), - ast::Stmt::Raise(ast::StmtRaise { - exc, - cause, - range: _, - node_index: _, - }) => Self::Raise(StmtRaise { - exc: exc.as_ref().map(Into::into), - cause: cause.as_ref().map(Into::into), - }), - ast::Stmt::Try(ast::StmtTry { - body, - handlers, - orelse, - finalbody, - is_star, - range: _, - node_index: _, - }) => Self::Try(StmtTry { - body: body.iter().map(Into::into).collect(), - handlers: handlers.iter().map(Into::into).collect(), - orelse: orelse.iter().map(Into::into).collect(), - finalbody: finalbody.iter().map(Into::into).collect(), - is_star: *is_star, - }), - ast::Stmt::Assert(ast::StmtAssert { - test, - msg, - range: _, - node_index: _, - }) => Self::Assert(StmtAssert { - test: test.into(), - msg: msg.as_ref().map(Into::into), - }), - ast::Stmt::Import(ast::StmtImport { - names, - range: _, - node_index: _, - }) => Self::Import(StmtImport { - names: names.iter().map(Into::into).collect(), - }), - ast::Stmt::ImportFrom(ast::StmtImportFrom { - module, - names, - level, - range: _, - node_index: _, - }) => Self::ImportFrom(StmtImportFrom { - module: module.as_deref(), - names: names.iter().map(Into::into).collect(), - level: *level, - }), - ast::Stmt::Global(ast::StmtGlobal { - names, - range: _, - node_index: _, - }) => Self::Global(StmtGlobal { - names: names.iter().map(ast::Identifier::as_str).collect(), - }), - ast::Stmt::Nonlocal(ast::StmtNonlocal { - names, - range: _, - node_index: _, - }) => Self::Nonlocal(StmtNonlocal { - names: names.iter().map(ast::Identifier::as_str).collect(), - }), - ast::Stmt::IpyEscapeCommand(ast::StmtIpyEscapeCommand { - kind, - value, - range: _, - node_index: _, - }) => Self::IpyEscapeCommand(StmtIpyEscapeCommand { kind: *kind, value }), + }) => { + Self::Return(StmtReturn { + value: value.as_ref().map(Into::into), + }) + } + ast::Stmt::Delete(node) => { + let ast::StmtDelete { + targets, + range: _, + node_index: _, + } = &**node; + Self::Delete(StmtDelete { + targets: targets.iter().map(Into::into).collect(), + }) + } + ast::Stmt::TypeAlias(node) => { + let ast::StmtTypeAlias { + range: _, + node_index: _, + name, + type_params, + value, + } = &**node; + Self::TypeAlias(StmtTypeAlias { + name: name.into(), + type_params: type_params.as_ref().map(Into::into), + value: value.into(), + }) + } + ast::Stmt::Assign(node) => { + let ast::StmtAssign { + targets, + value, + range: _, + node_index: _, + } = &**node; + Self::Assign(StmtAssign { + targets: targets.iter().map(Into::into).collect(), + value: value.into(), + }) + } + ast::Stmt::AugAssign(node) => { + let ast::StmtAugAssign { + target, + op, + value, + range: _, + node_index: _, + } = &**node; + Self::AugAssign(StmtAugAssign { + target: target.into(), + op: (*op).into(), + value: value.into(), + }) + } + ast::Stmt::AnnAssign(node) => { + let ast::StmtAnnAssign { + target, + annotation, + value, + simple, + range: _, + node_index: _, + } = &**node; + Self::AnnAssign(StmtAnnAssign { + target: target.into(), + annotation: annotation.into(), + value: value.as_ref().map(Into::into), + simple: *simple, + }) + } + ast::Stmt::For(node) => { + let ast::StmtFor { + is_async, + target, + iter, + body, + orelse, + range: _, + node_index: _, + } = &**node; + Self::For(StmtFor { + is_async: *is_async, + target: target.into(), + iter: iter.into(), + body: body.iter().map(Into::into).collect(), + orelse: orelse.iter().map(Into::into).collect(), + }) + } + ast::Stmt::While(node) => { + let ast::StmtWhile { + test, + body, + orelse, + range: _, + node_index: _, + } = &**node; + Self::While(StmtWhile { + test: test.into(), + body: body.iter().map(Into::into).collect(), + orelse: orelse.iter().map(Into::into).collect(), + }) + } + ast::Stmt::If(node) => { + let ast::StmtIf { + test, + body, + elif_else_clauses, + range: _, + node_index: _, + } = &**node; + Self::If(StmtIf { + test: test.into(), + body: body.iter().map(Into::into).collect(), + elif_else_clauses: elif_else_clauses.iter().map(Into::into).collect(), + }) + } + ast::Stmt::With(node) => { + let ast::StmtWith { + is_async, + items, + body, + range: _, + node_index: _, + } = &**node; + Self::With(StmtWith { + is_async: *is_async, + items: items.iter().map(Into::into).collect(), + body: body.iter().map(Into::into).collect(), + }) + } + ast::Stmt::Match(node) => { + let ast::StmtMatch { + subject, + cases, + range: _, + node_index: _, + } = &**node; + Self::Match(StmtMatch { + subject: subject.into(), + cases: cases.iter().map(Into::into).collect(), + }) + } + ast::Stmt::Raise(node) => { + let ast::StmtRaise { + exc, + cause, + range: _, + node_index: _, + } = &**node; + Self::Raise(StmtRaise { + exc: exc.as_ref().map(Into::into), + cause: cause.as_ref().map(Into::into), + }) + } + ast::Stmt::Try(node) => { + let ast::StmtTry { + body, + handlers, + orelse, + finalbody, + is_star, + range: _, + node_index: _, + } = &**node; + Self::Try(StmtTry { + body: body.iter().map(Into::into).collect(), + handlers: handlers.iter().map(Into::into).collect(), + orelse: orelse.iter().map(Into::into).collect(), + finalbody: finalbody.iter().map(Into::into).collect(), + is_star: *is_star, + }) + } + ast::Stmt::Assert(node) => { + let ast::StmtAssert { + test, + msg, + range: _, + node_index: _, + } = &**node; + Self::Assert(StmtAssert { + test: test.into(), + msg: msg.as_ref().map(Into::into), + }) + } + ast::Stmt::Import(node) => { + let ast::StmtImport { + names, + range: _, + node_index: _, + } = &**node; + Self::Import(StmtImport { + names: names.iter().map(Into::into).collect(), + }) + } + ast::Stmt::ImportFrom(node) => { + let ast::StmtImportFrom { + module, + names, + level, + range: _, + node_index: _, + } = &**node; + Self::ImportFrom(StmtImportFrom { + module: module.as_deref(), + names: names.iter().map(Into::into).collect(), + level: *level, + }) + } + ast::Stmt::Global(node) => { + let ast::StmtGlobal { + names, + range: _, + node_index: _, + } = &**node; + Self::Global(StmtGlobal { + names: names.iter().map(ast::Identifier::as_str).collect(), + }) + } + ast::Stmt::Nonlocal(node) => { + let ast::StmtNonlocal { + names, + range: _, + node_index: _, + } = &**node; + Self::Nonlocal(StmtNonlocal { + names: names.iter().map(ast::Identifier::as_str).collect(), + }) + } + ast::Stmt::IpyEscapeCommand(node) => { + let ast::StmtIpyEscapeCommand { + kind, + value, + range: _, + node_index: _, + } = &**node; + Self::IpyEscapeCommand(StmtIpyEscapeCommand { kind: *kind, value }) + } ast::Stmt::Expr(ast::StmtExpr { value, range: _, node_index: _, - }) => Self::Expr(StmtExpr { - value: value.into(), - }), + }) => { + Self::Expr(StmtExpr { + value: value.into(), + }) + } ast::Stmt::Pass(_) => Self::Pass, ast::Stmt::Break(_) => Self::Break, ast::Stmt::Continue(_) => Self::Continue, diff --git a/crates/ruff_python_ast/src/generated.rs b/crates/ruff_python_ast/src/generated.rs index 547c50d631..bf67969628 100644 --- a/crates/ruff_python_ast/src/generated.rs +++ b/crates/ruff_python_ast/src/generated.rs @@ -123,42 +123,42 @@ impl Mod { #[derive(Clone, Debug, PartialEq)] #[cfg_attr(feature = "get-size", derive(get_size2::GetSize))] pub enum Stmt { - FunctionDef(crate::StmtFunctionDef), - ClassDef(crate::StmtClassDef), + FunctionDef(Box), + ClassDef(Box), Return(crate::StmtReturn), - Delete(crate::StmtDelete), - TypeAlias(crate::StmtTypeAlias), - Assign(crate::StmtAssign), - AugAssign(crate::StmtAugAssign), - AnnAssign(crate::StmtAnnAssign), - For(crate::StmtFor), - While(crate::StmtWhile), - If(crate::StmtIf), - With(crate::StmtWith), - Match(crate::StmtMatch), - Raise(crate::StmtRaise), - Try(crate::StmtTry), - Assert(crate::StmtAssert), - Import(crate::StmtImport), - ImportFrom(crate::StmtImportFrom), - Global(crate::StmtGlobal), - Nonlocal(crate::StmtNonlocal), + Delete(Box), + TypeAlias(Box), + Assign(Box), + AugAssign(Box), + AnnAssign(Box), + For(Box), + While(Box), + If(Box), + With(Box), + Match(Box), + Raise(Box), + Try(Box), + Assert(Box), + Import(Box), + ImportFrom(Box), + Global(Box), + Nonlocal(Box), Expr(crate::StmtExpr), Pass(crate::StmtPass), Break(crate::StmtBreak), Continue(crate::StmtContinue), - IpyEscapeCommand(crate::StmtIpyEscapeCommand), + IpyEscapeCommand(Box), } impl From for Stmt { fn from(node: crate::StmtFunctionDef) -> Self { - Self::FunctionDef(node) + Self::FunctionDef(Box::new(node)) } } impl From for Stmt { fn from(node: crate::StmtClassDef) -> Self { - Self::ClassDef(node) + Self::ClassDef(Box::new(node)) } } @@ -170,103 +170,103 @@ impl From for Stmt { impl From for Stmt { fn from(node: crate::StmtDelete) -> Self { - Self::Delete(node) + Self::Delete(Box::new(node)) } } impl From for Stmt { fn from(node: crate::StmtTypeAlias) -> Self { - Self::TypeAlias(node) + Self::TypeAlias(Box::new(node)) } } impl From for Stmt { fn from(node: crate::StmtAssign) -> Self { - Self::Assign(node) + Self::Assign(Box::new(node)) } } impl From for Stmt { fn from(node: crate::StmtAugAssign) -> Self { - Self::AugAssign(node) + Self::AugAssign(Box::new(node)) } } impl From for Stmt { fn from(node: crate::StmtAnnAssign) -> Self { - Self::AnnAssign(node) + Self::AnnAssign(Box::new(node)) } } impl From for Stmt { fn from(node: crate::StmtFor) -> Self { - Self::For(node) + Self::For(Box::new(node)) } } impl From for Stmt { fn from(node: crate::StmtWhile) -> Self { - Self::While(node) + Self::While(Box::new(node)) } } impl From for Stmt { fn from(node: crate::StmtIf) -> Self { - Self::If(node) + Self::If(Box::new(node)) } } impl From for Stmt { fn from(node: crate::StmtWith) -> Self { - Self::With(node) + Self::With(Box::new(node)) } } impl From for Stmt { fn from(node: crate::StmtMatch) -> Self { - Self::Match(node) + Self::Match(Box::new(node)) } } impl From for Stmt { fn from(node: crate::StmtRaise) -> Self { - Self::Raise(node) + Self::Raise(Box::new(node)) } } impl From for Stmt { fn from(node: crate::StmtTry) -> Self { - Self::Try(node) + Self::Try(Box::new(node)) } } impl From for Stmt { fn from(node: crate::StmtAssert) -> Self { - Self::Assert(node) + Self::Assert(Box::new(node)) } } impl From for Stmt { fn from(node: crate::StmtImport) -> Self { - Self::Import(node) + Self::Import(Box::new(node)) } } impl From for Stmt { fn from(node: crate::StmtImportFrom) -> Self { - Self::ImportFrom(node) + Self::ImportFrom(Box::new(node)) } } impl From for Stmt { fn from(node: crate::StmtGlobal) -> Self { - Self::Global(node) + Self::Global(Box::new(node)) } } impl From for Stmt { fn from(node: crate::StmtNonlocal) -> Self { - Self::Nonlocal(node) + Self::Nonlocal(Box::new(node)) } } @@ -296,7 +296,7 @@ impl From for Stmt { impl From for Stmt { fn from(node: crate::StmtIpyEscapeCommand) -> Self { - Self::IpyEscapeCommand(node) + Self::IpyEscapeCommand(Box::new(node)) } } @@ -374,7 +374,7 @@ impl Stmt { #[inline] pub fn function_def_stmt(self) -> Option { match self { - Self::FunctionDef(val) => Some(val), + Self::FunctionDef(val) => Some(*val), _ => None, } } @@ -382,7 +382,7 @@ impl Stmt { #[inline] pub fn expect_function_def_stmt(self) -> crate::StmtFunctionDef { match self { - Self::FunctionDef(val) => val, + Self::FunctionDef(val) => *val, _ => panic!("called expect on {self:?}"), } } @@ -411,7 +411,7 @@ impl Stmt { #[inline] pub fn class_def_stmt(self) -> Option { match self { - Self::ClassDef(val) => Some(val), + Self::ClassDef(val) => Some(*val), _ => None, } } @@ -419,7 +419,7 @@ impl Stmt { #[inline] pub fn expect_class_def_stmt(self) -> crate::StmtClassDef { match self { - Self::ClassDef(val) => val, + Self::ClassDef(val) => *val, _ => panic!("called expect on {self:?}"), } } @@ -485,7 +485,7 @@ impl Stmt { #[inline] pub fn delete_stmt(self) -> Option { match self { - Self::Delete(val) => Some(val), + Self::Delete(val) => Some(*val), _ => None, } } @@ -493,7 +493,7 @@ impl Stmt { #[inline] pub fn expect_delete_stmt(self) -> crate::StmtDelete { match self { - Self::Delete(val) => val, + Self::Delete(val) => *val, _ => panic!("called expect on {self:?}"), } } @@ -522,7 +522,7 @@ impl Stmt { #[inline] pub fn type_alias_stmt(self) -> Option { match self { - Self::TypeAlias(val) => Some(val), + Self::TypeAlias(val) => Some(*val), _ => None, } } @@ -530,7 +530,7 @@ impl Stmt { #[inline] pub fn expect_type_alias_stmt(self) -> crate::StmtTypeAlias { match self { - Self::TypeAlias(val) => val, + Self::TypeAlias(val) => *val, _ => panic!("called expect on {self:?}"), } } @@ -559,7 +559,7 @@ impl Stmt { #[inline] pub fn assign_stmt(self) -> Option { match self { - Self::Assign(val) => Some(val), + Self::Assign(val) => Some(*val), _ => None, } } @@ -567,7 +567,7 @@ impl Stmt { #[inline] pub fn expect_assign_stmt(self) -> crate::StmtAssign { match self { - Self::Assign(val) => val, + Self::Assign(val) => *val, _ => panic!("called expect on {self:?}"), } } @@ -596,7 +596,7 @@ impl Stmt { #[inline] pub fn aug_assign_stmt(self) -> Option { match self { - Self::AugAssign(val) => Some(val), + Self::AugAssign(val) => Some(*val), _ => None, } } @@ -604,7 +604,7 @@ impl Stmt { #[inline] pub fn expect_aug_assign_stmt(self) -> crate::StmtAugAssign { match self { - Self::AugAssign(val) => val, + Self::AugAssign(val) => *val, _ => panic!("called expect on {self:?}"), } } @@ -633,7 +633,7 @@ impl Stmt { #[inline] pub fn ann_assign_stmt(self) -> Option { match self { - Self::AnnAssign(val) => Some(val), + Self::AnnAssign(val) => Some(*val), _ => None, } } @@ -641,7 +641,7 @@ impl Stmt { #[inline] pub fn expect_ann_assign_stmt(self) -> crate::StmtAnnAssign { match self { - Self::AnnAssign(val) => val, + Self::AnnAssign(val) => *val, _ => panic!("called expect on {self:?}"), } } @@ -670,7 +670,7 @@ impl Stmt { #[inline] pub fn for_stmt(self) -> Option { match self { - Self::For(val) => Some(val), + Self::For(val) => Some(*val), _ => None, } } @@ -678,7 +678,7 @@ impl Stmt { #[inline] pub fn expect_for_stmt(self) -> crate::StmtFor { match self { - Self::For(val) => val, + Self::For(val) => *val, _ => panic!("called expect on {self:?}"), } } @@ -707,7 +707,7 @@ impl Stmt { #[inline] pub fn while_stmt(self) -> Option { match self { - Self::While(val) => Some(val), + Self::While(val) => Some(*val), _ => None, } } @@ -715,7 +715,7 @@ impl Stmt { #[inline] pub fn expect_while_stmt(self) -> crate::StmtWhile { match self { - Self::While(val) => val, + Self::While(val) => *val, _ => panic!("called expect on {self:?}"), } } @@ -744,7 +744,7 @@ impl Stmt { #[inline] pub fn if_stmt(self) -> Option { match self { - Self::If(val) => Some(val), + Self::If(val) => Some(*val), _ => None, } } @@ -752,7 +752,7 @@ impl Stmt { #[inline] pub fn expect_if_stmt(self) -> crate::StmtIf { match self { - Self::If(val) => val, + Self::If(val) => *val, _ => panic!("called expect on {self:?}"), } } @@ -781,7 +781,7 @@ impl Stmt { #[inline] pub fn with_stmt(self) -> Option { match self { - Self::With(val) => Some(val), + Self::With(val) => Some(*val), _ => None, } } @@ -789,7 +789,7 @@ impl Stmt { #[inline] pub fn expect_with_stmt(self) -> crate::StmtWith { match self { - Self::With(val) => val, + Self::With(val) => *val, _ => panic!("called expect on {self:?}"), } } @@ -818,7 +818,7 @@ impl Stmt { #[inline] pub fn match_stmt(self) -> Option { match self { - Self::Match(val) => Some(val), + Self::Match(val) => Some(*val), _ => None, } } @@ -826,7 +826,7 @@ impl Stmt { #[inline] pub fn expect_match_stmt(self) -> crate::StmtMatch { match self { - Self::Match(val) => val, + Self::Match(val) => *val, _ => panic!("called expect on {self:?}"), } } @@ -855,7 +855,7 @@ impl Stmt { #[inline] pub fn raise_stmt(self) -> Option { match self { - Self::Raise(val) => Some(val), + Self::Raise(val) => Some(*val), _ => None, } } @@ -863,7 +863,7 @@ impl Stmt { #[inline] pub fn expect_raise_stmt(self) -> crate::StmtRaise { match self { - Self::Raise(val) => val, + Self::Raise(val) => *val, _ => panic!("called expect on {self:?}"), } } @@ -892,7 +892,7 @@ impl Stmt { #[inline] pub fn try_stmt(self) -> Option { match self { - Self::Try(val) => Some(val), + Self::Try(val) => Some(*val), _ => None, } } @@ -900,7 +900,7 @@ impl Stmt { #[inline] pub fn expect_try_stmt(self) -> crate::StmtTry { match self { - Self::Try(val) => val, + Self::Try(val) => *val, _ => panic!("called expect on {self:?}"), } } @@ -929,7 +929,7 @@ impl Stmt { #[inline] pub fn assert_stmt(self) -> Option { match self { - Self::Assert(val) => Some(val), + Self::Assert(val) => Some(*val), _ => None, } } @@ -937,7 +937,7 @@ impl Stmt { #[inline] pub fn expect_assert_stmt(self) -> crate::StmtAssert { match self { - Self::Assert(val) => val, + Self::Assert(val) => *val, _ => panic!("called expect on {self:?}"), } } @@ -966,7 +966,7 @@ impl Stmt { #[inline] pub fn import_stmt(self) -> Option { match self { - Self::Import(val) => Some(val), + Self::Import(val) => Some(*val), _ => None, } } @@ -974,7 +974,7 @@ impl Stmt { #[inline] pub fn expect_import_stmt(self) -> crate::StmtImport { match self { - Self::Import(val) => val, + Self::Import(val) => *val, _ => panic!("called expect on {self:?}"), } } @@ -1003,7 +1003,7 @@ impl Stmt { #[inline] pub fn import_from_stmt(self) -> Option { match self { - Self::ImportFrom(val) => Some(val), + Self::ImportFrom(val) => Some(*val), _ => None, } } @@ -1011,7 +1011,7 @@ impl Stmt { #[inline] pub fn expect_import_from_stmt(self) -> crate::StmtImportFrom { match self { - Self::ImportFrom(val) => val, + Self::ImportFrom(val) => *val, _ => panic!("called expect on {self:?}"), } } @@ -1040,7 +1040,7 @@ impl Stmt { #[inline] pub fn global_stmt(self) -> Option { match self { - Self::Global(val) => Some(val), + Self::Global(val) => Some(*val), _ => None, } } @@ -1048,7 +1048,7 @@ impl Stmt { #[inline] pub fn expect_global_stmt(self) -> crate::StmtGlobal { match self { - Self::Global(val) => val, + Self::Global(val) => *val, _ => panic!("called expect on {self:?}"), } } @@ -1077,7 +1077,7 @@ impl Stmt { #[inline] pub fn nonlocal_stmt(self) -> Option { match self { - Self::Nonlocal(val) => Some(val), + Self::Nonlocal(val) => Some(*val), _ => None, } } @@ -1085,7 +1085,7 @@ impl Stmt { #[inline] pub fn expect_nonlocal_stmt(self) -> crate::StmtNonlocal { match self { - Self::Nonlocal(val) => val, + Self::Nonlocal(val) => *val, _ => panic!("called expect on {self:?}"), } } @@ -1262,7 +1262,7 @@ impl Stmt { #[inline] pub fn ipy_escape_command_stmt(self) -> Option { match self { - Self::IpyEscapeCommand(val) => Some(val), + Self::IpyEscapeCommand(val) => Some(*val), _ => None, } } @@ -1270,7 +1270,7 @@ impl Stmt { #[inline] pub fn expect_ipy_escape_command_stmt(self) -> crate::StmtIpyEscapeCommand { match self { - Self::IpyEscapeCommand(val) => val, + Self::IpyEscapeCommand(val) => *val, _ => panic!("called expect on {self:?}"), } } diff --git a/crates/ruff_python_ast/src/helpers.rs b/crates/ruff_python_ast/src/helpers.rs index 9680d03ec3..c0703e95ca 100644 --- a/crates/ruff_python_ast/src/helpers.rs +++ b/crates/ruff_python_ast/src/helpers.rs @@ -392,43 +392,32 @@ pub fn any_over_interpolated_string_element( pub fn any_over_stmt(stmt: &Stmt, func: &dyn Fn(&Expr) -> bool) -> bool { match stmt { - Stmt::FunctionDef(ast::StmtFunctionDef { - parameters, - type_params, - body, - decorator_list, - returns, - .. - }) => { - parameters.iter().any(|param| { + Stmt::FunctionDef(node) => { + node.parameters.iter().any(|param| { param .default() .is_some_and(|default| any_over_expr(default, func)) || param .annotation() .is_some_and(|annotation| any_over_expr(annotation, func)) - }) || type_params.as_ref().is_some_and(|type_params| { + }) || node.type_params.as_ref().is_some_and(|type_params| { type_params .iter() .any(|type_param| any_over_type_param(type_param, func)) - }) || body.iter().any(|stmt| any_over_stmt(stmt, func)) - || decorator_list + }) || node.body.iter().any(|stmt| any_over_stmt(stmt, func)) + || node + .decorator_list .iter() .any(|decorator| any_over_expr(&decorator.expression, func)) - || returns + || node + .returns .as_ref() .is_some_and(|value| any_over_expr(value, func)) } - Stmt::ClassDef(ast::StmtClassDef { - arguments, - type_params, - body, - decorator_list, - .. - }) => { + Stmt::ClassDef(node) => { // Note that e.g. `class A(*args, a=2, *args2, **kwargs): pass` is a valid class // definition - arguments + node.arguments .as_deref() .is_some_and(|Arguments { args, keywords, .. }| { args.iter().any(|expr| any_over_expr(expr, func)) @@ -436,89 +425,61 @@ pub fn any_over_stmt(stmt: &Stmt, func: &dyn Fn(&Expr) -> bool) -> bool { .iter() .any(|keyword| any_over_expr(&keyword.value, func)) }) - || type_params.as_ref().is_some_and(|type_params| { + || node.type_params.as_ref().is_some_and(|type_params| { type_params .iter() .any(|type_param| any_over_type_param(type_param, func)) }) - || body.iter().any(|stmt| any_over_stmt(stmt, func)) - || decorator_list + || node.body.iter().any(|stmt| any_over_stmt(stmt, func)) + || node + .decorator_list .iter() .any(|decorator| any_over_expr(&decorator.expression, func)) } - Stmt::Return(ast::StmtReturn { - value, - range: _, - node_index: _, - }) => value + Stmt::Return(node) => node + .value .as_ref() .is_some_and(|value| any_over_expr(value, func)), - Stmt::Delete(ast::StmtDelete { - targets, - range: _, - node_index: _, - }) => targets.iter().any(|expr| any_over_expr(expr, func)), - Stmt::TypeAlias(ast::StmtTypeAlias { - name, - type_params, - value, - .. - }) => { - any_over_expr(name, func) - || type_params.as_ref().is_some_and(|type_params| { + Stmt::Delete(node) => node.targets.iter().any(|expr| any_over_expr(expr, func)), + Stmt::TypeAlias(node) => { + any_over_expr(&node.name, func) + || node.type_params.as_ref().is_some_and(|type_params| { type_params .iter() .any(|type_param| any_over_type_param(type_param, func)) }) - || any_over_expr(value, func) + || any_over_expr(&node.value, func) } - Stmt::Assign(ast::StmtAssign { targets, value, .. }) => { - targets.iter().any(|expr| any_over_expr(expr, func)) || any_over_expr(value, func) + Stmt::Assign(node) => { + node.targets.iter().any(|expr| any_over_expr(expr, func)) + || any_over_expr(&node.value, func) } - Stmt::AugAssign(ast::StmtAugAssign { target, value, .. }) => { - any_over_expr(target, func) || any_over_expr(value, func) + Stmt::AugAssign(node) => { + any_over_expr(&node.target, func) || any_over_expr(&node.value, func) } - Stmt::AnnAssign(ast::StmtAnnAssign { - target, - annotation, - value, - .. - }) => { - any_over_expr(target, func) - || any_over_expr(annotation, func) - || value + Stmt::AnnAssign(node) => { + any_over_expr(&node.target, func) + || any_over_expr(&node.annotation, func) + || node + .value .as_ref() .is_some_and(|value| any_over_expr(value, func)) } - Stmt::For(ast::StmtFor { - target, - iter, - body, - orelse, - .. - }) => { - any_over_expr(target, func) - || any_over_expr(iter, func) - || any_over_body(body, func) - || any_over_body(orelse, func) + Stmt::For(node) => { + any_over_expr(&node.target, func) + || any_over_expr(&node.iter, func) + || any_over_body(&node.body, func) + || any_over_body(&node.orelse, func) } - Stmt::While(ast::StmtWhile { - test, - body, - orelse, - range: _, - node_index: _, - }) => any_over_expr(test, func) || any_over_body(body, func) || any_over_body(orelse, func), - Stmt::If(ast::StmtIf { - test, - body, - elif_else_clauses, - range: _, - node_index: _, - }) => { - any_over_expr(test, func) - || any_over_body(body, func) - || elif_else_clauses.iter().any(|clause| { + Stmt::While(node) => { + any_over_expr(&node.test, func) + || any_over_body(&node.body, func) + || any_over_body(&node.orelse, func) + } + Stmt::If(node) => { + any_over_expr(&node.test, func) + || any_over_body(&node.body, func) + || node.elif_else_clauses.iter().any(|clause| { clause .test .as_ref() @@ -526,37 +487,27 @@ pub fn any_over_stmt(stmt: &Stmt, func: &dyn Fn(&Expr) -> bool) -> bool { || any_over_body(&clause.body, func) }) } - Stmt::With(ast::StmtWith { items, body, .. }) => { - items.iter().any(|with_item| { + Stmt::With(node) => { + node.items.iter().any(|with_item| { any_over_expr(&with_item.context_expr, func) || with_item .optional_vars .as_ref() .is_some_and(|expr| any_over_expr(expr, func)) - }) || any_over_body(body, func) + }) || any_over_body(&node.body, func) } - Stmt::Raise(ast::StmtRaise { - exc, - cause, - range: _, - node_index: _, - }) => { - exc.as_ref().is_some_and(|value| any_over_expr(value, func)) - || cause + Stmt::Raise(node) => { + node.exc + .as_ref() + .is_some_and(|value| any_over_expr(value, func)) + || node + .cause .as_ref() .is_some_and(|value| any_over_expr(value, func)) } - Stmt::Try(ast::StmtTry { - body, - handlers, - orelse, - finalbody, - is_star: _, - range: _, - node_index: _, - }) => { - any_over_body(body, func) - || handlers.iter().any(|handler| { + Stmt::Try(node) => { + any_over_body(&node.body, func) + || node.handlers.iter().any(|handler| { let ExceptHandler::ExceptHandler(ast::ExceptHandlerExceptHandler { type_, body, @@ -565,26 +516,19 @@ pub fn any_over_stmt(stmt: &Stmt, func: &dyn Fn(&Expr) -> bool) -> bool { type_.as_ref().is_some_and(|expr| any_over_expr(expr, func)) || any_over_body(body, func) }) - || any_over_body(orelse, func) - || any_over_body(finalbody, func) + || any_over_body(&node.orelse, func) + || any_over_body(&node.finalbody, func) } - Stmt::Assert(ast::StmtAssert { - test, - msg, - range: _, - node_index: _, - }) => { - any_over_expr(test, func) - || msg.as_ref().is_some_and(|value| any_over_expr(value, func)) + Stmt::Assert(node) => { + any_over_expr(&node.test, func) + || node + .msg + .as_ref() + .is_some_and(|value| any_over_expr(value, func)) } - Stmt::Match(ast::StmtMatch { - subject, - cases, - range: _, - node_index: _, - }) => { - any_over_expr(subject, func) - || cases.iter().any(|case| { + Stmt::Match(node) => { + any_over_expr(&node.subject, func) + || node.cases.iter().any(|case| { let MatchCase { pattern, guard, @@ -601,11 +545,7 @@ pub fn any_over_stmt(stmt: &Stmt, func: &dyn Fn(&Expr) -> bool) -> bool { Stmt::ImportFrom(_) => false, Stmt::Global(_) => false, Stmt::Nonlocal(_) => false, - Stmt::Expr(ast::StmtExpr { - value, - range: _, - node_index: _, - }) => any_over_expr(value, func), + Stmt::Expr(node) => any_over_expr(&node.value, func), Stmt::Pass(_) | Stmt::Break(_) | Stmt::Continue(_) => false, Stmt::IpyEscapeCommand(_) => false, } @@ -631,15 +571,15 @@ pub fn is_assignment_to_a_dunder(stmt: &Stmt) -> bool { // Check whether it's an assignment to a dunder, with or without a type // annotation. This is what pycodestyle (as of 2.9.1) does. match stmt { - Stmt::Assign(ast::StmtAssign { targets, .. }) => { - if let [Expr::Name(ast::ExprName { id, .. })] = targets.as_slice() { + Stmt::Assign(node) => { + if let [Expr::Name(ast::ExprName { id, .. })] = node.targets.as_slice() { is_dunder(id) } else { false } } - Stmt::AnnAssign(ast::StmtAnnAssign { target, .. }) => { - if let Expr::Name(ast::ExprName { id, .. }) = target.as_ref() { + Stmt::AnnAssign(node) => { + if let Expr::Name(ast::ExprName { id, .. }) = node.target.as_ref() { is_dunder(id) } else { false @@ -1021,33 +961,31 @@ pub struct RaiseStatementVisitor<'a> { impl<'a> StatementVisitor<'a> for RaiseStatementVisitor<'a> { fn visit_stmt(&mut self, stmt: &'a Stmt) { match stmt { - Stmt::Raise(ast::StmtRaise { - exc, - cause, - range: _, - node_index: _, - }) => { - self.raises - .push((stmt.range(), exc.as_deref(), cause.as_deref())); + Stmt::Raise(node) => { + self.raises.push(( + stmt.range(), + node.exc.as_deref(), + node.cause.as_deref(), + )); } Stmt::ClassDef(_) | Stmt::FunctionDef(_) | Stmt::Try(_) => {} - Stmt::If(ast::StmtIf { - body, - elif_else_clauses, - .. - }) => { - crate::statement_visitor::walk_body(self, body); - for clause in elif_else_clauses { + Stmt::If(node) => { + crate::statement_visitor::walk_body(self, &node.body); + for clause in &node.elif_else_clauses { self.visit_elif_else_clause(clause); } } - Stmt::While(ast::StmtWhile { body, .. }) - | Stmt::With(ast::StmtWith { body, .. }) - | Stmt::For(ast::StmtFor { body, .. }) => { - crate::statement_visitor::walk_body(self, body); + Stmt::While(node) => { + crate::statement_visitor::walk_body(self, &node.body); } - Stmt::Match(ast::StmtMatch { cases, .. }) => { - for case in cases { + Stmt::With(node) => { + crate::statement_visitor::walk_body(self, &node.body); + } + Stmt::For(node) => { + crate::statement_visitor::walk_body(self, &node.body); + } + Stmt::Match(node) => { + for case in &node.cases { crate::statement_visitor::walk_body(self, &case.body); } } @@ -1066,10 +1004,10 @@ impl Visitor<'_> for AwaitVisitor { fn visit_stmt(&mut self, stmt: &Stmt) { match stmt { Stmt::FunctionDef(_) | Stmt::ClassDef(_) => (), - Stmt::With(ast::StmtWith { is_async: true, .. }) => { + Stmt::With(node) if node.is_async => { self.seen_await = true; } - Stmt::For(ast::StmtFor { is_async: true, .. }) => { + Stmt::For(node) if node.is_async => { self.seen_await = true; } _ => crate::visitor::walk_stmt(self, stmt), @@ -1095,13 +1033,8 @@ impl Visitor<'_> for AwaitVisitor { /// Return `true` if a `Stmt` is a docstring. pub fn is_docstring_stmt(stmt: &Stmt) -> bool { - if let Stmt::Expr(ast::StmtExpr { - value, - range: _, - node_index: _, - }) = stmt - { - value.is_string_literal_expr() + if let Stmt::Expr(node) = stmt { + node.value.is_string_literal_expr() } else { false } @@ -1113,13 +1046,8 @@ pub fn on_conditional_branch<'a>(parents: &mut impl Iterator) - if matches!(parent, Stmt::If(_) | Stmt::While(_) | Stmt::Match(_)) { return true; } - if let Stmt::Expr(ast::StmtExpr { - value, - range: _, - node_index: _, - }) = parent - { - if value.is_if_expr() { + if let Stmt::Expr(node) = parent { + if node.value.is_if_expr() { return true; } } @@ -1140,7 +1068,7 @@ pub fn in_nested_block<'a>(mut parents: impl Iterator) -> bool /// Check if a node represents an unpacking assignment. pub fn is_unpacking_assignment(parent: &Stmt, child: &Expr) -> bool { match parent { - Stmt::With(ast::StmtWith { items, .. }) => items.iter().any(|item| { + Stmt::With(node) => node.items.iter().any(|item| { if let Some(optional_vars) = &item.optional_vars { if optional_vars.is_tuple_expr() { if any_over_expr(optional_vars, &|expr| expr == child) { @@ -1150,22 +1078,23 @@ pub fn is_unpacking_assignment(parent: &Stmt, child: &Expr) -> bool { } false }), - Stmt::Assign(ast::StmtAssign { targets, value, .. }) => { + Stmt::Assign(node) => { // In `(a, b) = (1, 2)`, `(1, 2)` is the target, and it is a tuple. let value_is_tuple = matches!( - value.as_ref(), + node.value.as_ref(), Expr::Set(_) | Expr::List(_) | Expr::Tuple(_) ); // In `(a, b) = coords = (1, 2)`, `(a, b)` and `coords` are the targets, and // `(a, b)` is a tuple. (We use "tuple" as a placeholder for any // unpackable expression.) - let targets_are_tuples = targets + let targets_are_tuples = node + .targets .iter() .all(|item| matches!(item, Expr::Set(_) | Expr::List(_) | Expr::Tuple(_))); // If we're looking at `a` in `(a, b) = coords = (1, 2)`, then we should // identify that the current expression is in a tuple. let child_in_tuple = targets_are_tuples - || targets.iter().any(|item| { + || node.targets.iter().any(|item| { matches!(item, Expr::Set(_) | Expr::List(_) | Expr::Tuple(_)) && any_over_expr(item, &|expr| expr == child) }); @@ -1748,7 +1677,7 @@ mod tests { default: Some(Box::new(constant_two.clone())), name: Identifier::new("x", TextRange::default()), }); - let type_alias = Stmt::TypeAlias(StmtTypeAlias { + let type_alias = Stmt::TypeAlias(Box::new(StmtTypeAlias { name: Box::new(name.clone()), type_params: Some(Box::new(TypeParams { type_params: vec![type_var_one, type_var_two], @@ -1758,7 +1687,7 @@ mod tests { value: Box::new(constant_three.clone()), range: TextRange::default(), node_index: AtomicNodeIndex::NONE, - }); + })); assert!(!any_over_stmt(&type_alias, &|expr| { seen.borrow_mut().push(expr.clone()); false diff --git a/crates/ruff_python_ast/src/identifier.rs b/crates/ruff_python_ast/src/identifier.rs index c8a54cbadf..0fd08e5b1a 100644 --- a/crates/ruff_python_ast/src/identifier.rs +++ b/crates/ruff_python_ast/src/identifier.rs @@ -112,10 +112,10 @@ pub fn except(handler: &ExceptHandler, source: &str) -> TextRange { /// Return the [`TextRange`] of the `else` token in a `For` or `While` statement. pub fn else_(stmt: &Stmt, source: &str) -> Option { - let (Stmt::For(ast::StmtFor { body, orelse, .. }) - | Stmt::While(ast::StmtWhile { body, orelse, .. })) = stmt - else { - return None; + let (body, orelse) = match stmt { + Stmt::For(node) => (&node.body, &node.orelse), + Stmt::While(node) => (&node.body, &node.orelse), + _ => return None, }; if orelse.is_empty() { diff --git a/crates/ruff_python_ast/src/nodes.rs b/crates/ruff_python_ast/src/nodes.rs index 2ea2701f82..06719f113b 100644 --- a/crates/ruff_python_ast/src/nodes.rs +++ b/crates/ruff_python_ast/src/nodes.rs @@ -3626,7 +3626,9 @@ mod tests { #[test] #[cfg(target_pointer_width = "64")] fn size() { - assert_eq!(std::mem::size_of::(), 128); + // Stmt variants are boxed to reduce the enum size from 128 to 32 bytes. + // Only Return, Expr, Pass, Break, Continue remain unboxed. + assert_eq!(std::mem::size_of::(), 32); assert_eq!(std::mem::size_of::(), 128); assert_eq!(std::mem::size_of::(), 120); assert_eq!(std::mem::size_of::(), 112); diff --git a/crates/ruff_python_ast/src/statement_visitor.rs b/crates/ruff_python_ast/src/statement_visitor.rs index 7ab3ebe06c..3bad456279 100644 --- a/crates/ruff_python_ast/src/statement_visitor.rs +++ b/crates/ruff_python_ast/src/statement_visitor.rs @@ -29,51 +29,41 @@ pub fn walk_body<'a, V: StatementVisitor<'a> + ?Sized>(visitor: &mut V, body: &' pub fn walk_stmt<'a, V: StatementVisitor<'a> + ?Sized>(visitor: &mut V, stmt: &'a Stmt) { match stmt { - Stmt::FunctionDef(ast::StmtFunctionDef { body, .. }) => { - visitor.visit_body(body); + Stmt::FunctionDef(node) => { + visitor.visit_body(&node.body); } - Stmt::For(ast::StmtFor { body, orelse, .. }) => { - visitor.visit_body(body); - visitor.visit_body(orelse); + Stmt::For(node) => { + visitor.visit_body(&node.body); + visitor.visit_body(&node.orelse); } - Stmt::ClassDef(ast::StmtClassDef { body, .. }) => { - visitor.visit_body(body); + Stmt::ClassDef(node) => { + visitor.visit_body(&node.body); } - Stmt::While(ast::StmtWhile { body, orelse, .. }) => { - visitor.visit_body(body); - visitor.visit_body(orelse); + Stmt::While(node) => { + visitor.visit_body(&node.body); + visitor.visit_body(&node.orelse); } - Stmt::If(ast::StmtIf { - body, - elif_else_clauses, - .. - }) => { - visitor.visit_body(body); - for clause in elif_else_clauses { + Stmt::If(node) => { + visitor.visit_body(&node.body); + for clause in &node.elif_else_clauses { visitor.visit_elif_else_clause(clause); } } - Stmt::With(ast::StmtWith { body, .. }) => { - visitor.visit_body(body); + Stmt::With(node) => { + visitor.visit_body(&node.body); } - Stmt::Match(ast::StmtMatch { cases, .. }) => { - for match_case in cases { + Stmt::Match(node) => { + for match_case in &node.cases { visitor.visit_match_case(match_case); } } - Stmt::Try(ast::StmtTry { - body, - handlers, - orelse, - finalbody, - .. - }) => { - visitor.visit_body(body); - for except_handler in handlers { + Stmt::Try(node) => { + visitor.visit_body(&node.body); + for except_handler in &node.handlers { visitor.visit_except_handler(except_handler); } - visitor.visit_body(orelse); - visitor.visit_body(finalbody); + visitor.visit_body(&node.orelse); + visitor.visit_body(&node.finalbody); } _ => {} } diff --git a/crates/ruff_python_ast/src/traversal.rs b/crates/ruff_python_ast/src/traversal.rs index 1803042e77..8f77cd9667 100644 --- a/crates/ruff_python_ast/src/traversal.rs +++ b/crates/ruff_python_ast/src/traversal.rs @@ -1,41 +1,32 @@ //! Utilities for manually traversing a Python AST. -use crate::{self as ast, AnyNodeRef, ExceptHandler, Stmt}; +use crate::{AnyNodeRef, ExceptHandler, Stmt}; /// Given a [`Stmt`] and its parent, return the [`ast::Suite`] that contains the [`Stmt`]. pub fn suite<'a>(stmt: &'a Stmt, parent: &'a Stmt) -> Option> { // TODO: refactor this to work without a parent, ie when `stmt` is at the top level match parent { - Stmt::FunctionDef(ast::StmtFunctionDef { body, .. }) => EnclosingSuite::new(body, stmt), - Stmt::ClassDef(ast::StmtClassDef { body, .. }) => EnclosingSuite::new(body, stmt), - Stmt::For(ast::StmtFor { body, orelse, .. }) => [body, orelse] + Stmt::FunctionDef(node) => EnclosingSuite::new(&node.body, stmt), + Stmt::ClassDef(node) => EnclosingSuite::new(&node.body, stmt), + Stmt::For(node) => [&node.body, &node.orelse] .iter() .find_map(|suite| EnclosingSuite::new(suite, stmt)), - Stmt::While(ast::StmtWhile { body, orelse, .. }) => [body, orelse] + Stmt::While(node) => [&node.body, &node.orelse] .iter() .find_map(|suite| EnclosingSuite::new(suite, stmt)), - Stmt::If(ast::StmtIf { - body, - elif_else_clauses, - .. - }) => [body] + Stmt::If(node) => [&node.body] .into_iter() - .chain(elif_else_clauses.iter().map(|clause| &clause.body)) + .chain(node.elif_else_clauses.iter().map(|clause| &clause.body)) .find_map(|suite| EnclosingSuite::new(suite, stmt)), - Stmt::With(ast::StmtWith { body, .. }) => EnclosingSuite::new(body, stmt), - Stmt::Match(ast::StmtMatch { cases, .. }) => cases + Stmt::With(node) => EnclosingSuite::new(&node.body, stmt), + Stmt::Match(node) => node + .cases .iter() .map(|case| &case.body) .find_map(|body| EnclosingSuite::new(body, stmt)), - Stmt::Try(ast::StmtTry { - body, - handlers, - orelse, - finalbody, - .. - }) => [body, orelse, finalbody] + Stmt::Try(node) => [&node.body, &node.orelse, &node.finalbody] .into_iter() .chain( - handlers + node.handlers .iter() .filter_map(ExceptHandler::as_except_handler) .map(|handler| &handler.body), diff --git a/crates/ruff_python_ast/src/visitor.rs b/crates/ruff_python_ast/src/visitor.rs index 425c317dc3..aef326f251 100644 --- a/crates/ruff_python_ast/src/visitor.rs +++ b/crates/ruff_python_ast/src/visitor.rs @@ -134,43 +134,30 @@ pub fn walk_elif_else_clause<'a, V: Visitor<'a> + ?Sized>( pub fn walk_stmt<'a, V: Visitor<'a> + ?Sized>(visitor: &mut V, stmt: &'a Stmt) { match stmt { - Stmt::FunctionDef(ast::StmtFunctionDef { - parameters, - body, - decorator_list, - returns, - type_params, - .. - }) => { - for decorator in decorator_list { + Stmt::FunctionDef(node) => { + for decorator in &node.decorator_list { visitor.visit_decorator(decorator); } - if let Some(type_params) = type_params { + if let Some(type_params) = &node.type_params { visitor.visit_type_params(type_params); } - visitor.visit_parameters(parameters); - if let Some(expr) = returns { + visitor.visit_parameters(&node.parameters); + if let Some(expr) = &node.returns { visitor.visit_annotation(expr); } - visitor.visit_body(body); + visitor.visit_body(&node.body); } - Stmt::ClassDef(ast::StmtClassDef { - arguments, - body, - decorator_list, - type_params, - .. - }) => { - for decorator in decorator_list { + Stmt::ClassDef(node) => { + for decorator in &node.decorator_list { visitor.visit_decorator(decorator); } - if let Some(type_params) = type_params { + if let Some(type_params) = &node.type_params { visitor.visit_type_params(type_params); } - if let Some(arguments) = arguments { + if let Some(arguments) = &node.arguments { visitor.visit_arguments(arguments); } - visitor.visit_body(body); + visitor.visit_body(&node.body); } Stmt::Return(ast::StmtReturn { value, @@ -181,87 +168,99 @@ pub fn walk_stmt<'a, V: Visitor<'a> + ?Sized>(visitor: &mut V, stmt: &'a Stmt) { visitor.visit_expr(expr); } } - Stmt::Delete(ast::StmtDelete { - targets, - range: _, - node_index: _, - }) => { + Stmt::Delete(node) => { + let ast::StmtDelete { + targets, + range: _, + node_index: _, + } = &**node; for expr in targets { visitor.visit_expr(expr); } } - Stmt::TypeAlias(ast::StmtTypeAlias { - range: _, - node_index: _, - name, - type_params, - value, - }) => { + Stmt::TypeAlias(node) => { + let ast::StmtTypeAlias { + range: _, + node_index: _, + name, + type_params, + value, + } = &**node; visitor.visit_expr(value); if let Some(type_params) = type_params { visitor.visit_type_params(type_params); } visitor.visit_expr(name); } - Stmt::Assign(ast::StmtAssign { targets, value, .. }) => { + Stmt::Assign(node) => { + let ast::StmtAssign { targets, value, range: _, node_index: _ } = &**node; visitor.visit_expr(value); for expr in targets { visitor.visit_expr(expr); } } - Stmt::AugAssign(ast::StmtAugAssign { - target, - op, - value, - range: _, - node_index: _, - }) => { + Stmt::AugAssign(node) => { + let ast::StmtAugAssign { + target, + op, + value, + range: _, + node_index: _, + } = &**node; visitor.visit_expr(value); visitor.visit_operator(op); visitor.visit_expr(target); } - Stmt::AnnAssign(ast::StmtAnnAssign { - target, - annotation, - value, - .. - }) => { + Stmt::AnnAssign(node) => { + let ast::StmtAnnAssign { + target, + annotation, + value, + simple: _, + range: _, + node_index: _, + } = &**node; if let Some(expr) = value { visitor.visit_expr(expr); } visitor.visit_annotation(annotation); visitor.visit_expr(target); } - Stmt::For(ast::StmtFor { - target, - iter, - body, - orelse, - .. - }) => { + Stmt::For(node) => { + let ast::StmtFor { + target, + iter, + body, + orelse, + is_async: _, + range: _, + node_index: _, + } = &**node; visitor.visit_expr(iter); visitor.visit_expr(target); visitor.visit_body(body); visitor.visit_body(orelse); } - Stmt::While(ast::StmtWhile { - test, - body, - orelse, - range: _, - node_index: _, - }) => { + Stmt::While(node) => { + let ast::StmtWhile { + test, + body, + orelse, + range: _, + node_index: _, + } = &**node; visitor.visit_expr(test); visitor.visit_body(body); visitor.visit_body(orelse); } - Stmt::If(ast::StmtIf { - test, - body, - elif_else_clauses, - range: _, - node_index: _, - }) => { + Stmt::If(node) => { + let ast::StmtIf { + test, + body, + elif_else_clauses, + range: _, + node_index: _, + } = &**node; visitor.visit_expr(test); visitor.visit_body(body); for clause in elif_else_clauses { @@ -271,29 +270,32 @@ pub fn walk_stmt<'a, V: Visitor<'a> + ?Sized>(visitor: &mut V, stmt: &'a Stmt) { walk_elif_else_clause(visitor, clause); } } - Stmt::With(ast::StmtWith { items, body, .. }) => { + Stmt::With(node) => { + let ast::StmtWith { items, body, is_async: _, range: _, node_index: _ } = &**node; for with_item in items { visitor.visit_with_item(with_item); } visitor.visit_body(body); } - Stmt::Match(ast::StmtMatch { - subject, - cases, - range: _, - node_index: _, - }) => { + Stmt::Match(node) => { + let ast::StmtMatch { + subject, + cases, + range: _, + node_index: _, + } = &**node; visitor.visit_expr(subject); for match_case in cases { visitor.visit_match_case(match_case); } } - Stmt::Raise(ast::StmtRaise { - exc, - cause, - range: _, - node_index: _, - }) => { + Stmt::Raise(node) => { + let ast::StmtRaise { + exc, + cause, + range: _, + node_index: _, + } = &**node; if let Some(expr) = exc { visitor.visit_expr(expr); } @@ -301,15 +303,16 @@ pub fn walk_stmt<'a, V: Visitor<'a> + ?Sized>(visitor: &mut V, stmt: &'a Stmt) { visitor.visit_expr(expr); } } - Stmt::Try(ast::StmtTry { - body, - handlers, - orelse, - finalbody, - is_star: _, - range: _, - node_index: _, - }) => { + Stmt::Try(node) => { + let ast::StmtTry { + body, + handlers, + orelse, + finalbody, + is_star: _, + range: _, + node_index: _, + } = &**node; visitor.visit_body(body); for except_handler in handlers { visitor.visit_except_handler(except_handler); @@ -317,27 +320,30 @@ pub fn walk_stmt<'a, V: Visitor<'a> + ?Sized>(visitor: &mut V, stmt: &'a Stmt) { visitor.visit_body(orelse); visitor.visit_body(finalbody); } - Stmt::Assert(ast::StmtAssert { - test, - msg, - range: _, - node_index: _, - }) => { + Stmt::Assert(node) => { + let ast::StmtAssert { + test, + msg, + range: _, + node_index: _, + } = &**node; visitor.visit_expr(test); if let Some(expr) = msg { visitor.visit_expr(expr); } } - Stmt::Import(ast::StmtImport { - names, - range: _, - node_index: _, - }) => { + Stmt::Import(node) => { + let ast::StmtImport { + names, + range: _, + node_index: _, + } = &**node; for alias in names { visitor.visit_alias(alias); } } - Stmt::ImportFrom(ast::StmtImportFrom { names, .. }) => { + Stmt::ImportFrom(node) => { + let ast::StmtImportFrom { names, module: _, level: _, range: _, node_index: _ } = &**node; for alias in names { visitor.visit_alias(alias); } @@ -348,7 +354,9 @@ pub fn walk_stmt<'a, V: Visitor<'a> + ?Sized>(visitor: &mut V, stmt: &'a Stmt) { value, range: _, node_index: _, - }) => visitor.visit_expr(value), + }) => { + visitor.visit_expr(value); + } Stmt::Pass(_) | Stmt::Break(_) | Stmt::Continue(_) | Stmt::IpyEscapeCommand(_) => {} } } diff --git a/crates/ruff_python_ast/src/visitor/transformer.rs b/crates/ruff_python_ast/src/visitor/transformer.rs index 3a526c0471..1ceb1903c5 100644 --- a/crates/ruff_python_ast/src/visitor/transformer.rs +++ b/crates/ruff_python_ast/src/visitor/transformer.rs @@ -121,43 +121,30 @@ pub fn walk_elif_else_clause( pub fn walk_stmt(visitor: &V, stmt: &mut Stmt) { match stmt { - Stmt::FunctionDef(ast::StmtFunctionDef { - parameters, - body, - decorator_list, - returns, - type_params, - .. - }) => { - for decorator in decorator_list { + Stmt::FunctionDef(node) => { + for decorator in &mut node.decorator_list { visitor.visit_decorator(decorator); } - if let Some(type_params) = type_params { + if let Some(type_params) = &mut node.type_params { visitor.visit_type_params(type_params); } - visitor.visit_parameters(parameters); - if let Some(expr) = returns { + visitor.visit_parameters(&mut node.parameters); + if let Some(expr) = &mut node.returns { visitor.visit_annotation(expr); } - visitor.visit_body(body); + visitor.visit_body(&mut node.body); } - Stmt::ClassDef(ast::StmtClassDef { - arguments, - body, - decorator_list, - type_params, - .. - }) => { - for decorator in decorator_list { + Stmt::ClassDef(node) => { + for decorator in &mut node.decorator_list { visitor.visit_decorator(decorator); } - if let Some(type_params) = type_params { + if let Some(type_params) = &mut node.type_params { visitor.visit_type_params(type_params); } - if let Some(arguments) = arguments { + if let Some(arguments) = &mut node.arguments { visitor.visit_arguments(arguments); } - visitor.visit_body(body); + visitor.visit_body(&mut node.body); } Stmt::Return(ast::StmtReturn { value, @@ -168,116 +155,131 @@ pub fn walk_stmt(visitor: &V, stmt: &mut Stmt) { visitor.visit_expr(expr); } } - Stmt::Delete(ast::StmtDelete { - targets, - range: _, - node_index: _, - }) => { + Stmt::Delete(node) => { + let ast::StmtDelete { + targets, + range: _, + node_index: _, + } = &mut **node; for expr in targets { visitor.visit_expr(expr); } } - Stmt::TypeAlias(ast::StmtTypeAlias { - range: _, - node_index: _, - name, - type_params, - value, - }) => { + Stmt::TypeAlias(node) => { + let ast::StmtTypeAlias { + range: _, + node_index: _, + name, + type_params, + value, + } = &mut **node; visitor.visit_expr(value); if let Some(type_params) = type_params { visitor.visit_type_params(type_params); } visitor.visit_expr(name); } - Stmt::Assign(ast::StmtAssign { targets, value, .. }) => { + Stmt::Assign(node) => { + let ast::StmtAssign { targets, value, range: _, node_index: _ } = &mut **node; visitor.visit_expr(value); for expr in targets { visitor.visit_expr(expr); } } - Stmt::AugAssign(ast::StmtAugAssign { - target, - op, - value, - range: _, - node_index: _, - }) => { + Stmt::AugAssign(node) => { + let ast::StmtAugAssign { + target, + op, + value, + range: _, + node_index: _, + } = &mut **node; visitor.visit_expr(value); visitor.visit_operator(op); visitor.visit_expr(target); } - Stmt::AnnAssign(ast::StmtAnnAssign { - target, - annotation, - value, - .. - }) => { + Stmt::AnnAssign(node) => { + let ast::StmtAnnAssign { + target, + annotation, + value, + simple: _, + range: _, + node_index: _, + } = &mut **node; if let Some(expr) = value { visitor.visit_expr(expr); } visitor.visit_annotation(annotation); visitor.visit_expr(target); } - Stmt::For(ast::StmtFor { - target, - iter, - body, - orelse, - .. - }) => { + Stmt::For(node) => { + let ast::StmtFor { + target, + iter, + body, + orelse, + is_async: _, + range: _, + node_index: _, + } = &mut **node; visitor.visit_expr(iter); visitor.visit_expr(target); visitor.visit_body(body); visitor.visit_body(orelse); } - Stmt::While(ast::StmtWhile { - test, - body, - orelse, - range: _, - node_index: _, - }) => { + Stmt::While(node) => { + let ast::StmtWhile { + test, + body, + orelse, + range: _, + node_index: _, + } = &mut **node; visitor.visit_expr(test); visitor.visit_body(body); visitor.visit_body(orelse); } - Stmt::If(ast::StmtIf { - test, - body, - elif_else_clauses, - range: _, - node_index: _, - }) => { + Stmt::If(node) => { + let ast::StmtIf { + test, + body, + elif_else_clauses, + range: _, + node_index: _, + } = &mut **node; visitor.visit_expr(test); visitor.visit_body(body); for clause in elif_else_clauses { walk_elif_else_clause(visitor, clause); } } - Stmt::With(ast::StmtWith { items, body, .. }) => { + Stmt::With(node) => { + let ast::StmtWith { items, body, is_async: _, range: _, node_index: _ } = &mut **node; for with_item in items { visitor.visit_with_item(with_item); } visitor.visit_body(body); } - Stmt::Match(ast::StmtMatch { - subject, - cases, - range: _, - node_index: _, - }) => { + Stmt::Match(node) => { + let ast::StmtMatch { + subject, + cases, + range: _, + node_index: _, + } = &mut **node; visitor.visit_expr(subject); for match_case in cases { visitor.visit_match_case(match_case); } } - Stmt::Raise(ast::StmtRaise { - exc, - cause, - range: _, - node_index: _, - }) => { + Stmt::Raise(node) => { + let ast::StmtRaise { + exc, + cause, + range: _, + node_index: _, + } = &mut **node; if let Some(expr) = exc { visitor.visit_expr(expr); } @@ -285,15 +287,16 @@ pub fn walk_stmt(visitor: &V, stmt: &mut Stmt) { visitor.visit_expr(expr); } } - Stmt::Try(ast::StmtTry { - body, - handlers, - orelse, - finalbody, - is_star: _, - range: _, - node_index: _, - }) => { + Stmt::Try(node) => { + let ast::StmtTry { + body, + handlers, + orelse, + finalbody, + is_star: _, + range: _, + node_index: _, + } = &mut **node; visitor.visit_body(body); for except_handler in handlers { visitor.visit_except_handler(except_handler); @@ -301,27 +304,30 @@ pub fn walk_stmt(visitor: &V, stmt: &mut Stmt) { visitor.visit_body(orelse); visitor.visit_body(finalbody); } - Stmt::Assert(ast::StmtAssert { - test, - msg, - range: _, - node_index: _, - }) => { + Stmt::Assert(node) => { + let ast::StmtAssert { + test, + msg, + range: _, + node_index: _, + } = &mut **node; visitor.visit_expr(test); if let Some(expr) = msg { visitor.visit_expr(expr); } } - Stmt::Import(ast::StmtImport { - names, - range: _, - node_index: _, - }) => { + Stmt::Import(node) => { + let ast::StmtImport { + names, + range: _, + node_index: _, + } = &mut **node; for alias in names { visitor.visit_alias(alias); } } - Stmt::ImportFrom(ast::StmtImportFrom { names, .. }) => { + Stmt::ImportFrom(node) => { + let ast::StmtImportFrom { names, module: _, level: _, range: _, node_index: _ } = &mut **node; for alias in names { visitor.visit_alias(alias); } @@ -332,7 +338,9 @@ pub fn walk_stmt(visitor: &V, stmt: &mut Stmt) { value, range: _, node_index: _, - }) => visitor.visit_expr(value), + }) => { + visitor.visit_expr(value); + } Stmt::Pass(_) | Stmt::Break(_) | Stmt::Continue(_) | Stmt::IpyEscapeCommand(_) => {} } } diff --git a/crates/ruff_python_codegen/src/generator.rs b/crates/ruff_python_codegen/src/generator.rs index 710e295f62..4c58c42d9e 100644 --- a/crates/ruff_python_codegen/src/generator.rs +++ b/crates/ruff_python_codegen/src/generator.rs @@ -256,55 +256,47 @@ impl<'a> Generator<'a> { } match ast { - Stmt::FunctionDef(ast::StmtFunctionDef { - is_async, - name, - parameters, - body, - returns, - decorator_list, - type_params, - .. - }) => { + Stmt::FunctionDef(node) => { self.newlines(if self.indent_depth == 0 { 2 } else { 1 }); - for decorator in decorator_list { + for decorator in &node.decorator_list { statement!({ self.p("@"); self.unparse_expr(&decorator.expression, precedence::MAX); }); } statement!({ - if *is_async { + if node.is_async { self.p("async "); } self.p("def "); - self.p_id(name); - if let Some(type_params) = type_params { + self.p_id(&node.name); + if let Some(type_params) = &node.type_params { self.unparse_type_params(type_params); } self.p("("); - self.unparse_parameters(parameters); + self.unparse_parameters(&node.parameters); self.p(")"); - if let Some(returns) = returns { + if let Some(returns) = &node.returns { self.p(" -> "); self.unparse_expr(returns, precedence::MAX); } self.p(":"); }); - self.body(body); + self.body(&node.body); if self.indent_depth == 0 { self.newlines(2); } } - Stmt::ClassDef(ast::StmtClassDef { - name, - arguments, - body, - decorator_list, - type_params, - range: _, - node_index: _, - }) => { + Stmt::ClassDef(node) => { + let ast::StmtClassDef { + name, + arguments, + body, + decorator_list, + type_params, + range: _, + node_index: _, + } = &**node; self.newlines(if self.indent_depth == 0 { 2 } else { 1 }); for decorator in decorator_list { statement!({ @@ -362,11 +354,12 @@ impl<'a> Generator<'a> { } }); } - Stmt::Delete(ast::StmtDelete { - targets, - range: _, - node_index: _, - }) => { + Stmt::Delete(node) => { + let ast::StmtDelete { + targets, + range: _, + node_index: _, + } = &**node; statement!({ self.p("del "); let mut first = true; @@ -376,22 +369,23 @@ impl<'a> Generator<'a> { } }); } - Stmt::Assign(ast::StmtAssign { targets, value, .. }) => { + Stmt::Assign(node) => { statement!({ - for target in targets { + for target in &node.targets { self.unparse_expr(target, precedence::ASSIGN); self.p(" = "); } - self.unparse_expr(value, precedence::ASSIGN); + self.unparse_expr(&node.value, precedence::ASSIGN); }); } - Stmt::AugAssign(ast::StmtAugAssign { - target, - op, - value, - range: _, - node_index: _, - }) => { + Stmt::AugAssign(node) => { + let ast::StmtAugAssign { + target, + op, + value, + range: _, + node_index: _, + } = &**node; statement!({ self.unparse_expr(target, precedence::AUG_ASSIGN); self.p(" "); @@ -414,14 +408,15 @@ impl<'a> Generator<'a> { self.unparse_expr(value, precedence::AUG_ASSIGN); }); } - Stmt::AnnAssign(ast::StmtAnnAssign { - target, - annotation, - value, - simple, - range: _, - node_index: _, - }) => { + Stmt::AnnAssign(node) => { + let ast::StmtAnnAssign { + target, + annotation, + value, + simple, + range: _, + node_index: _, + } = &**node; statement!({ let need_parens = matches!(target.as_ref(), Expr::Name(_)) && !simple; self.p_if(need_parens, "("); @@ -435,39 +430,33 @@ impl<'a> Generator<'a> { } }); } - Stmt::For(ast::StmtFor { - is_async, - target, - iter, - body, - orelse, - .. - }) => { + Stmt::For(node) => { statement!({ - if *is_async { + if node.is_async { self.p("async "); } self.p("for "); - self.unparse_expr(target, precedence::FOR); + self.unparse_expr(&node.target, precedence::FOR); self.p(" in "); - self.unparse_expr(iter, precedence::MAX); + self.unparse_expr(&node.iter, precedence::MAX); self.p(":"); }); - self.body(body); - if !orelse.is_empty() { + self.body(&node.body); + if !node.orelse.is_empty() { statement!({ self.p("else:"); }); - self.body(orelse); + self.body(&node.orelse); } } - Stmt::While(ast::StmtWhile { - test, - body, - orelse, - range: _, - node_index: _, - }) => { + Stmt::While(node) => { + let ast::StmtWhile { + test, + body, + orelse, + range: _, + node_index: _, + } = &**node; statement!({ self.p("while "); self.unparse_expr(test, precedence::WHILE); @@ -481,13 +470,14 @@ impl<'a> Generator<'a> { self.body(orelse); } } - Stmt::If(ast::StmtIf { - test, - body, - elif_else_clauses, - range: _, - node_index: _, - }) => { + Stmt::If(node) => { + let ast::StmtIf { + test, + body, + elif_else_clauses, + range: _, + node_index: _, + } = &**node; statement!({ self.p("if "); self.unparse_expr(test, precedence::IF); @@ -510,32 +500,28 @@ impl<'a> Generator<'a> { self.body(&clause.body); } } - Stmt::With(ast::StmtWith { - is_async, - items, - body, - .. - }) => { + Stmt::With(node) => { statement!({ - if *is_async { + if node.is_async { self.p("async "); } self.p("with "); let mut first = true; - for item in items { + for item in &node.items { self.p_delim(&mut first, ", "); self.unparse_with_item(item); } self.p(":"); }); - self.body(body); + self.body(&node.body); } - Stmt::Match(ast::StmtMatch { - subject, - cases, - range: _, - node_index: _, - }) => { + Stmt::Match(node) => { + let ast::StmtMatch { + subject, + cases, + range: _, + node_index: _, + } = &**node; statement!({ self.p("match "); self.unparse_expr(subject, precedence::MAX); @@ -549,13 +535,14 @@ impl<'a> Generator<'a> { self.indent_depth = self.indent_depth.saturating_sub(1); } } - Stmt::TypeAlias(ast::StmtTypeAlias { - name, - range: _, - node_index: _, - type_params, - value, - }) => { + Stmt::TypeAlias(node) => { + let ast::StmtTypeAlias { + name, + range: _, + node_index: _, + type_params, + value, + } = &**node; statement!({ self.p("type "); self.unparse_expr(name, precedence::MAX); @@ -566,12 +553,13 @@ impl<'a> Generator<'a> { self.unparse_expr(value, precedence::ASSIGN); }); } - Stmt::Raise(ast::StmtRaise { - exc, - cause, - range: _, - node_index: _, - }) => { + Stmt::Raise(node) => { + let ast::StmtRaise { + exc, + cause, + range: _, + node_index: _, + } = &**node; statement!({ self.p("raise"); if let Some(exc) = exc { @@ -584,15 +572,16 @@ impl<'a> Generator<'a> { } }); } - Stmt::Try(ast::StmtTry { - body, - handlers, - orelse, - finalbody, - is_star, - range: _, - node_index: _, - }) => { + Stmt::Try(node) => { + let ast::StmtTry { + body, + handlers, + orelse, + finalbody, + is_star, + range: _, + node_index: _, + } = &**node; statement!({ self.p("try:"); }); @@ -617,12 +606,13 @@ impl<'a> Generator<'a> { self.body(finalbody); } } - Stmt::Assert(ast::StmtAssert { - test, - msg, - range: _, - node_index: _, - }) => { + Stmt::Assert(node) => { + let ast::StmtAssert { + test, + msg, + range: _, + node_index: _, + } = &**node; statement!({ self.p("assert "); self.unparse_expr(test, precedence::ASSERT); @@ -632,11 +622,12 @@ impl<'a> Generator<'a> { } }); } - Stmt::Import(ast::StmtImport { - names, - range: _, - node_index: _, - }) => { + Stmt::Import(node) => { + let ast::StmtImport { + names, + range: _, + node_index: _, + } = &**node; statement!({ self.p("import "); let mut first = true; @@ -646,13 +637,14 @@ impl<'a> Generator<'a> { } }); } - Stmt::ImportFrom(ast::StmtImportFrom { - module, - names, - level, - range: _, - node_index: _, - }) => { + Stmt::ImportFrom(node) => { + let ast::StmtImportFrom { + module, + names, + level, + range: _, + node_index: _, + } = &**node; statement!({ self.p("from "); if *level > 0 { @@ -671,11 +663,12 @@ impl<'a> Generator<'a> { } }); } - Stmt::Global(ast::StmtGlobal { - names, - range: _, - node_index: _, - }) => { + Stmt::Global(node) => { + let ast::StmtGlobal { + names, + range: _, + node_index: _, + } = &**node; statement!({ self.p("global "); let mut first = true; @@ -685,11 +678,12 @@ impl<'a> Generator<'a> { } }); } - Stmt::Nonlocal(ast::StmtNonlocal { - names, - range: _, - node_index: _, - }) => { + Stmt::Nonlocal(node) => { + let ast::StmtNonlocal { + names, + range: _, + node_index: _, + } = &**node; statement!({ self.p("nonlocal "); let mut first = true; @@ -723,9 +717,9 @@ impl<'a> Generator<'a> { self.p("continue"); }); } - Stmt::IpyEscapeCommand(ast::StmtIpyEscapeCommand { kind, value, .. }) => { + Stmt::IpyEscapeCommand(node) => { statement!({ - self.p(&format!("{kind}{value}")); + self.p(&format!("{}{}", node.kind, node.value)); }); } } diff --git a/crates/ruff_python_parser/src/parser/statement.rs b/crates/ruff_python_parser/src/parser/statement.rs index 07e5816a9c..950c1fa8ca 100644 --- a/crates/ruff_python_parser/src/parser/statement.rs +++ b/crates/ruff_python_parser/src/parser/statement.rs @@ -114,13 +114,13 @@ impl<'src> Parser<'src> { let start = self.node_start(); match self.current_token_kind() { - TokenKind::If => Stmt::If(self.parse_if_statement()), - TokenKind::For => Stmt::For(self.parse_for_statement(start)), - TokenKind::While => Stmt::While(self.parse_while_statement()), - TokenKind::Def => Stmt::FunctionDef(self.parse_function_definition(vec![], start)), - TokenKind::Class => Stmt::ClassDef(self.parse_class_definition(vec![], start)), - TokenKind::Try => Stmt::Try(self.parse_try_statement()), - TokenKind::With => Stmt::With(self.parse_with_statement(start)), + TokenKind::If => self.parse_if_statement().into(), + TokenKind::For => self.parse_for_statement(start).into(), + TokenKind::While => self.parse_while_statement().into(), + TokenKind::Def => self.parse_function_definition(vec![], start).into(), + TokenKind::Class => self.parse_class_definition(vec![], start).into(), + TokenKind::Try => self.parse_try_statement().into(), + TokenKind::With => self.parse_with_statement(start).into(), TokenKind::At => self.parse_decorators(), TokenKind::Async => self.parse_async_statement(), token => { @@ -130,11 +130,11 @@ impl<'src> Parser<'src> { match self.classify_match_token() { MatchTokenKind::Keyword => { - return Stmt::Match(self.parse_match_statement()); + return self.parse_match_statement().into(); } MatchTokenKind::KeywordOrIdentifier => { if let Some(match_stmt) = self.try_parse_match_statement() { - return Stmt::Match(match_stmt); + return match_stmt.into(); } } MatchTokenKind::Identifier => {} @@ -262,19 +262,19 @@ impl<'src> Parser<'src> { /// See: fn parse_simple_statement(&mut self) -> Stmt { match self.current_token_kind() { - TokenKind::Return => Stmt::Return(self.parse_return_statement()), - TokenKind::Import => Stmt::Import(self.parse_import_statement()), - TokenKind::From => Stmt::ImportFrom(self.parse_from_import_statement()), - TokenKind::Pass => Stmt::Pass(self.parse_pass_statement()), - TokenKind::Continue => Stmt::Continue(self.parse_continue_statement()), - TokenKind::Break => Stmt::Break(self.parse_break_statement()), - TokenKind::Raise => Stmt::Raise(self.parse_raise_statement()), - TokenKind::Del => Stmt::Delete(self.parse_delete_statement()), - TokenKind::Assert => Stmt::Assert(self.parse_assert_statement()), - TokenKind::Global => Stmt::Global(self.parse_global_statement()), - TokenKind::Nonlocal => Stmt::Nonlocal(self.parse_nonlocal_statement()), + TokenKind::Return => self.parse_return_statement().into(), + TokenKind::Import => self.parse_import_statement().into(), + TokenKind::From => self.parse_from_import_statement().into(), + TokenKind::Pass => self.parse_pass_statement().into(), + TokenKind::Continue => self.parse_continue_statement().into(), + TokenKind::Break => self.parse_break_statement().into(), + TokenKind::Raise => self.parse_raise_statement().into(), + TokenKind::Del => self.parse_delete_statement().into(), + TokenKind::Assert => self.parse_assert_statement().into(), + TokenKind::Global => self.parse_global_statement().into(), + TokenKind::Nonlocal => self.parse_nonlocal_statement().into(), TokenKind::IpyEscapeCommand => { - Stmt::IpyEscapeCommand(self.parse_ipython_escape_command_statement()) + self.parse_ipython_escape_command_statement().into() } token => { if token == TokenKind::Type { @@ -285,7 +285,7 @@ impl<'src> Parser<'src> { if (first == TokenKind::Name || first.is_soft_keyword()) && matches!(second, TokenKind::Lsqb | TokenKind::Equal) { - return Stmt::TypeAlias(self.parse_type_alias_statement()); + return self.parse_type_alias_statement().into(); } } @@ -296,19 +296,17 @@ impl<'src> Parser<'src> { self.parse_expression_list(ExpressionContext::yield_or_starred_bitwise_or()); if self.at(TokenKind::Equal) { - Stmt::Assign(self.parse_assign_statement(parsed_expr, start)) + self.parse_assign_statement(parsed_expr, start).into() } else if self.at(TokenKind::Colon) { - Stmt::AnnAssign(self.parse_annotated_assignment_statement(parsed_expr, start)) + self.parse_annotated_assignment_statement(parsed_expr, start).into() } else if let Some(op) = self.current_token_kind().as_augmented_assign_operator() { - Stmt::AugAssign(self.parse_augmented_assignment_statement( + self.parse_augmented_assignment_statement( parsed_expr, op, start, - )) + ).into() } else if self.options.mode == Mode::Ipython && self.at(TokenKind::Question) { - Stmt::IpyEscapeCommand( - self.parse_ipython_help_end_escape_command_statement(&parsed_expr), - ) + self.parse_ipython_help_end_escape_command_statement(&parsed_expr).into() } else { Stmt::Expr(ast::StmtExpr { range: self.node_range(start), @@ -2728,24 +2726,24 @@ impl<'src> Parser<'src> { match self.current_token_kind() { // test_ok async_function_definition // async def foo(): ... - TokenKind::Def => Stmt::FunctionDef(ast::StmtFunctionDef { + TokenKind::Def => ast::StmtFunctionDef { is_async: true, ..self.parse_function_definition(vec![], async_start) - }), + }.into(), // test_ok async_with_statement // async with item: ... - TokenKind::With => Stmt::With(ast::StmtWith { + TokenKind::With => ast::StmtWith { is_async: true, ..self.parse_with_statement(async_start) - }), + }.into(), // test_ok async_for_statement // async for target in iter: ... - TokenKind::For => Stmt::For(ast::StmtFor { + TokenKind::For => ast::StmtFor { is_async: true, ..self.parse_for_statement(async_start) - }), + }.into(), kind => { // test_err async_unexpected_token @@ -2888,18 +2886,18 @@ impl<'src> Parser<'src> { } match self.current_token_kind() { - TokenKind::Def => Stmt::FunctionDef(self.parse_function_definition(decorators, start)), - TokenKind::Class => Stmt::ClassDef(self.parse_class_definition(decorators, start)), + TokenKind::Def => self.parse_function_definition(decorators, start).into(), + TokenKind::Class => self.parse_class_definition(decorators, start).into(), TokenKind::Async if self.peek() == TokenKind::Def => { self.bump(TokenKind::Async); // test_ok decorator_async_function // @decorator // async def foo(): ... - Stmt::FunctionDef(ast::StmtFunctionDef { + ast::StmtFunctionDef { is_async: true, ..self.parse_function_definition(decorators, start) - }) + }.into() } _ => { // test_err decorator_unexpected_token diff --git a/crates/ruff_python_parser/src/semantic_errors.rs b/crates/ruff_python_parser/src/semantic_errors.rs index 0c7ceef4a4..63899f2edf 100644 --- a/crates/ruff_python_parser/src/semantic_errors.rs +++ b/crates/ruff_python_parser/src/semantic_errors.rs @@ -5,8 +5,7 @@ //! be called in a parent `Visitor`'s `visit_stmt` and `visit_expr` methods, respectively. use ruff_python_ast::{ - self as ast, Expr, ExprContext, IrrefutablePatternKind, Pattern, PythonVersion, Stmt, StmtExpr, - StmtFunctionDef, StmtImportFrom, + self as ast, Expr, ExprContext, IrrefutablePatternKind, Pattern, PythonVersion, Stmt, comparable::ComparableExpr, helpers, visitor::{Visitor, walk_expr, walk_stmt}, @@ -59,13 +58,14 @@ impl SemanticSyntaxChecker { fn check_stmt(&mut self, stmt: &ast::Stmt, ctx: &Ctx) { match stmt { - Stmt::ImportFrom(StmtImportFrom { - range, - module, - level, - names, - .. - }) => { + Stmt::ImportFrom(node) => { + let ast::StmtImportFrom { + range, + module, + level, + names, + node_index: _, + } = &**node; if matches!(module.as_deref(), Some("__future__")) { for name in names { if !is_known_future_feature(&name.name) { @@ -124,17 +124,19 @@ impl SemanticSyntaxChecker { visitor.visit_pattern(&case.pattern); } } - Stmt::FunctionDef(ast::StmtFunctionDef { - type_params, - parameters, - .. - }) => { + Stmt::FunctionDef(node) => { + let ast::StmtFunctionDef { + type_params, + parameters, + .. + } = &**node; if let Some(type_params) = type_params { Self::duplicate_type_parameter_name(type_params, ctx); } Self::duplicate_parameter_name(parameters, ctx); } - Stmt::Global(ast::StmtGlobal { names, .. }) => { + Stmt::Global(node) => { + let ast::StmtGlobal { names, .. } = &**node; for name in names { if ctx.is_bound_parameter(name) { Self::add_error( @@ -145,19 +147,18 @@ impl SemanticSyntaxChecker { } } } - Stmt::ClassDef(ast::StmtClassDef { - type_params: Some(type_params), - .. - }) - | Stmt::TypeAlias(ast::StmtTypeAlias { - type_params: Some(type_params), - .. - }) => { + Stmt::ClassDef(node) if node.type_params.is_some() => { + let type_params = node.type_params.as_ref().unwrap(); Self::duplicate_type_parameter_name(type_params, ctx); Self::type_parameter_default_order(type_params, ctx); } - Stmt::Assign(ast::StmtAssign { targets, value, .. }) => { - if let [Expr::Starred(ast::ExprStarred { range, .. })] = targets.as_slice() { + Stmt::TypeAlias(node) if node.type_params.is_some() => { + let type_params = node.type_params.as_ref().unwrap(); + Self::duplicate_type_parameter_name(type_params, ctx); + Self::type_parameter_default_order(type_params, ctx); + } + Stmt::Assign(node) => { + if let [Expr::Starred(ast::ExprStarred { range, .. })] = node.targets.as_slice() { // test_ok single_starred_assignment_target // (*a,) = (1,) // *a, = (1,) @@ -183,7 +184,7 @@ impl SemanticSyntaxChecker { // _ = *{42} // _ = *list() // _ = *(p + q) - Self::invalid_star_expression(value, ctx); + Self::invalid_star_expression(&node.value, ctx); } Stmt::Return(ast::StmtReturn { value, @@ -199,12 +200,13 @@ impl SemanticSyntaxChecker { Self::add_error(ctx, SemanticSyntaxErrorKind::ReturnOutsideFunction, *range); } } - Stmt::For(ast::StmtFor { - target, - iter, - is_async, - .. - }) => { + Stmt::For(node) => { + let ast::StmtFor { + target, + iter, + is_async, + .. + } = &**node; // test_err single_star_for // for _ in *x: ... // for *x in xs: ... @@ -218,14 +220,15 @@ impl SemanticSyntaxChecker { ); } } - Stmt::With(ast::StmtWith { is_async: true, .. }) => { + Stmt::With(node) if node.is_async => { Self::await_outside_async_function( ctx, stmt, AwaitOutsideAsyncFunctionKind::AsyncWith, ); } - Stmt::Nonlocal(ast::StmtNonlocal { names, range, .. }) => { + Stmt::Nonlocal(node) => { + let ast::StmtNonlocal { names, range, .. } = &**node; // test_ok nonlocal_declaration_at_module_level // def _(): // nonlocal x @@ -272,7 +275,7 @@ impl SemanticSyntaxChecker { fn check_annotation(stmt: &ast::Stmt, ctx: &Ctx) { match stmt { - Stmt::AnnAssign(ast::StmtAnnAssign { annotation, .. }) => { + Stmt::AnnAssign(node) => { if ctx.python_version() > PythonVersion::PY313 { // test_ok valid_annotation_py313 // # parse_options: {"target-version": "3.13"} @@ -295,15 +298,10 @@ impl SemanticSyntaxChecker { position: InvalidExpressionPosition::TypeAnnotation, ctx, }; - visitor.visit_expr(annotation); + visitor.visit_expr(&node.annotation); } } - Stmt::FunctionDef(ast::StmtFunctionDef { - type_params, - parameters, - returns, - .. - }) => { + Stmt::FunctionDef(node) => { // test_ok valid_annotation_function_py313 // # parse_options: {"target-version": "3.13"} // def f() -> (y := 3): ... @@ -355,32 +353,30 @@ impl SemanticSyntaxChecker { position: InvalidExpressionPosition::TypeAnnotation, ctx, }; - if let Some(type_params) = type_params { + if let Some(type_params) = &node.type_params { visitor.visit_type_params(type_params); } // the __future__ annotation error takes precedence over the generic error if ctx.future_annotations_or_stub() || ctx.python_version() > PythonVersion::PY313 { visitor.position = InvalidExpressionPosition::TypeAnnotation; - } else if type_params.is_some() { + } else if node.type_params.is_some() { visitor.position = InvalidExpressionPosition::GenericDefinition; } else { return; } - for param in parameters + for param in node + .parameters .iter() .filter_map(ast::AnyParameterRef::annotation) { visitor.visit_expr(param); } - if let Some(returns) = returns { + if let Some(returns) = &node.returns { visitor.visit_expr(returns); } } - Stmt::ClassDef(ast::StmtClassDef { - type_params: Some(type_params), - arguments, - .. - }) => { + Stmt::ClassDef(node) if node.type_params.is_some() => { + let type_params = node.type_params.as_ref().unwrap(); // test_ok valid_annotation_class // class F(y := list): ... // def f(): @@ -402,14 +398,12 @@ impl SemanticSyntaxChecker { ctx, }; visitor.visit_type_params(type_params); - if let Some(arguments) = arguments { + if let Some(arguments) = &node.arguments { visitor.position = InvalidExpressionPosition::GenericDefinition; visitor.visit_arguments(arguments); } } - Stmt::TypeAlias(ast::StmtTypeAlias { - type_params, value, .. - }) => { + Stmt::TypeAlias(node) => { // test_err invalid_annotation_type_alias // type X[T: (yield 1)] = int # TypeVar bound // type X[T = (yield 1)] = int # TypeVar default @@ -423,8 +417,8 @@ impl SemanticSyntaxChecker { position: InvalidExpressionPosition::TypeAlias, ctx, }; - visitor.visit_expr(value); - if let Some(type_params) = type_params { + visitor.visit_expr(&node.value); + if let Some(type_params) = &node.type_params { visitor.visit_type_params(type_params); } } @@ -485,43 +479,34 @@ impl SemanticSyntaxChecker { /// Check for [`SemanticSyntaxErrorKind::WriteToDebug`] in `stmt`. fn debug_shadowing(stmt: &ast::Stmt, ctx: &Ctx) { match stmt { - Stmt::FunctionDef(ast::StmtFunctionDef { - name, - type_params, - parameters, - .. - }) => { + Stmt::FunctionDef(node) => { // test_err debug_shadow_function // def __debug__(): ... # function name // def f[__debug__](): ... # type parameter name // def f(__debug__): ... # parameter name - Self::check_identifier(name, ctx); - if let Some(type_params) = type_params { + Self::check_identifier(&node.name, ctx); + if let Some(type_params) = &node.type_params { for type_param in type_params.iter() { Self::check_identifier(type_param.name(), ctx); } } - for parameter in parameters { + for parameter in &node.parameters { Self::check_identifier(parameter.name(), ctx); } } - Stmt::ClassDef(ast::StmtClassDef { - name, type_params, .. - }) => { + Stmt::ClassDef(node) => { // test_err debug_shadow_class // class __debug__: ... # class name // class C[__debug__]: ... # type parameter name - Self::check_identifier(name, ctx); - if let Some(type_params) = type_params { + Self::check_identifier(&node.name, ctx); + if let Some(type_params) = &node.type_params { for type_param in type_params.iter() { Self::check_identifier(type_param.name(), ctx); } } } - Stmt::TypeAlias(ast::StmtTypeAlias { - type_params: Some(type_params), - .. - }) => { + Stmt::TypeAlias(node) if node.type_params.is_some() => { + let type_params = node.type_params.as_ref().unwrap(); // test_err debug_shadow_type_alias // type __debug__ = list[int] # visited as an Expr but still flagged // type Debug[__debug__] = str @@ -529,8 +514,7 @@ impl SemanticSyntaxChecker { Self::check_identifier(type_param.name(), ctx); } } - Stmt::Import(ast::StmtImport { names, .. }) - | Stmt::ImportFrom(ast::StmtImportFrom { names, .. }) => { + Stmt::Import(node) => { // test_err debug_shadow_import // import __debug__ // import debug as __debug__ @@ -541,18 +525,27 @@ impl SemanticSyntaxChecker { // import __debug__ as debug // from __debug__ import Some // from x import __debug__ as debug - for name in names { + for name in &node.names { match &name.asname { Some(asname) => Self::check_identifier(asname, ctx), None => Self::check_identifier(&name.name, ctx), } } } - Stmt::Try(ast::StmtTry { handlers, .. }) => { + Stmt::ImportFrom(node) => { + for name in &node.names { + match &name.asname { + Some(asname) => Self::check_identifier(asname, ctx), + None => Self::check_identifier(&name.name, ctx), + } + } + } + Stmt::Try(node) => { // test_err debug_shadow_try // try: ... // except Exception as __debug__: ... - for handler in handlers + for handler in node + .handlers .iter() .filter_map(ast::ExceptHandler::as_except_handler) { @@ -732,18 +725,18 @@ impl SemanticSyntaxChecker { // update internal state match stmt { - Stmt::Expr(StmtExpr { value, .. }) - if !self.seen_module_docstring_boundary && value.is_string_literal_expr() => {} - Stmt::ImportFrom(StmtImportFrom { module, .. }) => { + Stmt::Expr(node) + if !self.seen_module_docstring_boundary && node.value.is_string_literal_expr() => {} + Stmt::ImportFrom(node) => { // Allow __future__ imports until we see a non-__future__ import. - if !matches!(module.as_deref(), Some("__future__")) { + if !matches!(node.module.as_deref(), Some("__future__")) { self.seen_futures_boundary = true; } } - Stmt::FunctionDef(StmtFunctionDef { is_async, body, .. }) => { - if *is_async { + Stmt::FunctionDef(node) => { + if node.is_async { let mut visitor = ReturnVisitor::default(); - visitor.visit_body(body); + visitor.visit_body(&node.body); if visitor.has_yield { if let Some(return_range) = visitor.return_range { diff --git a/crates/ruff_python_semantic/src/analyze/class.rs b/crates/ruff_python_semantic/src/analyze/class.rs index 14f6b2c983..b875168b65 100644 --- a/crates/ruff_python_semantic/src/analyze/class.rs +++ b/crates/ruff_python_semantic/src/analyze/class.rs @@ -7,10 +7,7 @@ use crate::{BindingId, SemanticModel}; use ruff_python_ast as ast; use ruff_python_ast::helpers::map_subscript; use ruff_python_ast::name::QualifiedName; -use ruff_python_ast::{ - ExceptHandler, Expr, ExprName, ExprStarred, ExprSubscript, ExprTuple, Stmt, StmtFor, StmtIf, - StmtMatch, StmtTry, StmtWhile, StmtWith, -}; +use ruff_python_ast::{ExceptHandler, Expr, ExprName, ExprStarred, ExprSubscript, ExprTuple, Stmt}; /// Return `true` if any base class matches a [`QualifiedName`] predicate. pub fn any_qualified_base_class( @@ -183,18 +180,17 @@ pub fn any_member_declaration( Stmt::FunctionDef(function_def) => Some(ClassMemberKind::FunctionDef(function_def)), Stmt::Assign(assign) => Some(ClassMemberKind::Assign(assign)), Stmt::AnnAssign(assign) => Some(ClassMemberKind::AnnAssign(assign)), - Stmt::With(StmtWith { body, .. }) => { - if any_stmt_in_body(body, func, ClassMemberBoundness::PossiblyUnbound) { + Stmt::With(node) => { + if any_stmt_in_body(&node.body, func, ClassMemberBoundness::PossiblyUnbound) { return true; } None } - Stmt::For(StmtFor { body, orelse, .. }) - | Stmt::While(StmtWhile { body, orelse, .. }) => { - if any_stmt_in_body(body, func, ClassMemberBoundness::PossiblyUnbound) - || any_stmt_in_body(orelse, func, ClassMemberBoundness::PossiblyUnbound) + Stmt::For(node) => { + if any_stmt_in_body(&node.body, func, ClassMemberBoundness::PossiblyUnbound) + || any_stmt_in_body(&node.orelse, func, ClassMemberBoundness::PossiblyUnbound) { return true; } @@ -202,13 +198,19 @@ pub fn any_member_declaration( None } - Stmt::If(StmtIf { - body, - elif_else_clauses, - .. - }) => { - if any_stmt_in_body(body, func, ClassMemberBoundness::PossiblyUnbound) - || elif_else_clauses.iter().any(|it| { + Stmt::While(node) => { + if any_stmt_in_body(&node.body, func, ClassMemberBoundness::PossiblyUnbound) + || any_stmt_in_body(&node.orelse, func, ClassMemberBoundness::PossiblyUnbound) + { + return true; + } + + None + } + + Stmt::If(node) => { + if any_stmt_in_body(&node.body, func, ClassMemberBoundness::PossiblyUnbound) + || node.elif_else_clauses.iter().any(|it| { any_stmt_in_body(&it.body, func, ClassMemberBoundness::PossiblyUnbound) }) { @@ -217,8 +219,8 @@ pub fn any_member_declaration( None } - Stmt::Match(StmtMatch { cases, .. }) => { - if cases.iter().any(|it| { + Stmt::Match(node) => { + if node.cases.iter().any(|it| { any_stmt_in_body(&it.body, func, ClassMemberBoundness::PossiblyUnbound) }) { return true; @@ -227,17 +229,11 @@ pub fn any_member_declaration( None } - Stmt::Try(StmtTry { - body, - handlers, - orelse, - finalbody, - .. - }) => { - if any_stmt_in_body(body, func, ClassMemberBoundness::PossiblyUnbound) - || any_stmt_in_body(orelse, func, ClassMemberBoundness::PossiblyUnbound) - || any_stmt_in_body(finalbody, func, ClassMemberBoundness::PossiblyUnbound) - || handlers.iter().any(|ExceptHandler::ExceptHandler(it)| { + Stmt::Try(node) => { + if any_stmt_in_body(&node.body, func, ClassMemberBoundness::PossiblyUnbound) + || any_stmt_in_body(&node.orelse, func, ClassMemberBoundness::PossiblyUnbound) + || any_stmt_in_body(&node.finalbody, func, ClassMemberBoundness::PossiblyUnbound) + || node.handlers.iter().any(|ExceptHandler::ExceptHandler(it)| { any_stmt_in_body(&it.body, func, ClassMemberBoundness::PossiblyUnbound) }) { @@ -336,12 +332,10 @@ impl IsMetaclass { fn has_metaclass_new_signature(class_def: &ast::StmtClassDef, semantic: &SemanticModel) -> bool { // Look for a __new__ method in the class body for stmt in &class_def.body { - let ast::Stmt::FunctionDef(ast::StmtFunctionDef { - name, parameters, .. - }) = stmt - else { + let ast::Stmt::FunctionDef(func_def) = stmt else { continue; }; + let (name, parameters) = (&func_def.name, &func_def.parameters); if name != "__new__" { continue; diff --git a/crates/ruff_python_semantic/src/analyze/function_type.rs b/crates/ruff_python_semantic/src/analyze/function_type.rs index 5949b71c88..f88c0f2dce 100644 --- a/crates/ruff_python_semantic/src/analyze/function_type.rs +++ b/crates/ruff_python_semantic/src/analyze/function_type.rs @@ -1,6 +1,6 @@ use ruff_python_ast::helpers::map_callable; use ruff_python_ast::name::{QualifiedName, UnqualifiedName}; -use ruff_python_ast::{Decorator, Expr, Stmt, StmtExpr, StmtFunctionDef, StmtRaise}; +use ruff_python_ast::{Decorator, Expr, Stmt, StmtExpr, StmtFunctionDef}; use crate::model::SemanticModel; use crate::scope::Scope; @@ -146,12 +146,7 @@ pub fn is_stub(function_def: &StmtFunctionDef, semantic: &SemanticModel) -> bool Expr::StringLiteral(_) | Expr::EllipsisLiteral(_) ) } - Stmt::Raise(StmtRaise { - range: _, - node_index: _, - exc: exception, - cause: _, - }) => exception.as_ref().is_some_and(|exc| { + Stmt::Raise(raise_stmt) => raise_stmt.exc.as_ref().is_some_and(|exc| { semantic .resolve_builtin_symbol(map_callable(exc)) .is_some_and(|name| matches!(name, "NotImplementedError" | "NotImplemented")) diff --git a/crates/ruff_python_semantic/src/analyze/imports.rs b/crates/ruff_python_semantic/src/analyze/imports.rs index e5f20f04d6..7ca8282f80 100644 --- a/crates/ruff_python_semantic/src/analyze/imports.rs +++ b/crates/ruff_python_semantic/src/analyze/imports.rs @@ -12,11 +12,7 @@ use crate::SemanticModel; /// ``` pub fn is_sys_path_modification(stmt: &Stmt, semantic: &SemanticModel) -> bool { match stmt { - Stmt::Expr(ast::StmtExpr { - value, - range: _, - node_index: _, - }) => match value.as_ref() { + Stmt::Expr(node) => match node.value.as_ref() { Expr::Call(ast::ExprCall { func, .. }) => semantic .resolve_qualified_name(func.as_ref()) .is_some_and(|qualified_name| { @@ -38,8 +34,8 @@ pub fn is_sys_path_modification(stmt: &Stmt, semantic: &SemanticModel) -> bool { }), _ => false, }, - Stmt::AugAssign(ast::StmtAugAssign { target, .. }) => semantic - .resolve_qualified_name(map_subscript(target)) + Stmt::AugAssign(node) => semantic + .resolve_qualified_name(map_subscript(&node.target)) .is_some_and(|qualified_name| matches!(qualified_name.segments(), ["sys", "path"])), _ => false, } @@ -53,7 +49,7 @@ pub fn is_sys_path_modification(stmt: &Stmt, semantic: &SemanticModel) -> bool { /// ``` pub fn is_os_environ_modification(stmt: &Stmt, semantic: &SemanticModel) -> bool { match stmt { - Stmt::Expr(ast::StmtExpr { value, .. }) => match value.as_ref() { + Stmt::Expr(node) => match node.value.as_ref() { Expr::Call(ast::ExprCall { func, .. }) => semantic .resolve_qualified_name(func.as_ref()) .is_some_and(|qualified_name| { @@ -69,25 +65,25 @@ pub fn is_os_environ_modification(stmt: &Stmt, semantic: &SemanticModel) -> bool }), _ => false, }, - Stmt::Delete(ast::StmtDelete { targets, .. }) => targets.iter().any(|target| { + Stmt::Delete(node) => node.targets.iter().any(|target| { semantic .resolve_qualified_name(map_subscript(target)) .is_some_and(|qualified_name| { matches!(qualified_name.segments(), ["os", "environ"]) }) }), - Stmt::Assign(ast::StmtAssign { targets, .. }) => targets.iter().any(|target| { + Stmt::Assign(node) => node.targets.iter().any(|target| { semantic .resolve_qualified_name(map_subscript(target)) .is_some_and(|qualified_name| { matches!(qualified_name.segments(), ["os", "environ"]) }) }), - Stmt::AnnAssign(ast::StmtAnnAssign { target, .. }) => semantic - .resolve_qualified_name(map_subscript(target)) + Stmt::AnnAssign(node) => semantic + .resolve_qualified_name(map_subscript(&node.target)) .is_some_and(|qualified_name| matches!(qualified_name.segments(), ["os", "environ"])), - Stmt::AugAssign(ast::StmtAugAssign { target, .. }) => semantic - .resolve_qualified_name(map_subscript(target)) + Stmt::AugAssign(node) => semantic + .resolve_qualified_name(map_subscript(&node.target)) .is_some_and(|qualified_name| matches!(qualified_name.segments(), ["os", "environ"])), _ => false, } diff --git a/crates/ruff_python_semantic/src/analyze/terminal.rs b/crates/ruff_python_semantic/src/analyze/terminal.rs index 4702ac439a..0cae27753b 100644 --- a/crates/ruff_python_semantic/src/analyze/terminal.rs +++ b/crates/ruff_python_semantic/src/analyze/terminal.rs @@ -41,23 +41,31 @@ impl Terminal { for stmt in stmts { match stmt { - Stmt::For(ast::StmtFor { body, orelse, .. }) - | Stmt::While(ast::StmtWhile { body, orelse, .. }) => { - if always_breaks(body) { + Stmt::For(node) => { + if always_breaks(&node.body) { continue; } - terminal = terminal.and_then(Self::from_body(body)); + terminal = terminal.and_then(Self::from_body(&node.body)); - if !sometimes_breaks(body) { - terminal = terminal.and_then(Self::from_body(orelse)); + if !sometimes_breaks(&node.body) { + terminal = terminal.and_then(Self::from_body(&node.orelse)); } } - Stmt::If(ast::StmtIf { - body, - elif_else_clauses, - .. - }) => { + Stmt::While(node) => { + if always_breaks(&node.body) { + continue; + } + + terminal = terminal.and_then(Self::from_body(&node.body)); + + if !sometimes_breaks(&node.body) { + terminal = terminal.and_then(Self::from_body(&node.orelse)); + } + } + Stmt::If(node) => { + let body = &node.body; + let elif_else_clauses = &node.elif_else_clauses; let branch_terminal = Terminal::branches( std::iter::once(Self::from_body(body)).chain( elif_else_clauses @@ -77,14 +85,14 @@ impl Terminal { terminal = terminal.and_then(Terminal::ConditionalReturn); } } - Stmt::Match(ast::StmtMatch { cases, .. }) => { + Stmt::Match(node) => { let branch_terminal = terminal.and_then(Terminal::branches( - cases.iter().map(|case| Self::from_body(&case.body)), + node.cases.iter().map(|case| Self::from_body(&case.body)), )); // If the `match` is known to be exhaustive (by way of including a wildcard // pattern)... - if cases.iter().any(is_wildcard) { + if node.cases.iter().any(is_wildcard) { // And all branches return, then the `match` statement returns. terminal = terminal.and_then(branch_terminal); } else { @@ -95,27 +103,21 @@ impl Terminal { } } } - Stmt::Try(ast::StmtTry { - body, - handlers, - orelse, - finalbody, - .. - }) => { + Stmt::Try(node) => { // If the body returns, then this can't be a non-returning function. We assume // that _any_ statement in the body could raise an exception, so we don't // consider the body to be exhaustive. In other words, we assume the exception // handlers exist for a reason. - let body_terminal = Self::from_body(body); + let body_terminal = Self::from_body(&node.body); if body_terminal.has_any_return() { terminal = terminal.and_then(Terminal::ConditionalReturn); } // If the `finally` block returns, the `try` block must also return. (Similarly, // if the `finally` block raises, the `try` block must also raise.) - terminal = terminal.and_then(Self::from_body(finalbody)); + terminal = terminal.and_then(Self::from_body(&node.finalbody)); - let branch_terminal = Terminal::branches(handlers.iter().map(|handler| { + let branch_terminal = Terminal::branches(node.handlers.iter().map(|handler| { let ExceptHandler::ExceptHandler(ast::ExceptHandlerExceptHandler { body, .. @@ -123,7 +125,7 @@ impl Terminal { Self::from_body(body) })); - if orelse.is_empty() { + if node.orelse.is_empty() { // If there's no `else`, we may fall through, so only mark that this can't // be a non-returning function if any of the branches return. if branch_terminal.has_any_return() { @@ -133,11 +135,11 @@ impl Terminal { // If there's an `else`, we won't fall through. If all the handlers and // the `else` block return,, the `try` block also returns. terminal = - terminal.and_then(branch_terminal.branch(Terminal::from_body(orelse))); + terminal.and_then(branch_terminal.branch(Terminal::from_body(&node.orelse))); } } - Stmt::With(ast::StmtWith { body, .. }) => { - terminal = terminal.and_then(Self::from_body(body)); + Stmt::With(node) => { + terminal = terminal.and_then(Self::from_body(&node.body)); } Stmt::Return(_) => { terminal = terminal.and_then(Terminal::RaiseOrReturn); @@ -247,62 +249,52 @@ impl Terminal { fn sometimes_breaks(stmts: &[Stmt]) -> bool { for stmt in stmts { match stmt { - Stmt::For(ast::StmtFor { body, orelse, .. }) => { - if Terminal::from_body(body).has_any_return() { + Stmt::For(node) => { + if Terminal::from_body(&node.body).has_any_return() { return false; } - if sometimes_breaks(orelse) { + if sometimes_breaks(&node.orelse) { return true; } } - Stmt::While(ast::StmtWhile { body, orelse, .. }) => { - if Terminal::from_body(body).has_any_return() { + Stmt::While(node) => { + if Terminal::from_body(&node.body).has_any_return() { return false; } - if sometimes_breaks(orelse) { + if sometimes_breaks(&node.orelse) { return true; } } - Stmt::If(ast::StmtIf { - body, - elif_else_clauses, - .. - }) => { - if std::iter::once(body) - .chain(elif_else_clauses.iter().map(|clause| &clause.body)) + Stmt::If(node) => { + if std::iter::once(&node.body) + .chain(node.elif_else_clauses.iter().map(|clause| &clause.body)) .any(|body| sometimes_breaks(body)) { return true; } } - Stmt::Match(ast::StmtMatch { cases, .. }) => { - if cases.iter().any(|case| sometimes_breaks(&case.body)) { + Stmt::Match(node) => { + if node.cases.iter().any(|case| sometimes_breaks(&case.body)) { return true; } } - Stmt::Try(ast::StmtTry { - body, - handlers, - orelse, - finalbody, - .. - }) => { - if sometimes_breaks(body) - || handlers.iter().any(|handler| { + Stmt::Try(node) => { + if sometimes_breaks(&node.body) + || node.handlers.iter().any(|handler| { let ExceptHandler::ExceptHandler(ast::ExceptHandlerExceptHandler { body, .. }) = handler; sometimes_breaks(body) }) - || sometimes_breaks(orelse) - || sometimes_breaks(finalbody) + || sometimes_breaks(&node.orelse) + || sometimes_breaks(&node.finalbody) { return true; } } - Stmt::With(ast::StmtWith { body, .. }) => { - if sometimes_breaks(body) { + Stmt::With(node) => { + if sometimes_breaks(&node.body) { return true; } } diff --git a/crates/ruff_python_semantic/src/analyze/typing.rs b/crates/ruff_python_semantic/src/analyze/typing.rs index 4b30993a15..1e85548a85 100644 --- a/crates/ruff_python_semantic/src/analyze/typing.rs +++ b/crates/ruff_python_semantic/src/analyze/typing.rs @@ -5,7 +5,6 @@ use ruff_python_ast::identifier::Identifier; use ruff_python_ast::name::QualifiedName; use ruff_python_ast::{ self as ast, Expr, ExprCall, ExprName, Operator, ParameterWithDefault, Parameters, Stmt, - StmtAssign, }; use ruff_python_stdlib::typing::{ as_pep_585_generic, is_immutable_generic_type, is_immutable_non_generic_type, @@ -366,9 +365,13 @@ pub fn is_immutable_newtype_call( return false; } - let Some(Stmt::Assign(StmtAssign { value, .. })) = binding.statement(semantic) else { + let Some(stmt) = binding.statement(semantic) else { return false; }; + let Stmt::Assign(node) = stmt else { + return false; + }; + let value = &node.value; let Expr::Call(ExprCall { func, arguments, .. @@ -632,18 +635,20 @@ pub fn check_type(binding: &Binding, semantic: &SemanticModel) - // ``` // // The type checker might know how to infer the type based on `init_expr`. - Some(Stmt::Assign(ast::StmtAssign { targets, value, .. })) => targets - .iter() - .find_map(|target| match_value(binding, target, value)) - .is_some_and(|value| T::match_initializer(value, semantic)), + Some(Stmt::Assign(node)) => { + node.targets + .iter() + .find_map(|target| match_value(binding, target, &node.value)) + .is_some_and(|value| T::match_initializer(value, semantic)) + } // ```python // x: annotation = some_expr // ``` // // In this situation, we check only the annotation. - Some(Stmt::AnnAssign(ast::StmtAnnAssign { annotation, .. })) => { - T::match_annotation(annotation, semantic) + Some(Stmt::AnnAssign(node)) => { + T::match_annotation(&node.annotation, semantic) } _ => false, @@ -670,14 +675,16 @@ pub fn check_type(binding: &Binding, semantic: &SemanticModel) - // with open("file.txt") as x: // ... // ``` - Some(Stmt::With(ast::StmtWith { items, .. })) => items - .iter() - .find_map(|item| { - let target = item.optional_vars.as_ref()?; - let value = &item.context_expr; - match_value(binding, target, value) - }) - .is_some_and(|value| T::match_initializer(value, semantic)), + Some(Stmt::With(node)) => { + node.items + .iter() + .find_map(|item| { + let target = item.optional_vars.as_ref()?; + let value = &item.context_expr; + match_value(binding, target, value) + }) + .is_some_and(|value| T::match_initializer(value, semantic)) + } _ => false, }, @@ -689,8 +696,8 @@ pub fn check_type(binding: &Binding, semantic: &SemanticModel) - // ``` // // We trust the annotation and see if the type checker matches the annotation. - Some(Stmt::FunctionDef(ast::StmtFunctionDef { parameters, .. })) => { - let Some(parameter) = find_parameter(parameters, binding) else { + Some(Stmt::FunctionDef(node)) => { + let Some(parameter) = find_parameter(&node.parameters, binding) else { return false; }; let Some(annotation) = parameter.annotation() else { @@ -708,8 +715,8 @@ pub fn check_type(binding: &Binding, semantic: &SemanticModel) - // ``` // // It's a typed declaration, type annotation is the only source of information. - Some(Stmt::AnnAssign(ast::StmtAnnAssign { annotation, .. })) => { - T::match_annotation(annotation, semantic) + Some(Stmt::AnnAssign(node)) => { + T::match_annotation(&node.annotation, semantic) } _ => false, }, @@ -719,9 +726,11 @@ pub fn check_type(binding: &Binding, semantic: &SemanticModel) - // def foo() -> int: // ... // ``` - Some(Stmt::FunctionDef(ast::StmtFunctionDef { returns, .. })) => returns - .as_ref() - .is_some_and(|return_ann| T::match_annotation(return_ann, semantic)), + Some(Stmt::FunctionDef(node)) => { + node.returns + .as_ref() + .is_some_and(|return_ann| T::match_annotation(return_ann, semantic)) + } _ => false, }, @@ -1038,12 +1047,12 @@ pub fn is_dict(binding: &Binding, semantic: &SemanticModel) -> bool { // ... // ``` if matches!(binding.kind, BindingKind::Argument) { - if let Some(Stmt::FunctionDef(ast::StmtFunctionDef { parameters, .. })) = - binding.statement(semantic) - { - if let Some(kwarg_parameter) = parameters.kwarg.as_deref() { - if kwarg_parameter.name.range() == binding.range() { - return true; + if let Some(stmt) = binding.statement(semantic) { + if let Stmt::FunctionDef(node) = stmt { + if let Some(kwarg_parameter) = node.parameters.kwarg.as_deref() { + if kwarg_parameter.name.range() == binding.range() { + return true; + } } } } @@ -1091,12 +1100,12 @@ pub fn is_tuple(binding: &Binding, semantic: &SemanticModel) -> bool { // ... // ``` if matches!(binding.kind, BindingKind::Argument) { - if let Some(Stmt::FunctionDef(ast::StmtFunctionDef { parameters, .. })) = - binding.statement(semantic) - { - if let Some(arg_parameter) = parameters.vararg.as_deref() { - if arg_parameter.name.range() == binding.range() { - return true; + if let Some(stmt) = binding.statement(semantic) { + if let Stmt::FunctionDef(node) = stmt { + if let Some(arg_parameter) = node.parameters.vararg.as_deref() { + if arg_parameter.name.range() == binding.range() { + return true; + } } } } @@ -1183,10 +1192,14 @@ pub fn resolve_assignment<'a>( let binding_id = semantic.resolve_name(name)?; let statement = semantic.binding(binding_id).statement(semantic)?; match statement { - Stmt::Assign(ast::StmtAssign { value, .. }) - | Stmt::AnnAssign(ast::StmtAnnAssign { - value: Some(value), .. - }) => { + Stmt::Assign(node) => { + let ast::ExprCall { func, .. } = node.value.as_call_expr()?; + + let qualified_name = semantic.resolve_qualified_name(func)?; + Some(qualified_name.extend_members(reversed_tail.into_iter().rev())) + } + Stmt::AnnAssign(node) => { + let value = node.value.as_ref()?; let ast::ExprCall { func, .. } = value.as_call_expr()?; let qualified_name = semantic.resolve_qualified_name(func)?; @@ -1237,24 +1250,23 @@ pub fn find_binding_value<'a>(binding: &Binding, semantic: &'a SemanticModel) -> } // Ex) `x = 1` BindingKind::Assignment => match binding.statement(semantic) { - Some(Stmt::Assign(ast::StmtAssign { value, targets, .. })) => { - return targets + Some(Stmt::Assign(node)) => { + return node + .targets .iter() - .find_map(|target| match_value(binding, target, value)); + .find_map(|target| match_value(binding, target, &node.value)); } - Some(Stmt::AnnAssign(ast::StmtAnnAssign { - value: Some(value), - target, - .. - })) => { - return match_value(binding, target, value); + Some(Stmt::AnnAssign(node)) => { + if let Some(value) = &node.value { + return match_value(binding, &node.target, value); + } } _ => {} }, // Ex) `with open("file.txt") as f:` BindingKind::WithItemVar => match binding.statement(semantic) { - Some(Stmt::With(ast::StmtWith { items, .. })) => { - return items.iter().find_map(|item| { + Some(Stmt::With(node)) => { + return node.items.iter().find_map(|item| { let target = item.optional_vars.as_ref()?; let value = &item.context_expr; match_value(binding, target, value) diff --git a/crates/ruff_python_semantic/src/globals.rs b/crates/ruff_python_semantic/src/globals.rs index 3a5e830733..81ba895b06 100644 --- a/crates/ruff_python_semantic/src/globals.rs +++ b/crates/ruff_python_semantic/src/globals.rs @@ -4,7 +4,7 @@ use std::ops::Index; -use ruff_python_ast::{self as ast, Stmt}; +use ruff_python_ast::Stmt; use ruff_text_size::{Ranged, TextRange}; use rustc_hash::FxHashMap; @@ -74,12 +74,8 @@ impl<'a> GlobalsVisitor<'a> { impl<'a> StatementVisitor<'a> for GlobalsVisitor<'a> { fn visit_stmt(&mut self, stmt: &'a Stmt) { match stmt { - Stmt::Global(ast::StmtGlobal { - names, - range: _, - node_index: _, - }) => { - for name in names { + Stmt::Global(global_stmt) => { + for name in &global_stmt.names { self.0.insert(name.as_str(), name.range()); } } diff --git a/crates/ruff_python_semantic/src/imports.rs b/crates/ruff_python_semantic/src/imports.rs index 95c54ced42..443d328297 100644 --- a/crates/ruff_python_semantic/src/imports.rs +++ b/crates/ruff_python_semantic/src/imports.rs @@ -204,7 +204,7 @@ impl serde::Serialize for NameImport { #[cfg(feature = "serde")] impl<'de> serde::de::Deserialize<'de> for NameImports { fn deserialize>(deserializer: D) -> Result { - use ruff_python_ast::{self as ast, Stmt}; + use ruff_python_ast::Stmt; use ruff_python_parser::Parsed; struct AnyNameImportsVisitor; @@ -225,30 +225,22 @@ impl<'de> serde::de::Deserialize<'de> for NameImports { }; let imports = match stmt { - Stmt::ImportFrom(ast::StmtImportFrom { - module, - names, - level, - range: _, - node_index: _, - }) => names + Stmt::ImportFrom(import_from) => import_from + .names .iter() .map(|name| { NameImport::ImportFrom(MemberNameImport { - module: module.as_deref().map(ToString::to_string), + module: import_from.module.as_deref().map(ToString::to_string), name: Alias { name: name.name.to_string(), as_name: name.asname.as_deref().map(ToString::to_string), }, - level: *level, + level: import_from.level, }) }) .collect(), - Stmt::Import(ast::StmtImport { - names, - range: _, - node_index: _, - }) => names + Stmt::Import(import_stmt) => import_stmt + .names .iter() .map(|name| { NameImport::Import(ModuleNameImport { diff --git a/crates/ruff_python_semantic/src/model/all.rs b/crates/ruff_python_semantic/src/model/all.rs index bc05d96efe..352e4832fb 100644 --- a/crates/ruff_python_semantic/src/model/all.rs +++ b/crates/ruff_python_semantic/src/model/all.rs @@ -102,9 +102,9 @@ impl SemanticModel<'_> { let mut flags = DunderAllFlags::empty(); if let Some(value) = match stmt { - Stmt::Assign(ast::StmtAssign { value, .. }) => Some(value), - Stmt::AnnAssign(ast::StmtAnnAssign { value, .. }) => value.as_ref(), - Stmt::AugAssign(ast::StmtAugAssign { value, .. }) => Some(value), + Stmt::Assign(node) => Some(&node.value), + Stmt::AnnAssign(node) => node.value.as_ref(), + Stmt::AugAssign(node) => Some(&node.value), _ => None, } { if let Expr::BinOp(ast::ExprBinOp { left, right, .. }) = value.as_ref() { diff --git a/crates/ty_ide/src/symbols.rs b/crates/ty_ide/src/symbols.rs index ba33af9ef2..c9a5e89fd7 100644 --- a/crates/ty_ide/src/symbols.rs +++ b/crates/ty_ide/src/symbols.rs @@ -940,23 +940,21 @@ impl SourceOrderVisitor<'_> for SymbolVisitor<'_> { }; self.add_assignment(stmt, name); } - ast::Stmt::AugAssign(ast::StmtAugAssign { - target, op, value, .. - }) => { + ast::Stmt::AugAssign(aug_assign) => { if self.all_origin.is_none() { // We can't update `__all__` if it doesn't already // exist. return; } - if !is_dunder_all(target) { + if !is_dunder_all(&aug_assign.target) { return; } // Anything other than `+=` is not valid. - if !matches!(op, ast::Operator::Add) { + if !matches!(aug_assign.op, ast::Operator::Add) { self.all_invalid = true; return; } - if !self.extend_all(value) { + if !self.extend_all(&aug_assign.value) { self.all_invalid = true; } } diff --git a/crates/ty_python_semantic/src/dunder_all.rs b/crates/ty_python_semantic/src/dunder_all.rs index 1803037d6d..5e5a4f9ea1 100644 --- a/crates/ty_python_semantic/src/dunder_all.rs +++ b/crates/ty_python_semantic/src/dunder_all.rs @@ -223,8 +223,8 @@ impl<'db> StatementVisitor<'db> for DunderAllNamesCollector<'db> { } match stmt { - ast::Stmt::ImportFrom(import_from @ ast::StmtImportFrom { names, .. }) => { - for ast::Alias { name, asname, .. } in names { + ast::Stmt::ImportFrom(import_from) => { + for ast::Alias { name, asname, .. } in &import_from.names { // `from module import *` where `module` is a module with a top-level `__all__` // variable that contains the "__all__" element. if name == "*" { @@ -275,14 +275,14 @@ impl<'db> StatementVisitor<'db> for DunderAllNamesCollector<'db> { } } - ast::Stmt::Assign(ast::StmtAssign { targets, value, .. }) => { - let [target] = targets.as_slice() else { + ast::Stmt::Assign(assign_stmt) => { + let [target] = assign_stmt.targets.as_slice() else { return; }; if !is_dunder_all(target) { return; } - match &**value { + match &*assign_stmt.value { // `__all__ = [...]` // `__all__ = (...)` ast::Expr::List(ast::ExprList { elts, .. }) @@ -298,30 +298,27 @@ impl<'db> StatementVisitor<'db> for DunderAllNamesCollector<'db> { } } - ast::Stmt::AugAssign(ast::StmtAugAssign { - target, - op: ast::Operator::Add, - value, - .. - }) => { + ast::Stmt::AugAssign(aug_assign) => { + if aug_assign.op != ast::Operator::Add { + return; + } if self.origin.is_none() { // We can't update `__all__` if it doesn't already exist. return; } - if !is_dunder_all(target) { + if !is_dunder_all(&aug_assign.target) { return; } - if !self.extend(value) { + if !self.extend(&aug_assign.value) { self.invalid = true; } } - ast::Stmt::AnnAssign(ast::StmtAnnAssign { - target, - value: Some(value), - .. - }) => { - if !is_dunder_all(target) { + ast::Stmt::AnnAssign(ann_assign) => { + let Some(value) = &ann_assign.value else { + return; + }; + if !is_dunder_all(&ann_assign.target) { return; } match &**value { @@ -368,15 +365,10 @@ impl<'db> StatementVisitor<'db> for DunderAllNamesCollector<'db> { } } - ast::Stmt::If(ast::StmtIf { - test, - body, - elif_else_clauses, - .. - }) => match self.evaluate_test_expr(test) { - Some(Truthiness::AlwaysTrue) => self.visit_body(body), + ast::Stmt::If(if_stmt) => match self.evaluate_test_expr(&if_stmt.test) { + Some(Truthiness::AlwaysTrue) => self.visit_body(&if_stmt.body), Some(Truthiness::AlwaysFalse) => { - for ast::ElifElseClause { test, body, .. } in elif_else_clauses { + for ast::ElifElseClause { test, body, .. } in &if_stmt.elif_else_clauses { if let Some(test) = test { match self.evaluate_test_expr(test) { Some(Truthiness::AlwaysTrue) => { @@ -409,9 +401,7 @@ impl<'db> StatementVisitor<'db> for DunderAllNamesCollector<'db> { // level. } - ast::Stmt::AugAssign(..) - | ast::Stmt::AnnAssign(..) - | ast::Stmt::Delete(..) + ast::Stmt::Delete(..) | ast::Stmt::Return(..) | ast::Stmt::Raise(..) | ast::Stmt::Assert(..) diff --git a/crates/ty_python_semantic/src/module_resolver/resolver.rs b/crates/ty_python_semantic/src/module_resolver/resolver.rs index 123b4ac31e..e54e019185 100644 --- a/crates/ty_python_semantic/src/module_resolver/resolver.rs +++ b/crates/ty_python_semantic/src/module_resolver/resolver.rs @@ -1506,11 +1506,11 @@ impl Visitor<'_> for LegacyNamespacePackageVisitor { return; } - let ast::Stmt::Assign(ast::StmtAssign { value, targets, .. }) = stmt else { + let ast::Stmt::Assign(node) = stmt else { return; }; - let [ast::Expr::Name(maybe_path)] = &**targets else { + let [ast::Expr::Name(maybe_path)] = &node.targets[..] else { return; }; @@ -1522,7 +1522,7 @@ impl Visitor<'_> for LegacyNamespacePackageVisitor { func: extend_func, arguments: extend_arguments, .. - }) = &**value + }) = &*node.value else { return; }; @@ -1574,7 +1574,7 @@ impl Visitor<'_> for LegacyNamespacePackageVisitor { } // Test that this is an `extend_path(__path__, __name__)` call - if maybe_extend_path != "extend_path" { + if &*maybe_extend_path != "extend_path" { return; } diff --git a/crates/ty_python_semantic/src/semantic_index/builder.rs b/crates/ty_python_semantic/src/semantic_index/builder.rs index 373c80f7ee..07486d1644 100644 --- a/crates/ty_python_semantic/src/semantic_index/builder.rs +++ b/crates/ty_python_semantic/src/semantic_index/builder.rs @@ -1350,35 +1350,24 @@ impl<'ast> Visitor<'ast> for SemanticIndexBuilder<'_, 'ast> { match stmt { ast::Stmt::FunctionDef(function_def) => { - let ast::StmtFunctionDef { - decorator_list, - parameters, - type_params, - name, - returns, - body, - is_async: _, - range: _, - node_index: _, - } = function_def; - for decorator in decorator_list { + for decorator in &function_def.decorator_list { self.visit_decorator(decorator); } self.with_type_params( NodeWithScopeRef::FunctionTypeParameters(function_def), - type_params.as_deref(), + function_def.type_params.as_deref(), |builder| { - builder.visit_parameters(parameters); - if let Some(returns) = returns { + builder.visit_parameters(&function_def.parameters); + if let Some(returns) = &function_def.returns { builder.visit_annotation(returns); } builder.push_scope(NodeWithScopeRef::Function(function_def)); - builder.declare_parameters(parameters); + builder.declare_parameters(&function_def.parameters); - let mut first_parameter_name = parameters + let mut first_parameter_name = function_def.parameters .iter_non_variadic_params() .next() .map(|first_param| first_param.parameter.name.id().as_str()); @@ -1387,7 +1376,7 @@ impl<'ast> Visitor<'ast> for SemanticIndexBuilder<'_, 'ast> { &mut first_parameter_name, ); - builder.visit_body(body); + builder.visit_body(&function_def.body); builder.current_first_parameter_name = first_parameter_name; builder.pop_scope() @@ -1395,7 +1384,7 @@ impl<'ast> Visitor<'ast> for SemanticIndexBuilder<'_, 'ast> { ); // The default value of the parameters needs to be evaluated in the // enclosing scope. - for default in parameters + for default in function_def.parameters .iter_non_variadic_params() .filter_map(|param| param.default.as_deref()) { @@ -1404,21 +1393,21 @@ impl<'ast> Visitor<'ast> for SemanticIndexBuilder<'_, 'ast> { // The symbol for the function name itself has to be evaluated // at the end to match the runtime evaluation of parameter defaults // and return-type annotations. - let symbol = self.add_symbol(name.id.clone()); + let symbol = self.add_symbol(function_def.name.id.clone()); // Record a use of the function name in the scope that it is defined in, so that it // can be used to find previously defined functions with the same name. This is // used to collect all the overloaded definitions of a function. This needs to be // done on the `Identifier` node as opposed to `ExprName` because that's what the // AST uses. - let use_id = self.current_ast_ids().record_use(name); + let use_id = self.current_ast_ids().record_use(&function_def.name); self.current_use_def_map_mut().record_use( symbol.into(), use_id, - NodeKey::from_node(name), + NodeKey::from_node(&function_def.name), ); - self.add_definition(symbol.into(), function_def); + self.add_definition(symbol.into(), &**function_def); self.mark_symbol_used(symbol); } ast::Stmt::ClassDef(class) => { @@ -1443,7 +1432,7 @@ impl<'ast> Visitor<'ast> for SemanticIndexBuilder<'_, 'ast> { // In Python runtime semantics, a class is registered after its scope is evaluated. let symbol = self.add_symbol(class.name.id.clone()); - self.add_definition(symbol.into(), class); + self.add_definition(symbol.into(), &**class); } ast::Stmt::TypeAlias(type_alias) => { let symbol = self.add_symbol( @@ -1453,7 +1442,7 @@ impl<'ast> Visitor<'ast> for SemanticIndexBuilder<'_, 'ast> { .map(|name| name.id.clone()) .unwrap_or("".into()), ); - self.add_definition(symbol.into(), type_alias); + self.add_definition(symbol.into(), &**type_alias); self.visit_expr(&type_alias.name); self.with_type_params( @@ -1468,7 +1457,7 @@ impl<'ast> Visitor<'ast> for SemanticIndexBuilder<'_, 'ast> { } ast::Stmt::Import(node) => { self.current_use_def_map_mut() - .record_node_reachability(NodeKey::from_node(node)); + .record_node_reachability(NodeKey::from_node(&**node)); for (alias_index, alias) in node.names.iter().enumerate() { // Mark the imported module, and all of its parents, as being imported in this @@ -1498,7 +1487,7 @@ impl<'ast> Visitor<'ast> for SemanticIndexBuilder<'_, 'ast> { } ast::Stmt::ImportFrom(node) => { self.current_use_def_map_mut() - .record_node_reachability(NodeKey::from_node(node)); + .record_node_reachability(NodeKey::from_node(&**node)); // If we see: // @@ -1688,12 +1677,7 @@ impl<'ast> Visitor<'ast> for SemanticIndexBuilder<'_, 'ast> { } } - ast::Stmt::Assert(ast::StmtAssert { - test, - msg, - range: _, - node_index: _, - }) => { + ast::Stmt::Assert(node) => { // We model an `assert test, msg` statement here. Conceptually, we can think of // this as being equivalent to the following: // @@ -1713,10 +1697,10 @@ impl<'ast> Visitor<'ast> for SemanticIndexBuilder<'_, 'ast> { // flow states and simplification of reachability constraints, since there is no way // of getting out of that `msg` branch. We simply restore to the post-test state. - self.visit_expr(test); - let predicate = self.build_predicate(test); + self.visit_expr(&node.test); + let predicate = self.build_predicate(&node.test); - if let Some(msg) = msg { + if let Some(msg) = &node.msg { let post_test = self.flow_snapshot(); let negated_predicate = predicate.negated(); self.record_narrowing_constraint(negated_predicate); @@ -1793,48 +1777,40 @@ impl<'ast> Visitor<'ast> for SemanticIndexBuilder<'_, 'ast> { *node.target, ast::Expr::Attribute(_) | ast::Expr::Subscript(_) | ast::Expr::Name(_) ) { - self.push_assignment(node.into()); + self.push_assignment((&**node).into()); self.visit_expr(&node.target); self.pop_assignment(); } else { self.visit_expr(&node.target); } } - ast::Stmt::AugAssign( - aug_assign @ ast::StmtAugAssign { - range: _, - node_index: _, - target, - op, - value, - }, - ) => { + ast::Stmt::AugAssign(aug_assign) => { debug_assert_eq!(&self.current_assignments, &[]); - self.visit_expr(value); + self.visit_expr(&aug_assign.value); - match &**target { + match &*aug_assign.target { ast::Expr::Name(ast::ExprName { id, .. }) - if id == "__all__" && op.is_add() && self.in_module_scope() => + if id == "__all__" && aug_assign.op.is_add() && self.in_module_scope() => { if let ast::Expr::Attribute(ast::ExprAttribute { value, attr, .. }) = - &**value + &*aug_assign.value { if attr == "__all__" { self.add_standalone_expression(value); } } - self.push_assignment(aug_assign.into()); - self.visit_expr(target); + self.push_assignment((&**aug_assign).into()); + self.visit_expr(&aug_assign.target); self.pop_assignment(); } ast::Expr::Name(_) | ast::Expr::Attribute(_) | ast::Expr::Subscript(_) => { - self.push_assignment(aug_assign.into()); - self.visit_expr(target); + self.push_assignment((&**aug_assign).into()); + self.visit_expr(&aug_assign.target); self.pop_assignment(); } _ => { - self.visit_expr(target); + self.visit_expr(&aug_assign.target); } } } @@ -1925,21 +1901,15 @@ impl<'ast> Visitor<'ast> for SemanticIndexBuilder<'_, 'ast> { self.in_type_checking_block = is_outer_block_in_type_checking; } - ast::Stmt::While(ast::StmtWhile { - test, - body, - orelse, - range: _, - node_index: _, - }) => { - self.visit_expr(test); + ast::Stmt::While(node) => { + self.visit_expr(&node.test); let pre_loop = self.flow_snapshot(); - let predicate = self.record_expression_narrowing_constraint(test); + let predicate = self.record_expression_narrowing_constraint(&node.test); self.record_reachability_constraint(predicate); let outer_loop = self.push_loop(); - self.visit_body(body); + self.visit_body(&node.body); let this_loop = self.pop_loop(outer_loop); // We execute the `else` branch once the condition evaluates to false. This could @@ -1961,7 +1931,7 @@ impl<'ast> Visitor<'ast> for SemanticIndexBuilder<'_, 'ast> { self.record_negated_narrowing_constraint(predicate); - self.visit_body(orelse); + self.visit_body(&node.orelse); // Breaking out of a while loop bypasses the `else` clause, so merge in the break // states after visiting `else`. @@ -1969,65 +1939,44 @@ impl<'ast> Visitor<'ast> for SemanticIndexBuilder<'_, 'ast> { self.flow_merge(break_state); } } - ast::Stmt::With(ast::StmtWith { - items, - body, - is_async, - .. - }) => { - for item @ ast::WithItem { - range: _, - node_index: _, - context_expr, - optional_vars, - } in items - { - self.visit_expr(context_expr); - if let Some(optional_vars) = optional_vars.as_deref() { - let context_manager = self.add_standalone_expression(context_expr); + ast::Stmt::With(node) => { + for item in &node.items { + self.visit_expr(&item.context_expr); + if let Some(optional_vars) = item.optional_vars.as_deref() { + let context_manager = self.add_standalone_expression(&item.context_expr); self.add_unpackable_assignment( &Unpackable::WithItem { item, - is_async: *is_async, + is_async: node.is_async, }, optional_vars, context_manager, ); } } - self.visit_body(body); + self.visit_body(&node.body); } - ast::Stmt::For( - for_stmt @ ast::StmtFor { - range: _, - node_index: _, - is_async: _, - target, - iter, - body, - orelse, - }, - ) => { + ast::Stmt::For(for_stmt) => { debug_assert_eq!(&self.current_assignments, &[]); - let iter_expr = self.add_standalone_expression(iter); - self.visit_expr(iter); + let iter_expr = self.add_standalone_expression(&for_stmt.iter); + self.visit_expr(&for_stmt.iter); self.record_ambiguous_reachability(); let pre_loop = self.flow_snapshot(); - self.add_unpackable_assignment(&Unpackable::For(for_stmt), target, iter_expr); + self.add_unpackable_assignment(&Unpackable::For(for_stmt), &for_stmt.target, iter_expr); let outer_loop = self.push_loop(); - self.visit_body(body); + self.visit_body(&for_stmt.body); let this_loop = self.pop_loop(outer_loop); // We may execute the `else` clause without ever executing the body, so merge in // the pre-loop state before visiting `else`. self.flow_merge(pre_loop); - self.visit_body(orelse); + self.visit_body(&for_stmt.orelse); // Breaking out of a `for` loop bypasses the `else` clause, so merge in the break // states after visiting `else`. @@ -2035,30 +1984,25 @@ impl<'ast> Visitor<'ast> for SemanticIndexBuilder<'_, 'ast> { self.flow_merge(break_state); } } - ast::Stmt::Match(ast::StmtMatch { - subject, - cases, - range: _, - node_index: _, - }) => { + ast::Stmt::Match(node) => { debug_assert_eq!(self.current_match_case, None); - let subject_expr = self.add_standalone_expression(subject); - self.visit_expr(subject); - if cases.is_empty() { + let subject_expr = self.add_standalone_expression(&node.subject); + self.visit_expr(&node.subject); + if node.cases.is_empty() { return; } let mut no_case_matched = self.flow_snapshot(); - let has_catchall = cases + let has_catchall = node.cases .last() .is_some_and(|case| case.guard.is_none() && case.pattern.is_wildcard()); let mut post_case_snapshots = vec![]; let mut previous_pattern: Option> = None; - for (i, case) in cases.iter().enumerate() { + for (i, case) in node.cases.iter().enumerate() { self.current_match_case = Some(CurrentMatchCase::new(&case.pattern)); self.visit_pattern(&case.pattern); self.current_match_case = None; @@ -2100,7 +2044,7 @@ impl<'ast> Visitor<'ast> for SemanticIndexBuilder<'_, 'ast> { post_case_snapshots.push(self.flow_snapshot()); - if i != cases.len() - 1 || !has_catchall { + if i != node.cases.len() - 1 || !has_catchall { // We need to restore the state after each case, but not after the last // one. The last one will just become the state that we merge the other // snapshots into. @@ -2124,15 +2068,7 @@ impl<'ast> Visitor<'ast> for SemanticIndexBuilder<'_, 'ast> { self.flow_merge(post_clause_state); } } - ast::Stmt::Try(ast::StmtTry { - body, - handlers, - orelse, - finalbody, - is_star, - range: _, - node_index: _, - }) => { + ast::Stmt::Try(node) => { self.record_ambiguous_reachability(); // Save the state prior to visiting any of the `try` block. @@ -2146,7 +2082,7 @@ impl<'ast> Visitor<'ast> for SemanticIndexBuilder<'_, 'ast> { self.try_node_context_stack_manager.push_context(); // Visit the `try` block! - self.visit_body(body); + self.visit_body(&node.body); let mut post_except_states = vec![]; @@ -2154,7 +2090,7 @@ impl<'ast> Visitor<'ast> for SemanticIndexBuilder<'_, 'ast> { // while visiting the `try` block let try_block_snapshots = self.try_node_context_stack_manager.pop_context(); - if !handlers.is_empty() { + if !node.handlers.is_empty() { // Save the state immediately *after* visiting the `try` block // but *before* we prepare for visiting the `except` block(s). // @@ -2170,9 +2106,9 @@ impl<'ast> Visitor<'ast> for SemanticIndexBuilder<'_, 'ast> { } let pre_except_state = self.flow_snapshot(); - let num_handlers = handlers.len(); + let num_handlers = node.handlers.len(); - for (i, except_handler) in handlers.iter().enumerate() { + for (i, except_handler) in node.handlers.iter().enumerate() { let ast::ExceptHandler::ExceptHandler(except_handler) = except_handler; let ast::ExceptHandlerExceptHandler { name: symbol_name, @@ -2196,7 +2132,7 @@ impl<'ast> Visitor<'ast> for SemanticIndexBuilder<'_, 'ast> { symbol.into(), DefinitionNodeRef::ExceptHandler(ExceptHandlerDefinitionNodeRef { handler: except_handler, - is_star: *is_star, + is_star: node.is_star, }), ); Some(symbol) @@ -2225,7 +2161,7 @@ impl<'ast> Visitor<'ast> for SemanticIndexBuilder<'_, 'ast> { self.flow_restore(post_try_block_state); } - self.visit_body(orelse); + self.visit_body(&node.orelse); for post_except_state in post_except_states { self.flow_merge(post_except_state); @@ -2241,7 +2177,7 @@ impl<'ast> Visitor<'ast> for SemanticIndexBuilder<'_, 'ast> { // For more details, see: // - https://astral-sh.notion.site/Exception-handler-control-flow-11348797e1ca80bb8ce1e9aedbbe439d // - https://github.com/astral-sh/ruff/pull/13633#discussion_r1788626702 - self.visit_body(finalbody); + self.visit_body(&node.finalbody); } ast::Stmt::Raise(_) | ast::Stmt::Return(_) | ast::Stmt::Continue(_) => { @@ -2258,12 +2194,8 @@ impl<'ast> Visitor<'ast> for SemanticIndexBuilder<'_, 'ast> { // Everything in the current block after a terminal statement is unreachable. self.mark_unreachable(); } - ast::Stmt::Global(ast::StmtGlobal { - range: _, - node_index: _, - names, - }) => { - for name in names { + ast::Stmt::Global(node) => { + for name in &node.names { self.scopes_by_expression .record_expression(name, self.current_scope()); let symbol_id = self.add_symbol(name.id.clone()); @@ -2295,12 +2227,8 @@ impl<'ast> Visitor<'ast> for SemanticIndexBuilder<'_, 'ast> { } walk_stmt(self, stmt); } - ast::Stmt::Nonlocal(ast::StmtNonlocal { - range: _, - node_index: _, - names, - }) => { - for name in names { + ast::Stmt::Nonlocal(node) => { + for name in &node.names { self.scopes_by_expression .record_expression(name, self.current_scope()); let symbol_id = self.add_symbol(name.id.clone()); @@ -2339,14 +2267,10 @@ impl<'ast> Visitor<'ast> for SemanticIndexBuilder<'_, 'ast> { } walk_stmt(self, stmt); } - ast::Stmt::Delete(ast::StmtDelete { - targets, - range: _, - node_index: _, - }) => { + ast::Stmt::Delete(node) => { // We will check the target expressions and then delete them. walk_stmt(self, stmt); - for target in targets { + for target in &node.targets { if let Some(mut target) = PlaceExpr::try_from_expr(target) { if let PlaceExpr::Symbol(symbol) = &mut target { // `del x` behaves like an assignment in that it forces all references diff --git a/crates/ty_python_semantic/src/semantic_index/re_exports.rs b/crates/ty_python_semantic/src/semantic_index/re_exports.rs index 693c0f3437..6ab0e93cd4 100644 --- a/crates/ty_python_semantic/src/semantic_index/re_exports.rs +++ b/crates/ty_python_semantic/src/semantic_index/re_exports.rs @@ -173,70 +173,39 @@ impl<'db> Visitor<'db> for ExportFinder<'db> { fn visit_stmt(&mut self, stmt: &'db ast::Stmt) { match stmt { - ast::Stmt::ClassDef(ast::StmtClassDef { - name, - decorator_list, - arguments, - type_params: _, // We don't want to visit the type params of the class - body: _, // We don't want to visit the body of the class - range: _, - node_index: _, - }) => { - self.possibly_add_export(&name.id, PossibleExportKind::Normal); - for decorator in decorator_list { + ast::Stmt::ClassDef(class_def) => { + self.possibly_add_export(&class_def.name.id, PossibleExportKind::Normal); + for decorator in &class_def.decorator_list { self.visit_decorator(decorator); } - if let Some(arguments) = arguments { + if let Some(arguments) = &class_def.arguments { self.visit_arguments(arguments); } } - ast::Stmt::FunctionDef(ast::StmtFunctionDef { - name, - decorator_list, - parameters, - returns, - type_params: _, // We don't want to visit the type params of the function - body: _, // We don't want to visit the body of the function - range: _, - node_index: _, - is_async: _, - }) => { - self.possibly_add_export(&name.id, PossibleExportKind::Normal); - for decorator in decorator_list { + ast::Stmt::FunctionDef(func_def) => { + self.possibly_add_export(&func_def.name.id, PossibleExportKind::Normal); + for decorator in &func_def.decorator_list { self.visit_decorator(decorator); } - self.visit_parameters(parameters); - if let Some(returns) = returns { + self.visit_parameters(&func_def.parameters); + if let Some(returns) = &func_def.returns { self.visit_expr(returns); } } - ast::Stmt::AnnAssign(ast::StmtAnnAssign { - target, - value, - annotation, - simple: _, - range: _, - node_index: _, - }) => { - if value.is_some() || self.visiting_stub_file { - self.visit_expr(target); + ast::Stmt::AnnAssign(ann_assign) => { + if ann_assign.value.is_some() || self.visiting_stub_file { + self.visit_expr(&ann_assign.target); } - self.visit_expr(annotation); - if let Some(value) = value { + self.visit_expr(&ann_assign.annotation); + if let Some(value) = &ann_assign.value { self.visit_expr(value); } } - ast::Stmt::TypeAlias(ast::StmtTypeAlias { - name, - type_params: _, - value: _, - range: _, - node_index: _, - }) => { - self.visit_expr(name); + ast::Stmt::TypeAlias(type_alias) => { + self.visit_expr(&type_alias.name); // Neither walrus expressions nor statements cannot appear in type aliases; // no need to recursively visit the `value` or `type_params` }