Add box to stmt

This commit is contained in:
Charlie Marsh 2025-12-11 11:24:31 -05:00
parent c9155d5e72
commit cc850ec348
123 changed files with 2523 additions and 2786 deletions

View File

@ -42,13 +42,14 @@ impl<'a> Collector<'a> {
impl<'ast> SourceOrderVisitor<'ast> for Collector<'_> { impl<'ast> SourceOrderVisitor<'ast> for Collector<'_> {
fn visit_stmt(&mut self, stmt: &'ast Stmt) { fn visit_stmt(&mut self, stmt: &'ast Stmt) {
match stmt { match stmt {
Stmt::ImportFrom(ast::StmtImportFrom { Stmt::ImportFrom(import_from) => {
let ast::StmtImportFrom {
names, names,
module, module,
level, level,
range: _, range: _,
node_index: _, node_index: _,
}) => { } = &**import_from;
let module = module.as_deref(); let module = module.as_deref();
let level = *level; let level = *level;
for alias in names { for alias in names {
@ -87,24 +88,26 @@ impl<'ast> SourceOrderVisitor<'ast> for Collector<'_> {
} }
} }
} }
Stmt::Import(ast::StmtImport { Stmt::Import(import_stmt) => {
let ast::StmtImport {
names, names,
range: _, range: _,
node_index: _, node_index: _,
}) => { } = &**import_stmt;
for alias in names { for alias in names {
if let Some(module_name) = ModuleName::new(alias.name.as_str()) { if let Some(module_name) = ModuleName::new(alias.name.as_str()) {
self.imports.push(CollectedImport::Import(module_name)); self.imports.push(CollectedImport::Import(module_name));
} }
} }
} }
Stmt::If(ast::StmtIf { Stmt::If(if_stmt) => {
let ast::StmtIf {
test, test,
body, body,
elif_else_clauses, elif_else_clauses,
range: _, range: _,
node_index: _, node_index: _,
}) => { } = &**if_stmt;
// Skip TYPE_CHECKING blocks if not requested // Skip TYPE_CHECKING blocks if not requested
if self.type_checking_imports || !is_type_checking_condition(test) { if self.type_checking_imports || !is_type_checking_condition(test) {
self.visit_body(body); self.visit_body(body);

View File

@ -17,11 +17,12 @@ use ruff_python_ast::PythonVersion;
/// Run lint rules over a [`Stmt`] syntax node. /// Run lint rules over a [`Stmt`] syntax node.
pub(crate) fn statement(stmt: &Stmt, checker: &mut Checker) { pub(crate) fn statement(stmt: &Stmt, checker: &mut Checker) {
match stmt { match stmt {
Stmt::Global(ast::StmtGlobal { Stmt::Global(global) => {
let ast::StmtGlobal {
names, names,
range: _, range: _,
node_index: _, node_index: _,
}) => { } = &**global;
if checker.is_rule_enabled(Rule::GlobalAtModuleLevel) { if checker.is_rule_enabled(Rule::GlobalAtModuleLevel) {
pylint::rules::global_at_module_level(checker, stmt); pylint::rules::global_at_module_level(checker, stmt);
} }
@ -31,13 +32,12 @@ pub(crate) fn statement(stmt: &Stmt, checker: &mut Checker) {
} }
} }
} }
Stmt::Nonlocal( Stmt::Nonlocal(nonlocal) => {
nonlocal @ ast::StmtNonlocal { let ast::StmtNonlocal {
names, names,
range: _, range: _,
node_index: _, node_index: _,
}, } = &**nonlocal;
) => {
if checker.is_rule_enabled(Rule::AmbiguousVariableName) { if checker.is_rule_enabled(Rule::AmbiguousVariableName) {
for name in names { for name in names {
pycodestyle::rules::ambiguous_variable_name(checker, name, name.range()); 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); pylint::rules::nonlocal_and_global(checker, nonlocal);
} }
} }
Stmt::FunctionDef( Stmt::FunctionDef(function_def) => {
function_def @ ast::StmtFunctionDef { let ast::StmtFunctionDef {
is_async, is_async,
name, name,
decorator_list, decorator_list,
@ -58,8 +58,7 @@ pub(crate) fn statement(stmt: &Stmt, checker: &mut Checker) {
type_params: _, type_params: _,
range: _, range: _,
node_index: _, node_index: _,
}, } = &**function_def;
) => {
if checker.is_rule_enabled(Rule::DjangoNonLeadingReceiverDecorator) { if checker.is_rule_enabled(Rule::DjangoNonLeadingReceiverDecorator) {
flake8_django::rules::non_leading_receiver_decorator(checker, decorator_list); 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); pylint::rules::in_function(checker, name, body);
} }
if checker.is_rule_enabled(Rule::ReimplementedOperator) { 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) { if checker.is_rule_enabled(Rule::SslWithBadDefaults) {
flake8_bandit::rules::ssl_with_bad_defaults(checker, function_def); 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); pylint::rules::return_in_init(checker, stmt);
} }
} }
Stmt::ClassDef( Stmt::ClassDef(class_def) => {
class_def @ ast::StmtClassDef { let ast::StmtClassDef {
name, name,
arguments, arguments,
type_params: _, type_params: _,
@ -365,8 +364,7 @@ pub(crate) fn statement(stmt: &Stmt, checker: &mut Checker) {
body, body,
range: _, range: _,
node_index: _, node_index: _,
}, } = &**class_def;
) => {
if checker.is_rule_enabled(Rule::NoClassmethodDecorator) { if checker.is_rule_enabled(Rule::NoClassmethodDecorator) {
pylint::rules::no_classmethod_decorator(checker, stmt); 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); ruff::rules::implicit_class_var_in_dataclass(checker, class_def);
} }
} }
Stmt::Import(ast::StmtImport { Stmt::Import(import) => {
let ast::StmtImport {
names, names,
range: _, range: _,
node_index: _, node_index: _,
}) => { } = &**import;
if checker.is_rule_enabled(Rule::MultipleImportsOnOneLine) { if checker.is_rule_enabled(Rule::MultipleImportsOnOneLine) {
pycodestyle::rules::multiple_imports_on_one_line(checker, stmt, names); 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); 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) { if checker.is_rule_enabled(Rule::NonAsciiImportName) {
pylint::rules::non_ascii_module_import(checker, alias); 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) { 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) { if checker.is_rule_enabled(Rule::ImportSelf) {
pylint::rules::import_self(checker, alias, checker.module.qualified_name()); 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( Stmt::ImportFrom(import_from) => {
import_from @ ast::StmtImportFrom { let level = import_from.level;
names, let module = import_from.module.as_deref();
module,
level,
range: _,
node_index: _,
},
) => {
let level = *level;
let module = module.as_deref();
if checker.is_rule_enabled(Rule::ModuleImportNotAtTopOfFile) { if checker.is_rule_enabled(Rule::ModuleImportNotAtTopOfFile) {
pycodestyle::rules::module_import_not_at_top_of_file(checker, stmt); 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); pylint::rules::import_outside_top_level(checker, stmt);
} }
if checker.is_rule_enabled(Rule::GlobalStatement) { 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() { if let Some(asname) = name.asname.as_ref() {
pylint::rules::global_statement(checker, asname); pylint::rules::global_statement(checker, asname);
} else { } else {
@ -708,7 +699,7 @@ pub(crate) fn statement(stmt: &Stmt, checker: &mut Checker) {
} }
} }
if checker.is_rule_enabled(Rule::NonAsciiImportName) { 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); 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 checker.is_rule_enabled(Rule::UnnecessaryBuiltinImport) {
if let Some(module) = module { if let Some(module) = module {
pyupgrade::rules::unnecessary_builtin_import( 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, &stmt,
); );
for alias in names { for alias in &import_from.names {
if &alias.name == "*" { if &alias.name == "*" {
continue; continue;
} }
@ -789,7 +780,7 @@ pub(crate) fn statement(stmt: &Stmt, checker: &mut Checker) {
flake8_pyi::rules::from_future_import(checker, import_from); 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 == "*" { if module != Some("__future__") && &alias.name == "*" {
// F403 // F403
checker.report_diagnostic_if_enabled( checker.report_diagnostic_if_enabled(
@ -890,7 +881,7 @@ pub(crate) fn statement(stmt: &Stmt, checker: &mut Checker) {
checker, checker,
level, level,
module, module,
names, &import_from.names,
checker.module.qualified_name(), 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); 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 checker.is_rule_enabled(Rule::RaiseNotImplemented) {
if let Some(expr) = exc { if let Some(expr) = &raise.exc {
pyflakes::rules::raise_not_implemented(checker, expr); pyflakes::rules::raise_not_implemented(checker, expr);
} }
} }
if checker.is_rule_enabled(Rule::RaiseLiteral) { 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); flake8_bugbear::rules::raise_literal(checker, exc);
} }
} }
@ -922,34 +913,34 @@ pub(crate) fn statement(stmt: &Stmt, checker: &mut Checker) {
Rule::FStringInException, Rule::FStringInException,
Rule::DotFormatInException, Rule::DotFormatInException,
]) { ]) {
if let Some(exc) = exc { if let Some(exc) = &raise.exc {
flake8_errmsg::rules::string_in_exception(checker, stmt, exc); flake8_errmsg::rules::string_in_exception(checker, stmt, exc);
} }
} }
if checker.is_rule_enabled(Rule::OSErrorAlias) { 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); pyupgrade::rules::os_error_alias_raise(checker, item);
} }
} }
if checker.is_rule_enabled(Rule::TimeoutErrorAlias) { if checker.is_rule_enabled(Rule::TimeoutErrorAlias) {
if checker.target_version() >= PythonVersion::PY310 { 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); pyupgrade::rules::timeout_error_alias_raise(checker, item);
} }
} }
} }
if checker.is_rule_enabled(Rule::RaiseVanillaClass) { 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); tryceratops::rules::raise_vanilla_class(checker, expr);
} }
} }
if checker.is_rule_enabled(Rule::RaiseVanillaArgs) { 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); tryceratops::rules::raise_vanilla_args(checker, expr);
} }
} }
if checker.is_rule_enabled(Rule::UnnecessaryParenOnRaiseException) { 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); 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); 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 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); 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); ruff::rules::sort_dunder_all_aug_assign(checker, aug_assign);
} }
} }
Stmt::If( Stmt::If(if_) => {
if_ @ ast::StmtIf {
test,
elif_else_clauses,
..
},
) => {
if checker.is_rule_enabled(Rule::TooManyNestedBlocks) { if checker.is_rule_enabled(Rule::TooManyNestedBlocks) {
pylint::rules::too_many_nested_blocks(checker, stmt); pylint::rules::too_many_nested_blocks(checker, stmt);
} }
@ -1036,33 +1021,33 @@ pub(crate) fn statement(stmt: &Stmt, checker: &mut Checker) {
Rule::PatchVersionComparison, Rule::PatchVersionComparison,
Rule::WrongTupleLengthVersionComparison, 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 { for value in values {
flake8_pyi::rules::unrecognized_version_info(checker, value); flake8_pyi::rules::unrecognized_version_info(checker, value);
} }
} else { } else {
flake8_pyi::rules::unrecognized_version_info(checker, test); flake8_pyi::rules::unrecognized_version_info(checker, &if_.test);
} }
} }
if checker.any_rule_enabled(&[ if checker.any_rule_enabled(&[
Rule::UnrecognizedPlatformCheck, Rule::UnrecognizedPlatformCheck,
Rule::UnrecognizedPlatformName, 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 { for value in values {
flake8_pyi::rules::unrecognized_platform(checker, value); flake8_pyi::rules::unrecognized_platform(checker, value);
} }
} else { } else {
flake8_pyi::rules::unrecognized_platform(checker, test); flake8_pyi::rules::unrecognized_platform(checker, &if_.test);
} }
} }
if checker.is_rule_enabled(Rule::ComplexIfStatementInStub) { 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 { for value in values {
flake8_pyi::rules::complex_if_statement_in_stub(checker, value); flake8_pyi::rules::complex_if_statement_in_stub(checker, value);
} }
} else { } 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); bad_version_info_comparison(checker, if_.test.as_ref(), has_else_clause);
for clause in elif_else_clauses { for clause in &if_.elif_else_clauses {
if let Some(test) = clause.test.as_ref() { if let Some(test) = clause.test.as_ref() {
bad_version_info_comparison(checker, test, has_else_clause); 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_); ruff::rules::if_key_in_dict_del(checker, if_);
} }
if checker.is_rule_enabled(Rule::NeedlessElse) { if checker.is_rule_enabled(Rule::NeedlessElse) {
ruff::rules::needless_else(checker, if_.into()); ruff::rules::needless_else(checker, (&**if_).into());
} }
} }
Stmt::Assert( Stmt::Assert(assert_stmt) => {
assert_stmt @ ast::StmtAssert {
test,
msg,
range: _,
node_index: _,
},
) => {
if !checker.semantic.in_type_checking_block() { if !checker.semantic.in_type_checking_block() {
if checker.is_rule_enabled(Rule::Assert) { if checker.is_rule_enabled(Rule::Assert) {
flake8_bandit::rules::assert_used(checker, stmt); flake8_bandit::rules::assert_used(checker, stmt);
} }
} }
if checker.is_rule_enabled(Rule::AssertTuple) { 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) { 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) { 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) { if checker.is_rule_enabled(Rule::PytestCompositeAssertion) {
flake8_pytest_style::rules::composite_condition( flake8_pytest_style::rules::composite_condition(
checker, checker,
stmt, stmt,
test, &assert_stmt.test,
msg.as_deref(), assert_stmt.msg.as_deref(),
); );
} }
if checker.is_rule_enabled(Rule::AssertOnStringLiteral) { 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) { 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) { if checker.is_rule_enabled(Rule::AssertWithPrintMessage) {
ruff::rules::assert_with_print_message(checker, assert_stmt); 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); 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) { if checker.is_rule_enabled(Rule::TooManyNestedBlocks) {
pylint::rules::too_many_nested_blocks(checker, stmt); pylint::rules::too_many_nested_blocks(checker, stmt);
} }
if checker.is_rule_enabled(Rule::AssertRaisesException) { 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) { 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) { 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) { if checker.is_rule_enabled(Rule::MultipleWithStatements) {
flake8_simplify::rules::multiple_with_statements( 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); pylint::rules::useless_with_lock(checker, with_stmt);
} }
if checker.is_rule_enabled(Rule::CancelScopeNoCheckpoint) { 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) { if checker.is_rule_enabled(Rule::TooManyNestedBlocks) {
pylint::rules::too_many_nested_blocks(checker, stmt); 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)); flake8_bugbear::rules::function_uses_loop_variable(checker, &Node::Stmt(stmt));
} }
if checker.is_rule_enabled(Rule::UselessElseOnLoop) { 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) { 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) { if checker.is_rule_enabled(Rule::AsyncBusyWait) {
flake8_async::rules::async_busy_wait(checker, while_stmt); flake8_async::rules::async_busy_wait(checker, while_stmt);
} }
if checker.is_rule_enabled(Rule::NeedlessElse) { 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( Stmt::For(for_stmt) => {
for_stmt @ ast::StmtFor {
target,
body,
iter,
orelse,
is_async,
range: _,
node_index: _,
},
) => {
if checker.is_rule_enabled(Rule::TooManyNestedBlocks) { if checker.is_rule_enabled(Rule::TooManyNestedBlocks) {
pylint::rules::too_many_nested_blocks(checker, stmt); 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()); checker.analyze.for_loops.push(checker.semantic.snapshot());
} }
if checker.is_rule_enabled(Rule::LoopVariableOverridesIterator) { 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) { if checker.is_rule_enabled(Rule::FunctionUsesLoopVariable) {
flake8_bugbear::rules::function_uses_loop_variable(checker, &Node::Stmt(stmt)); flake8_bugbear::rules::function_uses_loop_variable(checker, &Node::Stmt(stmt));
} }
if checker.is_rule_enabled(Rule::ReuseOfGroupbyGenerator) { 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) { 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) { if checker.is_rule_enabled(Rule::RedefinedLoopName) {
pylint::rules::redefined_loop_name(checker, stmt); pylint::rules::redefined_loop_name(checker, stmt);
} }
if checker.is_rule_enabled(Rule::IterationOverSet) { 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) { 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) { if checker.is_rule_enabled(Rule::ManualListCopy) {
perflint::rules::manual_list_copy(checker, for_stmt); 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); pylint::rules::modified_iterating_set(checker, for_stmt);
} }
if checker.is_rule_enabled(Rule::UnnecessaryListCast) { 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) { if checker.is_rule_enabled(Rule::UnnecessaryListIndexLookup) {
pylint::rules::unnecessary_list_index_lookup(checker, for_stmt); 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) { if checker.is_rule_enabled(Rule::ReadlinesInFor) {
refurb::rules::readlines_in_for(checker, for_stmt); refurb::rules::readlines_in_for(checker, for_stmt);
} }
if !*is_async { if !for_stmt.is_async {
if checker.is_rule_enabled(Rule::ReimplementedBuiltin) { if checker.is_rule_enabled(Rule::ReimplementedBuiltin) {
flake8_simplify::rules::convert_for_loop_to_any_all(checker, stmt); 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); flake8_simplify::rules::key_in_dict_for(checker, for_stmt);
} }
if checker.is_rule_enabled(Rule::TryExceptInLoop) { 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) { if checker.is_rule_enabled(Rule::ForLoopSetMutations) {
refurb::rules::for_loop_set_mutations(checker, for_stmt); 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) { 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( Stmt::Try(try_stmt) => {
try_stmt @ ast::StmtTry {
body,
handlers,
orelse,
finalbody,
..
},
) => {
if checker.is_rule_enabled(Rule::TooManyNestedBlocks) { if checker.is_rule_enabled(Rule::TooManyNestedBlocks) {
pylint::rules::too_many_nested_blocks(checker, stmt); pylint::rules::too_many_nested_blocks(checker, stmt);
} }
if checker.is_rule_enabled(Rule::JumpStatementInFinally) { 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.is_rule_enabled(Rule::ContinueInFinally) {
if checker.target_version() <= PythonVersion::PY38 { 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) { 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(&[ if checker.any_rule_enabled(&[
Rule::DuplicateHandlerException, Rule::DuplicateHandlerException,
Rule::DuplicateTryBlockException, 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) { 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) { 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.is_rule_enabled(Rule::TimeoutErrorAlias) {
if checker.target_version() >= PythonVersion::PY310 { 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) { 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) { if checker.is_rule_enabled(Rule::SuppressibleException) {
flake8_simplify::rules::suppressible_exception( 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) { if checker.is_rule_enabled(Rule::ReturnInTryExceptFinally) {
flake8_simplify::rules::return_in_try_except_finally( 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) { 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) { 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) { 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) { 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) { 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) { 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) { 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) { if checker.is_rule_enabled(Rule::SelfOrClsAssignment) {
for target in targets { for target in &assign.targets {
pylint::rules::self_or_cls_assignment(checker, target); pylint::rules::self_or_cls_assignment(checker, target);
} }
} }
if checker.is_rule_enabled(Rule::RedeclaredAssignedName) { 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 checker.is_rule_enabled(Rule::LambdaAssignment) {
if let [target] = &targets[..] { if let [target] = &assign.targets[..] {
pycodestyle::rules::lambda_assignment(checker, target, value, None, stmt); pycodestyle::rules::lambda_assignment(checker, target, &assign.value, None, stmt);
} }
} }
if checker.is_rule_enabled(Rule::AssignmentToOsEnviron) { 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) { 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) { if checker.is_rule_enabled(Rule::GlobalStatement) {
for target in targets { for target in &assign.targets {
if let Expr::Name(ast::ExprName { id, .. }) = target { if let Expr::Name(name_expr) = target {
pylint::rules::global_statement(checker, id); pylint::rules::global_statement(checker, &name_expr.id);
} }
} }
} }
if checker.is_rule_enabled(Rule::UselessMetaclassType) { 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) { if checker.is_rule_enabled(Rule::ConvertTypedDictFunctionalToClass) {
pyupgrade::rules::convert_typed_dict_functional_to_class( 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) { if checker.is_rule_enabled(Rule::ConvertNamedTupleFunctionalToClass) {
pyupgrade::rules::convert_named_tuple_functional_to_class( 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) { 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) { 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) { if checker.is_rule_enabled(Rule::SelfAssigningVariable) {
pylint::rules::self_assignment(checker, assign); pylint::rules::self_assignment(checker, assign);
} }
if checker.is_rule_enabled(Rule::TypeParamNameMismatch) { 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) { 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) { 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) { if checker.is_rule_enabled(Rule::NonAugmentedAssignment) {
pylint::rules::non_augmented_assignment(checker, assign); 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()) .any(|scope| scope.kind.is_function())
{ {
if checker.is_rule_enabled(Rule::UnprefixedTypeParam) { 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) { 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) { if checker.is_rule_enabled(Rule::UnannotatedAssignmentInStub) {
flake8_pyi::rules::unannotated_assignment_in_stub( flake8_pyi::rules::unannotated_assignment_in_stub(
checker, targets, value, checker, &assign.targets, &assign.value,
); );
} }
if checker.is_rule_enabled(Rule::ComplexAssignmentInStub) { 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) { if checker.is_rule_enabled(Rule::TypeAliasWithoutAnnotation) {
flake8_pyi::rules::type_alias_without_annotation( 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); pyupgrade::rules::non_pep695_type_alias_type(checker, assign);
} }
} }
Stmt::AnnAssign( Stmt::AnnAssign(assign_stmt) => {
assign_stmt @ ast::StmtAnnAssign { let target = &assign_stmt.target;
target, let annotation = &assign_stmt.annotation;
value, if let Some(value) = &assign_stmt.value {
annotation,
..
},
) => {
if let Some(value) = value {
if checker.is_rule_enabled(Rule::LambdaAssignment) { if checker.is_rule_enabled(Rule::LambdaAssignment) {
pycodestyle::rules::lambda_assignment( pycodestyle::rules::lambda_assignment(
checker, checker,
@ -1506,7 +1461,7 @@ pub(crate) fn statement(stmt: &Stmt, checker: &mut Checker) {
flake8_bugbear::rules::unintentional_type_annotation( flake8_bugbear::rules::unintentional_type_annotation(
checker, checker,
target, target,
value.as_deref(), assign_stmt.value.as_deref(),
stmt, stmt,
); );
} }
@ -1514,7 +1469,7 @@ pub(crate) fn statement(stmt: &Stmt, checker: &mut Checker) {
pyupgrade::rules::non_pep695_type_alias(checker, assign_stmt); pyupgrade::rules::non_pep695_type_alias(checker, assign_stmt);
} }
if checker.is_rule_enabled(Rule::HardcodedPasswordString) { 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( flake8_bandit::rules::assign_hardcoded_password_string(
checker, checker,
value, value,
@ -1526,7 +1481,7 @@ pub(crate) fn statement(stmt: &Stmt, checker: &mut Checker) {
ruff::rules::sort_dunder_all_ann_assign(checker, assign_stmt); ruff::rules::sort_dunder_all_ann_assign(checker, assign_stmt);
} }
if checker.source_type.is_stub() { 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) { if checker.is_rule_enabled(Rule::AssignmentDefaultInStub) {
// Ignore assignments in function bodies; those are covered by other rules. // Ignore assignments in function bodies; those are covered by other rules.
if !checker 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) { if checker.is_rule_enabled(Rule::SnakeCaseTypeAlias) {
flake8_pyi::rules::snake_case_type_alias(checker, name); 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); flake8_pyi::rules::t_suffixed_type_alias(checker, name);
} }
} }
Stmt::Delete( Stmt::Delete(delete) => {
delete @ ast::StmtDelete { let targets = &delete.targets;
targets,
range: _,
node_index: _,
},
) => {
if checker.is_rule_enabled(Rule::GlobalStatement) { if checker.is_rule_enabled(Rule::GlobalStatement) {
for target in targets { for target in targets {
if let Expr::Name(ast::ExprName { id, .. }) = target { if let Expr::Name(name_expr) = target {
pylint::rules::global_statement(checker, id); 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); pylint::rules::useless_exception_statement(checker, expr);
} }
} }
Stmt::Match(ast::StmtMatch { Stmt::Match(match_stmt) => {
let ast::StmtMatch {
subject: _, subject: _,
cases, cases,
range: _, range: _,
node_index: _, node_index: _,
}) => { } = &**match_stmt;
if checker.is_rule_enabled(Rule::NanComparison) { if checker.is_rule_enabled(Rule::NanComparison) {
pylint::rules::nan_comparison_match(checker, cases); pylint::rules::nan_comparison_match(checker, cases);
} }

View File

@ -782,7 +782,10 @@ impl SemanticSyntaxContext for Checker<'_> {
for scope in self.semantic.current_scopes() { for scope in self.semantic.current_scopes() {
match scope.kind { match scope.kind {
ScopeKind::Class(_) | ScopeKind::Lambda(_) => return false, 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::Generator { .. }
| ScopeKind::Module | ScopeKind::Module
| ScopeKind::Type | ScopeKind::Type
@ -870,9 +873,13 @@ impl SemanticSyntaxContext for Checker<'_> {
for parent in self.semantic.current_statements().skip(1) { for parent in self.semantic.current_statements().skip(1) {
match parent { match parent {
Stmt::For(ast::StmtFor { orelse, .. }) Stmt::For(node) => {
| Stmt::While(ast::StmtWhile { orelse, .. }) => { if !node.orelse.contains(child) {
if !orelse.contains(child) { return true;
}
}
Stmt::While(node) => {
if !node.orelse.contains(child) {
return true; return true;
} }
} }
@ -888,7 +895,8 @@ impl SemanticSyntaxContext for Checker<'_> {
fn is_bound_parameter(&self, name: &str) -> bool { fn is_bound_parameter(&self, name: &str) -> bool {
match self.semantic.current_scope().kind { match self.semantic.current_scope().kind {
ScopeKind::Function(ast::StmtFunctionDef { parameters, .. }) => { ScopeKind::Function(function_def) => {
let parameters = &function_def.parameters;
parameters.includes(name) parameters.includes(name)
} }
ScopeKind::Class(_) ScopeKind::Class(_)
@ -932,12 +940,13 @@ impl<'a> Visitor<'a> for Checker<'a> {
{ {
self.semantic.flags |= SemanticModelFlags::MODULE_DOCSTRING_BOUNDARY; self.semantic.flags |= SemanticModelFlags::MODULE_DOCSTRING_BOUNDARY;
} }
Stmt::ImportFrom(ast::StmtImportFrom { module, names, .. }) => { Stmt::ImportFrom(node) => {
self.semantic.flags |= SemanticModelFlags::MODULE_DOCSTRING_BOUNDARY; self.semantic.flags |= SemanticModelFlags::MODULE_DOCSTRING_BOUNDARY;
// Allow __future__ imports until we see a non-__future__ import. // Allow __future__ imports until we see a non-__future__ import.
if let Some("__future__") = module.as_deref() { if let Some("__future__") = node.module.as_deref() {
if names if node
.names
.iter() .iter()
.any(|alias| alias.name.as_str() == "annotations") .any(|alias| alias.name.as_str() == "annotations")
{ {
@ -981,20 +990,22 @@ impl<'a> Visitor<'a> for Checker<'a> {
// Step 1: Binding // Step 1: Binding
match stmt { match stmt {
Stmt::AugAssign(ast::StmtAugAssign { Stmt::AugAssign(node) => {
let ast::StmtAugAssign {
target, target,
op: _, op: _,
value: _, value: _,
range: _, range: _,
node_index: _, node_index: _,
}) => { } = &**node;
self.handle_node_load(target); self.handle_node_load(target);
} }
Stmt::Import(ast::StmtImport { Stmt::Import(node) => {
let ast::StmtImport {
names, names,
range: _, range: _,
node_index: _, node_index: _,
}) => { } = &**node;
if self.semantic.at_top_level() { if self.semantic.at_top_level() {
self.importer.visit_import(stmt); self.importer.visit_import(stmt);
} }
@ -1043,13 +1054,14 @@ impl<'a> Visitor<'a> for Checker<'a> {
} }
} }
} }
Stmt::ImportFrom(ast::StmtImportFrom { Stmt::ImportFrom(node) => {
let ast::StmtImportFrom {
names, names,
module, module,
level, level,
range: _, range: _,
node_index: _, node_index: _,
}) => { } = &**node;
if self.semantic.at_top_level() { if self.semantic.at_top_level() {
self.importer.visit_import(stmt); self.importer.visit_import(stmt);
} }
@ -1110,11 +1122,12 @@ impl<'a> Visitor<'a> for Checker<'a> {
} }
} }
} }
Stmt::Global(ast::StmtGlobal { Stmt::Global(node) => {
let ast::StmtGlobal {
names, names,
range: _, range: _,
node_index: _, node_index: _,
}) => { } = &**node;
if !self.semantic.scope_id.is_global() { if !self.semantic.scope_id.is_global() {
for name in names { for name in names {
let binding_id = self.semantic.global_scope().get(name); let binding_id = self.semantic.global_scope().get(name);
@ -1136,11 +1149,12 @@ impl<'a> Visitor<'a> for Checker<'a> {
} }
} }
} }
Stmt::Nonlocal(ast::StmtNonlocal { Stmt::Nonlocal(node) => {
let ast::StmtNonlocal {
names, names,
range: _, range: _,
node_index: _, node_index: _,
}) => { } = &**node;
if !self.semantic.scope_id.is_global() { if !self.semantic.scope_id.is_global() {
for name in names { for name in names {
if let Some((scope_id, binding_id)) = self.semantic.nonlocal(name) { 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 // Step 2: Traversal
match stmt { match stmt {
Stmt::FunctionDef( Stmt::FunctionDef(function_def) => {
function_def @ ast::StmtFunctionDef { let name = &function_def.name;
name, let body = &function_def.body;
body, let parameters = &function_def.parameters;
parameters, let decorator_list = &function_def.decorator_list;
decorator_list, let returns = &function_def.returns;
returns, let type_params = &function_def.type_params;
type_params,
..
},
) => {
// Visit the decorators and arguments, but avoid the body, which will be // Visit the decorators and arguments, but avoid the body, which will be
// deferred. // deferred.
for decorator in decorator_list { for decorator in decorator_list {
@ -1313,16 +1323,12 @@ impl<'a> Visitor<'a> for Checker<'a> {
BindingFlags::empty(), BindingFlags::empty(),
); );
} }
Stmt::ClassDef( Stmt::ClassDef(class_def) => {
class_def @ ast::StmtClassDef { let name = &class_def.name;
name, let body = &class_def.body;
body, let arguments = &class_def.arguments;
arguments, let decorator_list = &class_def.decorator_list;
decorator_list, let type_params = &class_def.type_params;
type_params,
..
},
) => {
for decorator in decorator_list { for decorator in decorator_list {
self.visit_decorator(decorator); self.visit_decorator(decorator);
} }
@ -1369,30 +1375,20 @@ impl<'a> Visitor<'a> for Checker<'a> {
BindingFlags::empty(), BindingFlags::empty(),
); );
} }
Stmt::TypeAlias(ast::StmtTypeAlias { Stmt::TypeAlias(node) => {
range: _,
node_index: _,
name,
type_params,
value,
}) => {
self.semantic.push_scope(ScopeKind::Type); 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_type_params(type_params);
} }
self.visit_deferred_type_alias_value(value); self.visit_deferred_type_alias_value(&node.value);
self.semantic.pop_scope(); self.semantic.pop_scope();
self.visit_expr(name); self.visit_expr(&node.name);
} }
Stmt::Try( Stmt::Try(try_node) => {
try_node @ ast::StmtTry { let body = &try_node.body;
body, let handlers = &try_node.handlers;
handlers, let orelse = &try_node.orelse;
orelse, let finalbody = &try_node.finalbody;
finalbody,
..
},
) => {
// Iterate over the `body`, then the `handlers`, then the `orelse`, then the // 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 // `finalbody`, but treat the body and the `orelse` as a single branch for
// flow analysis purposes. // flow analysis purposes.
@ -1418,64 +1414,60 @@ impl<'a> Visitor<'a> for Checker<'a> {
self.visit_body(finalbody); self.visit_body(finalbody);
self.semantic.pop_branch(); self.semantic.pop_branch();
} }
Stmt::AnnAssign(ast::StmtAnnAssign { Stmt::AnnAssign(node) => {
target,
annotation,
value,
..
}) => {
match AnnotationContext::from_model( match AnnotationContext::from_model(
&self.semantic, &self.semantic,
self.settings(), self.settings(),
self.target_version(), self.target_version(),
) { ) {
AnnotationContext::RuntimeRequired => { AnnotationContext::RuntimeRequired => {
self.visit_runtime_required_annotation(annotation); self.visit_runtime_required_annotation(&node.annotation);
} }
AnnotationContext::RuntimeEvaluated AnnotationContext::RuntimeEvaluated
if flake8_type_checking::helpers::is_dataclass_meta_annotation( if flake8_type_checking::helpers::is_dataclass_meta_annotation(
annotation, &node.annotation,
self.semantic(), self.semantic(),
) => ) =>
{ {
self.visit_runtime_required_annotation(annotation); self.visit_runtime_required_annotation(&node.annotation);
} }
AnnotationContext::RuntimeEvaluated => { AnnotationContext::RuntimeEvaluated => {
self.visit_runtime_evaluated_annotation(annotation); self.visit_runtime_evaluated_annotation(&node.annotation);
} }
AnnotationContext::TypingOnly AnnotationContext::TypingOnly
if flake8_type_checking::helpers::is_dataclass_meta_annotation( if flake8_type_checking::helpers::is_dataclass_meta_annotation(
annotation, &node.annotation,
self.semantic(), self.semantic(),
) => ) =>
{ {
if let Expr::Subscript(subscript) = &**annotation { if let Expr::Subscript(subscript) = &*node.annotation {
// Ex) `InitVar[str]` // Ex) `InitVar[str]`
self.visit_runtime_required_annotation(&subscript.value); self.visit_runtime_required_annotation(&subscript.value);
self.visit_annotation(&subscript.slice); self.visit_annotation(&subscript.slice);
} else { } else {
// Ex) `InitVar` // 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 let Some(expr) = &node.value {
if self.semantic.match_typing_expr(annotation, "TypeAlias") { if self.semantic.match_typing_expr(&node.annotation, "TypeAlias") {
self.visit_annotated_type_alias_value(expr); self.visit_annotated_type_alias_value(expr);
} else { } else {
self.visit_expr(expr); self.visit_expr(expr);
} }
} }
self.visit_expr(target); self.visit_expr(&node.target);
} }
Stmt::Assert(ast::StmtAssert { Stmt::Assert(node) => {
let ast::StmtAssert {
test, test,
msg, msg,
range: _, range: _,
node_index: _, node_index: _,
}) => { } = &**node;
let snapshot = self.semantic.flags; let snapshot = self.semantic.flags;
self.semantic.flags |= SemanticModelFlags::ASSERT_STATEMENT; self.semantic.flags |= SemanticModelFlags::ASSERT_STATEMENT;
self.visit_boolean_test(test); self.visit_boolean_test(test);
@ -1484,13 +1476,14 @@ impl<'a> Visitor<'a> for Checker<'a> {
} }
self.semantic.flags = snapshot; self.semantic.flags = snapshot;
} }
Stmt::With(ast::StmtWith { Stmt::With(node) => {
let ast::StmtWith {
items, items,
body, body,
is_async: _, is_async: _,
range: _, range: _,
node_index: _, node_index: _,
}) => { } = &**node;
for item in items { for item in items {
self.visit_with_item(item); self.visit_with_item(item);
} }
@ -1498,26 +1491,22 @@ impl<'a> Visitor<'a> for Checker<'a> {
self.visit_body(body); self.visit_body(body);
self.semantic.pop_branch(); self.semantic.pop_branch();
} }
Stmt::While(ast::StmtWhile { Stmt::While(node) => {
let ast::StmtWhile {
test, test,
body, body,
orelse, orelse,
range: _, range: _,
node_index: _, node_index: _,
}) => { } = &**node;
self.visit_boolean_test(test); self.visit_boolean_test(test);
self.visit_body(body); self.visit_body(body);
self.visit_body(orelse); self.visit_body(orelse);
} }
Stmt::If( Stmt::If(stmt_if) => {
stmt_if @ ast::StmtIf { let test = &stmt_if.test;
test, let body = &stmt_if.body;
body, let elif_else_clauses = &stmt_if.elif_else_clauses;
elif_else_clauses,
range: _,
node_index: _,
},
) => {
self.visit_boolean_test(test); self.visit_boolean_test(test);
self.semantic.push_branch(); 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() { if self.semantic().at_top_level() || self.semantic().current_scope().kind.is_class() {
match stmt { match stmt {
Stmt::Assign(ast::StmtAssign { targets, .. }) => { Stmt::Assign(node) => {
if let [Expr::Name(_)] = targets.as_slice() { if let [Expr::Name(_)] = node.targets.as_slice() {
self.docstring_state = self.docstring_state =
DocstringState::Expected(ExpectedDocstringKind::Attribute); DocstringState::Expected(ExpectedDocstringKind::Attribute);
} }
} }
Stmt::AnnAssign(ast::StmtAnnAssign { target, .. }) => { Stmt::AnnAssign(node) => {
if target.is_name_expr() { if node.target.is_name_expr() {
self.docstring_state = self.docstring_state =
DocstringState::Expected(ExpectedDocstringKind::Attribute); DocstringState::Expected(ExpectedDocstringKind::Attribute);
} }
@ -2690,13 +2679,13 @@ impl<'a> Checker<'a> {
match parent { match parent {
Stmt::TypeAlias(_) => flags.insert(BindingFlags::DEFERRED_TYPE_ALIAS), 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 // TODO: It is a bit unfortunate that we do this check twice
// maybe we should change how we visit this statement // maybe we should change how we visit this statement
// so the semantic flag for the type alias sticks around // so the semantic flag for the type alias sticks around
// until after we've handled this store, so we can check // until after we've handled this store, so we can check
// the flag instead of duplicating this 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); flags.insert(BindingFlags::ANNOTATED_TYPE_ALIAS);
} }
} }
@ -2707,22 +2696,22 @@ impl<'a> Checker<'a> {
if scope.kind.is_module() if scope.kind.is_module()
&& match parent { && match parent {
Stmt::Assign(ast::StmtAssign { targets, .. }) => { Stmt::Assign(node) => {
if let Some(Expr::Name(ast::ExprName { id, .. })) = targets.first() { if let Some(Expr::Name(ast::ExprName { id, .. })) = node.targets.first() {
id == "__all__" id == "__all__"
} else { } else {
false false
} }
} }
Stmt::AugAssign(ast::StmtAugAssign { target, .. }) => { Stmt::AugAssign(node) => {
if let Expr::Name(ast::ExprName { id, .. }) = target.as_ref() { if let Expr::Name(ast::ExprName { id, .. }) = node.target.as_ref() {
id == "__all__" id == "__all__"
} else { } else {
false false
} }
} }
Stmt::AnnAssign(ast::StmtAnnAssign { target, .. }) => { Stmt::AnnAssign(node) => {
if let Expr::Name(ast::ExprName { id, .. }) = target.as_ref() { if let Expr::Name(ast::ExprName { id, .. }) = node.target.as_ref() {
id == "__all__" id == "__all__"
} else { } else {
false false
@ -2765,10 +2754,8 @@ impl<'a> Checker<'a> {
// Match the left-hand side of an annotated assignment without a value, // 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 // 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`. // as assignments on par with statements such as `x: int = 5`.
if matches!( if matches!(parent, Stmt::AnnAssign(node) if node.value.is_none())
parent, && !self.semantic.in_annotation()
Stmt::AnnAssign(ast::StmtAnnAssign { value: None, .. })
) && !self.semantic.in_annotation()
{ {
self.add_binding(id, expr.range(), BindingKind::Annotation, flags); self.add_binding(id, expr.range(), BindingKind::Annotation, flags);
return; return;
@ -3040,19 +3027,16 @@ impl<'a> Checker<'a> {
let stmt = self.semantic.current_statement(); let stmt = self.semantic.current_statement();
let Stmt::FunctionDef(ast::StmtFunctionDef { let Stmt::FunctionDef(node) = stmt else {
body, parameters, ..
}) = stmt
else {
unreachable!("Expected Stmt::FunctionDef") unreachable!("Expected Stmt::FunctionDef")
}; };
self.with_semantic_checker(|semantic, context| semantic.visit_stmt(stmt, context)); 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. // Set the docstring state before visiting the function body.
self.docstring_state = DocstringState::Expected(ExpectedDocstringKind::Function); self.docstring_state = DocstringState::Expected(ExpectedDocstringKind::Function);
self.visit_body(body); self.visit_body(&node.body);
} }
} }
self.semantic.restore(snapshot); self.semantic.restore(snapshot);

View File

@ -127,8 +127,8 @@ pub(crate) fn make_redundant_alias<'a>(
stmt: &Stmt, stmt: &Stmt,
) -> Vec<Edit> { ) -> Vec<Edit> {
let aliases = match stmt { let aliases = match stmt {
Stmt::Import(ast::StmtImport { names, .. }) => names, Stmt::Import(node) => &node.names,
Stmt::ImportFrom(ast::StmtImportFrom { names, .. }) => names, Stmt::ImportFrom(node) => &node.names,
_ => { _ => {
return Vec::new(); return Vec::new();
} }
@ -404,43 +404,46 @@ fn is_only<T: PartialEq>(vec: &[T], value: &T) -> bool {
/// Determine if a child is the only statement in its body. /// Determine if a child is the only statement in its body.
fn is_lone_child(child: &Stmt, parent: &Stmt) -> bool { fn is_lone_child(child: &Stmt, parent: &Stmt) -> bool {
match parent { match parent {
Stmt::FunctionDef(ast::StmtFunctionDef { body, .. }) Stmt::FunctionDef(node) => {
| Stmt::ClassDef(ast::StmtClassDef { body, .. }) if is_only(&node.body, child) {
| Stmt::With(ast::StmtWith { body, .. }) => {
if is_only(body, child) {
return true; return true;
} }
} }
Stmt::For(ast::StmtFor { body, orelse, .. }) Stmt::ClassDef(node) => {
| Stmt::While(ast::StmtWhile { body, orelse, .. }) => { if is_only(&node.body, child) {
if is_only(body, child) || is_only(orelse, child) {
return true; return true;
} }
} }
Stmt::If(ast::StmtIf { Stmt::With(node) => {
body, if is_only(&node.body, child) {
elif_else_clauses, return true;
.. }
}) => { }
if is_only(body, child) Stmt::For(node) => {
|| elif_else_clauses 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() .iter()
.any(|ast::ElifElseClause { body, .. }| is_only(body, child)) .any(|ast::ElifElseClause { body, .. }| is_only(body, child))
{ {
return true; return true;
} }
} }
Stmt::Try(ast::StmtTry { Stmt::Try(node) => {
body, if is_only(&node.body, child)
handlers, || is_only(&node.orelse, child)
orelse, || is_only(&node.finalbody, child)
finalbody, || node.handlers.iter().any(|handler| match handler {
..
}) => {
if is_only(body, child)
|| is_only(orelse, child)
|| is_only(finalbody, child)
|| handlers.iter().any(|handler| match handler {
ExceptHandler::ExceptHandler(ast::ExceptHandlerExceptHandler { ExceptHandler::ExceptHandler(ast::ExceptHandlerExceptHandler {
body, .. body, ..
}) => is_only(body, child), }) => is_only(body, child),
@ -449,8 +452,8 @@ fn is_lone_child(child: &Stmt, parent: &Stmt) -> bool {
return true; return true;
} }
} }
Stmt::Match(ast::StmtMatch { cases, .. }) => { Stmt::Match(node) => {
if cases.iter().any(|case| is_only(&case.body, child)) { if node.cases.iter().any(|case| is_only(&case.body, child)) {
return true; return true;
} }
} }

View File

@ -236,9 +236,10 @@ impl<'a> Importer<'a> {
semantic: &SemanticModel<'a>, semantic: &SemanticModel<'a>,
type_checking_block: &Stmt, type_checking_block: &Stmt,
) -> Option<&'a Stmt> { ) -> Option<&'a Stmt> {
let Stmt::If(ast::StmtIf { test, .. }) = type_checking_block else { let Stmt::If(node) = type_checking_block else {
return None; return None;
}; };
let test = &node.test;
let mut source = test; let mut source = test;
while let Expr::Attribute(ast::ExprAttribute { value, .. }) = source.as_ref() { while let Expr::Attribute(ast::ExprAttribute { value, .. }) = source.as_ref() {
@ -453,17 +454,10 @@ impl<'a> Importer<'a> {
if stmt.start() >= at { if stmt.start() >= at {
break; break;
} }
if let Stmt::ImportFrom(ast::StmtImportFrom { if let Stmt::ImportFrom(node) = stmt {
module: name, if node.level == 0
names, && node.module.as_ref().is_some_and(|name| name == module)
level, && node.names.iter().all(|alias| alias.name.as_str() != "*")
range: _,
node_index: _,
}) = stmt
{
if *level == 0
&& name.as_ref().is_some_and(|name| name == module)
&& names.iter().all(|alias| alias.name.as_str() != "*")
{ {
import_from = Some(*stmt); import_from = Some(*stmt);
} }

View File

@ -281,12 +281,10 @@ impl Renamer {
) -> Option<Edit> { ) -> Option<Edit> {
let statement = binding.statement(semantic)?; let statement = binding.statement(semantic)?;
let (ast::Stmt::Assign(ast::StmtAssign { value, .. }) let value = match statement {
| ast::Stmt::AnnAssign(ast::StmtAnnAssign { ast::Stmt::Assign(node) => &node.value,
value: Some(value), .. ast::Stmt::AnnAssign(node) => node.value.as_ref()?,
})) = statement _ => return None,
else {
return None;
}; };
let ast::ExprCall { let ast::ExprCall {

View File

@ -448,11 +448,10 @@ fn is_kwarg_parameter(semantic: &SemanticModel, name: &ExprName) -> bool {
return false; return false;
}; };
let binding = semantic.binding(binding_id); let binding = semantic.binding(binding_id);
let Some(Stmt::FunctionDef(StmtFunctionDef { parameters, .. })) = binding.statement(semantic) let Some(Stmt::FunctionDef(node)) = binding.statement(semantic) else {
else {
return false; return false;
}; };
parameters node.parameters
.kwarg .kwarg
.as_deref() .as_deref()
.is_some_and(|kwarg| kwarg.name.as_str() == name.id.as_str()) .is_some_and(|kwarg| kwarg.name.as_str() == name.id.as_str())

View File

@ -2,7 +2,7 @@
//! //!
//! See: <https://bandit.readthedocs.io/en/latest/blacklists/blacklist_imports.html> //! See: <https://bandit.readthedocs.io/en/latest/blacklists/blacklist_imports.html>
use ruff_macros::{ViolationMetadata, derive_message_formats}; 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 ruff_text_size::Ranged;
use crate::Violation; use crate::Violation;
@ -371,7 +371,8 @@ pub(crate) fn suspicious_imports(checker: &Checker, stmt: &Stmt) {
} }
match stmt { match stmt {
Stmt::Import(ast::StmtImport { names, .. }) => { Stmt::Import(node) => {
let names = &node.names;
for name in names { for name in names {
match name.name.as_str() { match name.name.as_str() {
"telnetlib" => { "telnetlib" => {
@ -421,8 +422,9 @@ pub(crate) fn suspicious_imports(checker: &Checker, stmt: &Stmt) {
} }
} }
} }
Stmt::ImportFrom(ast::StmtImportFrom { module, names, .. }) => { Stmt::ImportFrom(node) => {
let Some(identifier) = module else { return }; let Some(identifier) = &node.module else { return };
let names = &node.names;
match identifier.as_str() { match identifier.as_str() {
"telnetlib" => { "telnetlib" => {
checker.report_diagnostic_if_enabled( checker.report_diagnostic_if_enabled(

View File

@ -154,10 +154,12 @@ impl<'a> StatementVisitor<'a> for ReraiseVisitor<'a> {
return; return;
} }
match stmt { 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 <name>]: // except Exception [as <name>]:
// raise [<exc> [from <cause>]] // raise [<exc> [from <cause>]]
let reraised = match (self.name, exc.as_deref(), cause.as_deref()) { let reraised = match (self.name, exc, cause) {
// `raise` // `raise`
(_, None, None) => true, (_, None, None) => true,
// `raise SomeExc from <name>` // `raise SomeExc from <name>`

View File

@ -173,24 +173,21 @@ pub(crate) fn abstract_base_class(
// If an ABC declares an attribute by providing a type annotation // If an ABC declares an attribute by providing a type annotation
// but does not actually assign a value for that attribute, // but does not actually assign a value for that attribute,
// assume it is intended to be an "abstract attribute" // assume it is intended to be an "abstract attribute"
if matches!( if let Stmt::AnnAssign(node) = stmt {
stmt, if node.value.is_none() {
Stmt::AnnAssign(ast::StmtAnnAssign { value: None, .. })
) {
has_abstract_method = true; has_abstract_method = true;
continue; continue;
} }
}
let Stmt::FunctionDef(ast::StmtFunctionDef { let Stmt::FunctionDef(node) = stmt else {
decorator_list,
body,
name: method_name,
..
}) = stmt
else {
continue; 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()); let has_abstract_decorator = is_abstract(decorator_list, checker.semantic());
has_abstract_method |= has_abstract_decorator; has_abstract_method |= has_abstract_decorator;

View File

@ -51,7 +51,7 @@ impl AlwaysFixableViolation for AssertFalse {
} }
fn assertion_error(msg: Option<&Expr>) -> Stmt { fn assertion_error(msg: Option<&Expr>) -> Stmt {
Stmt::Raise(ast::StmtRaise { Stmt::Raise(Box::new(ast::StmtRaise {
range: TextRange::default(), range: TextRange::default(),
node_index: ruff_python_ast::AtomicNodeIndex::NONE, node_index: ruff_python_ast::AtomicNodeIndex::NONE,
exc: Some(Box::new(Expr::Call(ast::ExprCall { 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, node_index: ruff_python_ast::AtomicNodeIndex::NONE,
}))), }))),
cause: None, cause: None,
}) }))
} }
/// B011 /// B011

View File

@ -114,14 +114,14 @@ pub(crate) fn class_as_data_structure(checker: &Checker, class_def: &ast::StmtCl
// assignment of a name to an attribute. // assignment of a name to an attribute.
fn is_simple_assignment_to_attribute(stmt: &ast::Stmt) -> bool { fn is_simple_assignment_to_attribute(stmt: &ast::Stmt) -> bool {
match stmt { match stmt {
ast::Stmt::Assign(ast::StmtAssign { targets, value, .. }) => { ast::Stmt::Assign(node) => {
let [target] = targets.as_slice() else { let [target] = node.targets.as_slice() else {
return false; 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, .. }) => { ast::Stmt::AnnAssign(node) => {
target.is_attribute_expr() && value.as_ref().is_some_and(|val| val.is_name_expr()) node.target.is_attribute_expr() && node.value.as_ref().is_some_and(|val| val.is_name_expr())
} }
_ => false, _ => false,
} }

View File

@ -86,12 +86,10 @@ struct SuspiciousVariablesVisitor<'a> {
impl<'a> Visitor<'a> for SuspiciousVariablesVisitor<'a> { impl<'a> Visitor<'a> for SuspiciousVariablesVisitor<'a> {
fn visit_stmt(&mut self, stmt: &'a Stmt) { fn visit_stmt(&mut self, stmt: &'a Stmt) {
match stmt { match stmt {
Stmt::FunctionDef(ast::StmtFunctionDef { Stmt::FunctionDef(node) => {
parameters, body, ..
}) => {
// Collect all loaded variable names. // Collect all loaded variable names.
let mut visitor = LoadedNamesVisitor::default(); let mut visitor = LoadedNamesVisitor::default();
visitor.visit_body(body); visitor.visit_body(&node.body);
// Treat any non-arguments as "suspicious". // Treat any non-arguments as "suspicious".
self.names self.names
@ -100,7 +98,7 @@ impl<'a> Visitor<'a> for SuspiciousVariablesVisitor<'a> {
return false; return false;
} }
if parameters.includes(&loaded.id) { if node.parameters.includes(&loaded.id) {
return false; return false;
} }
@ -242,18 +240,26 @@ impl<'a> Visitor<'a> for AssignedNamesVisitor<'a> {
} }
match stmt { match stmt {
Stmt::Assign(ast::StmtAssign { targets, .. }) => { Stmt::Assign(node) => {
let mut visitor = NamesFromAssignmentsVisitor::default(); let mut visitor = NamesFromAssignmentsVisitor::default();
for expr in targets { for expr in &node.targets {
visitor.visit_expr(expr); visitor.visit_expr(expr);
} }
self.names.extend(visitor.names); self.names.extend(visitor.names);
} }
Stmt::AugAssign(ast::StmtAugAssign { target, .. }) Stmt::AugAssign(node) => {
| Stmt::AnnAssign(ast::StmtAnnAssign { target, .. })
| Stmt::For(ast::StmtFor { target, .. }) => {
let mut visitor = NamesFromAssignmentsVisitor::default(); 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); self.names.extend(visitor.names);
} }
_ => {} _ => {}

View File

@ -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_macros::{ViolationMetadata, derive_message_formats};
use ruff_text_size::Ranged; use ruff_text_size::Ranged;
@ -71,15 +71,23 @@ fn walk_stmt(checker: &Checker, body: &[Stmt], f: fn(&Stmt) -> bool) {
); );
} }
match stmt { match stmt {
Stmt::While(ast::StmtWhile { body, .. }) | Stmt::For(ast::StmtFor { body, .. }) => { Stmt::While(node) => {
walk_stmt(checker, body, Stmt::is_return_stmt); walk_stmt(checker, &node.body, Stmt::is_return_stmt);
} }
Stmt::If(ast::StmtIf { body, .. }) Stmt::For(node) => {
| Stmt::Try(ast::StmtTry { body, .. }) walk_stmt(checker, &node.body, Stmt::is_return_stmt);
| Stmt::With(ast::StmtWith { body, .. }) => {
walk_stmt(checker, body, f);
} }
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 { for case in cases {
walk_stmt(checker, &case.body, f); walk_stmt(checker, &case.body, f);
} }

View File

@ -5,8 +5,7 @@ use ruff_macros::{ViolationMetadata, derive_message_formats};
use ruff_python_ast::comparable::ComparableExpr; use ruff_python_ast::comparable::ComparableExpr;
use ruff_python_ast::name::UnqualifiedName; use ruff_python_ast::name::UnqualifiedName;
use ruff_python_ast::{ use ruff_python_ast::{
Expr, ExprAttribute, ExprCall, ExprSubscript, ExprTuple, Stmt, StmtAssign, StmtAugAssign, self as ast, Expr, ExprAttribute, ExprCall, ExprSubscript, ExprTuple, Stmt, StmtFor,
StmtDelete, StmtFor, StmtIf,
visitor::{self, Visitor}, visitor::{self, Visitor},
}; };
use ruff_text_size::TextRange; use ruff_text_size::TextRange;
@ -242,43 +241,39 @@ impl<'a> Visitor<'a> for LoopMutationsVisitor<'a> {
fn visit_stmt(&mut self, stmt: &'a Stmt) { fn visit_stmt(&mut self, stmt: &'a Stmt) {
match stmt { match stmt {
// Ex) `del items[0]` // Ex) `del items[0]`
Stmt::Delete(StmtDelete { Stmt::Delete(node) => {
let ast::StmtDelete {
range, range,
targets, targets,
node_index: _, node_index: _,
}) => { } = &**node;
self.handle_delete(*range, targets); self.handle_delete(*range, targets);
visitor::walk_stmt(self, stmt); visitor::walk_stmt(self, stmt);
} }
// Ex) `items[0] = 1` // Ex) `items[0] = 1`
Stmt::Assign(StmtAssign { range, targets, .. }) => { Stmt::Assign(node) => {
self.handle_assign(*range, targets); self.handle_assign(node.range, &node.targets);
visitor::walk_stmt(self, stmt); visitor::walk_stmt(self, stmt);
} }
// Ex) `items += [1]` // Ex) `items += [1]`
Stmt::AugAssign(StmtAugAssign { range, target, .. }) => { Stmt::AugAssign(node) => {
self.handle_aug_assign(*range, target); self.handle_aug_assign(node.range, &node.target);
visitor::walk_stmt(self, stmt); visitor::walk_stmt(self, stmt);
} }
// Ex) `if True: items.append(1)` // Ex) `if True: items.append(1)`
Stmt::If(StmtIf { Stmt::If(node) => {
test,
body,
elif_else_clauses,
..
}) => {
// Handle the `if` branch. // Handle the `if` branch.
self.branch += 1; self.branch += 1;
self.branches.push(self.branch); self.branches.push(self.branch);
self.visit_expr(test); self.visit_expr(&node.test);
self.visit_body(body); self.visit_body(&node.body);
self.branches.pop(); self.branches.pop();
// Handle the `elif` and `else` branches. // Handle the `elif` and `else` branches.
for clause in elif_else_clauses { for clause in &node.elif_else_clauses {
self.branch += 1; self.branch += 1;
self.branches.push(self.branch); self.branches.push(self.branch);
if let Some(test) = &clause.test { if let Some(test) = &clause.test {

View File

@ -119,13 +119,11 @@ impl<'a> Visitor<'a> for GroupNameFinder<'a> {
return; return;
} }
match stmt { match stmt {
Stmt::For(ast::StmtFor { Stmt::For(node) => {
target, iter, body, .. if self.name_matches(&node.target) {
}) => {
if self.name_matches(target) {
self.overridden = true; self.overridden = true;
} else { } else {
if self.name_matches(iter) { if self.name_matches(&node.iter) {
self.increment_usage_count(1); self.increment_usage_count(1);
// This could happen when the group is being looped // This could happen when the group is being looped
// over multiple times: // over multiple times:
@ -136,36 +134,30 @@ impl<'a> Visitor<'a> for GroupNameFinder<'a> {
// for item in group: // for item in group:
// ... // ...
if self.usage_count > 1 { if self.usage_count > 1 {
self.exprs.push(iter); self.exprs.push(&node.iter);
} }
} }
self.nested = true; self.nested = true;
visitor::walk_body(self, body); visitor::walk_body(self, &node.body);
self.nested = false; self.nested = false;
} }
} }
Stmt::While(ast::StmtWhile { body, .. }) => { Stmt::While(node) => {
self.nested = true; self.nested = true;
visitor::walk_body(self, body); visitor::walk_body(self, &node.body);
self.nested = false; self.nested = false;
} }
Stmt::If(ast::StmtIf { Stmt::If(node) => {
test,
body,
elif_else_clauses,
range: _,
node_index: _,
}) => {
// base if plus branches // 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. // Initialize the vector with the count for the if branch.
if_stack.push(0); if_stack.push(0);
self.counter_stack.push(if_stack); self.counter_stack.push(if_stack);
self.visit_expr(test); self.visit_expr(&node.test);
self.visit_body(body); 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.counter_stack.last_mut().unwrap().push(0);
self.visit_elif_else_clause(clause); self.visit_elif_else_clause(clause);
} }
@ -177,15 +169,10 @@ impl<'a> Visitor<'a> for GroupNameFinder<'a> {
self.increment_usage_count(max_count); self.increment_usage_count(max_count);
} }
} }
Stmt::Match(ast::StmtMatch { Stmt::Match(node) => {
subject, self.counter_stack.push(Vec::with_capacity(node.cases.len()));
cases, self.visit_expr(&node.subject);
range: _, for match_case in &node.cases {
node_index: _,
}) => {
self.counter_stack.push(Vec::with_capacity(cases.len()));
self.visit_expr(subject);
for match_case in cases {
self.counter_stack.last_mut().unwrap().push(0); self.counter_stack.last_mut().unwrap().push(0);
self.visit_match_case(match_case); self.visit_match_case(match_case);
} }
@ -196,17 +183,17 @@ impl<'a> Visitor<'a> for GroupNameFinder<'a> {
self.increment_usage_count(max_count); self.increment_usage_count(max_count);
} }
} }
Stmt::Assign(ast::StmtAssign { targets, value, .. }) => { Stmt::Assign(node) => {
if targets.iter().any(|target| self.name_matches(target)) { if node.targets.iter().any(|target| self.name_matches(target)) {
self.overridden = true; self.overridden = true;
} else { } else {
self.visit_expr(value); self.visit_expr(&node.value);
} }
} }
Stmt::AnnAssign(ast::StmtAnnAssign { target, value, .. }) => { Stmt::AnnAssign(node) => {
if self.name_matches(target) { if self.name_matches(&node.target) {
self.overridden = true; self.overridden = true;
} else if let Some(expr) = value { } else if let Some(expr) = &node.value {
self.visit_expr(expr); self.visit_expr(expr);
} }
} }

View File

@ -66,7 +66,7 @@ impl AlwaysFixableViolation for SetAttrWithConstant {
} }
fn assignment(obj: &Expr, name: &str, value: &Expr, generator: Generator) -> String { 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 { targets: vec![Expr::Attribute(ast::ExprAttribute {
value: Box::new(obj.clone()), value: Box::new(obj.clone()),
attr: Identifier::new(name.to_string(), TextRange::default()), 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()), value: Box::new(value.clone()),
range: TextRange::default(), range: TextRange::default(),
node_index: ruff_python_ast::AtomicNodeIndex::NONE, node_index: ruff_python_ast::AtomicNodeIndex::NONE,
}); }));
generator.stmt(&stmt) generator.stmt(&stmt)
} }

View File

@ -59,16 +59,20 @@ pub(crate) fn all_with_model_form(checker: &Checker, class_def: &ast::StmtClassD
} }
for element in &class_def.body { for element in &class_def.body {
let Stmt::ClassDef(ast::StmtClassDef { name, body, .. }) = element else { let Stmt::ClassDef(class_def_inner) = element else {
continue; continue;
}; };
let name = &class_def_inner.name;
let body = &class_def_inner.body;
if name != "Meta" { if name != "Meta" {
continue; continue;
} }
for element in body { for element in body {
let Stmt::Assign(ast::StmtAssign { targets, value, .. }) = element else { let Stmt::Assign(assign) = element else {
continue; continue;
}; };
let targets = &assign.targets;
let value = &assign.value;
for target in targets { for target in targets {
let Expr::Name(ast::ExprName { id, .. }) = target else { let Expr::Name(ast::ExprName { id, .. }) = target else {
continue; continue;

View File

@ -57,16 +57,19 @@ pub(crate) fn exclude_with_model_form(checker: &Checker, class_def: &ast::StmtCl
} }
for element in &class_def.body { for element in &class_def.body {
let Stmt::ClassDef(ast::StmtClassDef { name, body, .. }) = element else { let Stmt::ClassDef(class_def_inner) = element else {
continue; continue;
}; };
let name = &class_def_inner.name;
let body = &class_def_inner.body;
if name != "Meta" { if name != "Meta" {
continue; continue;
} }
for element in body { for element in body {
let Stmt::Assign(ast::StmtAssign { targets, .. }) = element else { let Stmt::Assign(assign) = element else {
continue; continue;
}; };
let targets = &assign.targets;
for target in targets { for target in targets {
let Expr::Name(ast::ExprName { id, .. }) = target else { let Expr::Name(ast::ExprName { id, .. }) = target else {
continue; continue;

View File

@ -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 { fn has_dunder_method(class_def: &ast::StmtClassDef, semantic: &SemanticModel) -> bool {
analyze::class::any_super_class(class_def, semantic, &|class_def| { analyze::class::any_super_class(class_def, semantic, &|class_def| {
class_def.body.iter().any(|val| match val { class_def.body.iter().any(|val| match val {
Stmt::FunctionDef(ast::StmtFunctionDef { name, .. }) => name == "__str__", Stmt::FunctionDef(node) => node.name.as_str() == "__str__",
_ => false, _ => 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. /// Check if class is abstract, in terms of Django model inheritance.
fn is_model_abstract(class_def: &ast::StmtClassDef) -> bool { fn is_model_abstract(class_def: &ast::StmtClassDef) -> bool {
for element in &class_def.body { for element in &class_def.body {
let Stmt::ClassDef(ast::StmtClassDef { name, body, .. }) = element else { let Stmt::ClassDef(node) = element else {
continue; continue;
}; };
if name != "Meta" { if node.name.as_str() != "Meta" {
continue; continue;
} }
for element in body { for element in &node.body {
match element { match element {
Stmt::Assign(ast::StmtAssign { targets, value, .. }) => { Stmt::Assign(assign) => {
if targets if assign
.targets
.iter() .iter()
.any(|target| is_abstract_true_assignment(target, Some(value))) .any(|target| is_abstract_true_assignment(target, Some(&assign.value)))
{ {
return true; return true;
} }
} }
Stmt::AnnAssign(ast::StmtAnnAssign { target, value, .. }) => { Stmt::AnnAssign(ann_assign) => {
if is_abstract_true_assignment(target, value.as_deref()) { if is_abstract_true_assignment(&ann_assign.target, ann_assign.value.as_deref()) {
return true; return true;
} }
} }

View File

@ -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_macros::{ViolationMetadata, derive_message_formats};
use ruff_python_ast::helpers::is_const_true; 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 { for statement in body {
let value = match statement { let value = match statement {
Stmt::Assign(ast::StmtAssign { value, .. }) => value, Stmt::Assign(assign) => &assign.value,
Stmt::AnnAssign(ast::StmtAnnAssign { Stmt::AnnAssign(ann_assign) => {
value: Some(value), .. match &ann_assign.value {
}) => value, Some(value) => value,
None => continue,
}
}
_ => continue, _ => continue,
}; };

View File

@ -153,13 +153,13 @@ impl fmt::Display for ContentType {
fn get_element_type(element: &Stmt, semantic: &SemanticModel) -> Option<ContentType> { fn get_element_type(element: &Stmt, semantic: &SemanticModel) -> Option<ContentType> {
match element { match element {
Stmt::Assign(ast::StmtAssign { targets, value, .. }) => { Stmt::Assign(node) => {
if let Expr::Call(ast::ExprCall { func, .. }) = value.as_ref() { if let Expr::Call(ast::ExprCall { func, .. }) = node.value.as_ref() {
if helpers::is_model_field(func, semantic) { if helpers::is_model_field(func, semantic) {
return Some(ContentType::FieldDeclaration); return Some(ContentType::FieldDeclaration);
} }
} }
let expr = targets.first()?; let expr = node.targets.first()?;
let Expr::Name(ast::ExprName { id, .. }) = expr else { let Expr::Name(ast::ExprName { id, .. }) = expr else {
return None; return None;
}; };
@ -169,14 +169,14 @@ fn get_element_type(element: &Stmt, semantic: &SemanticModel) -> Option<ContentT
None None
} }
} }
Stmt::ClassDef(ast::StmtClassDef { name, .. }) => { Stmt::ClassDef(node) => {
if name == "Meta" { if node.name.as_str() == "Meta" {
Some(ContentType::MetaClass) Some(ContentType::MetaClass)
} else { } else {
None 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), name if is_dunder(name) => Some(ContentType::MagicMethod),
"save" => Some(ContentType::SaveMethod), "save" => Some(ContentType::SaveMethod),
"get_absolute_url" => Some(ContentType::GetAbsoluteUrlMethod), "get_absolute_url" => Some(ContentType::GetAbsoluteUrlMethod),

View File

@ -1,4 +1,4 @@
use ruff_python_ast::{Stmt, StmtTry}; use ruff_python_ast::Stmt;
use ruff_python_semantic::SemanticModel; use ruff_python_semantic::SemanticModel;
use ruff_text_size::{Ranged, TextSize}; use ruff_text_size::{Ranged, TextSize};
@ -8,9 +8,10 @@ pub(super) fn outside_handlers(offset: TextSize, semantic: &SemanticModel) -> bo
break; break;
} }
let Stmt::Try(StmtTry { handlers, .. }) = stmt else { let Stmt::Try(try_stmt) = stmt else {
continue; continue;
}; };
let handlers = &try_stmt.handlers;
if handlers if handlers
.iter() .iter()

View File

@ -2,7 +2,7 @@ use rustc_hash::FxHashSet;
use ruff_macros::{ViolationMetadata, derive_message_formats}; use ruff_macros::{ViolationMetadata, derive_message_formats};
use ruff_python_ast::helpers::any_over_expr; 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 ruff_text_size::Ranged;
use crate::checkers::ast::Checker; use crate::checkers::ast::Checker;
@ -59,15 +59,15 @@ pub(crate) fn duplicate_class_field_definition(checker: &Checker, body: &[Stmt])
for stmt in body { for stmt in body {
// Extract the property name from the assignment statement. // Extract the property name from the assignment statement.
let target = match stmt { let target = match stmt {
Stmt::Assign(ast::StmtAssign { targets, .. }) => { Stmt::Assign(assign_stmt) => {
if let [Expr::Name(id)] = targets.as_slice() { if let [Expr::Name(id)] = assign_stmt.targets.as_slice() {
id id
} else { } else {
continue; continue;
} }
} }
Stmt::AnnAssign(ast::StmtAnnAssign { target, .. }) => { Stmt::AnnAssign(ann_assign_stmt) => {
if let Expr::Name(id) = target.as_ref() { if let Expr::Name(id) = ann_assign_stmt.target.as_ref() {
id id
} else { } else {
continue; continue;
@ -78,22 +78,22 @@ 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. // If this is an unrolled augmented assignment (e.g., `x = x + 1`), skip it.
match stmt { match stmt {
Stmt::Assign(ast::StmtAssign { value, .. }) => { Stmt::Assign(assign_stmt) => {
if any_over_expr(value.as_ref(), &|expr| { if any_over_expr(assign_stmt.value.as_ref(), &|expr| {
expr.as_name_expr().is_some_and(|name| name.id == target.id) expr.as_name_expr().is_some_and(|name| name.id == target.id)
}) { }) {
continue; continue;
} }
} }
Stmt::AnnAssign(ast::StmtAnnAssign { Stmt::AnnAssign(ann_assign_stmt) => {
value: Some(value), .. if let Some(value) = &ann_assign_stmt.value {
}) => {
if any_over_expr(value.as_ref(), &|expr| { if any_over_expr(value.as_ref(), &|expr| {
expr.as_name_expr().is_some_and(|name| name.id == target.id) expr.as_name_expr().is_some_and(|name| name.id == target.id)
}) { }) {
continue; continue;
} }
} }
}
_ => continue, _ => continue,
} }

View File

@ -58,11 +58,11 @@ impl Violation for NonUniqueEnums {
pub(crate) fn non_unique_enums(checker: &Checker, parent: &Stmt, body: &[Stmt]) { pub(crate) fn non_unique_enums(checker: &Checker, parent: &Stmt, body: &[Stmt]) {
let semantic = checker.semantic(); let semantic = checker.semantic();
let Stmt::ClassDef(parent) = parent else { let Stmt::ClassDef(class_def) = parent else {
return; return;
}; };
if !parent.bases().iter().any(|expr| { if !class_def.bases().iter().any(|expr| {
semantic semantic
.resolve_qualified_name(expr) .resolve_qualified_name(expr)
.is_some_and(|qualified_name| matches!(qualified_name.segments(), ["enum", "Enum"])) .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<ComparableExpr> = FxHashSet::default(); let mut seen_targets: FxHashSet<ComparableExpr> = FxHashSet::default();
for stmt in body { for stmt in body {
let Stmt::Assign(ast::StmtAssign { value, .. }) = stmt else { let Stmt::Assign(assign_stmt) = stmt else {
continue; continue;
}; };
let value = &assign_stmt.value;
if is_call_to_enum_auto(semantic, value) { if is_call_to_enum_auto(semantic, value) {
continue; continue;

View File

@ -1,4 +1,3 @@
use ruff_python_ast as ast;
use ruff_python_ast::Stmt; use ruff_python_ast::Stmt;
use ruff_macros::{ViolationMetadata, derive_message_formats}; use ruff_macros::{ViolationMetadata, derive_message_formats};
@ -44,17 +43,15 @@ impl AlwaysFixableViolation for StrOrReprDefinedInStub {
/// PYI029 /// PYI029
pub(crate) fn str_or_repr_defined_in_stub(checker: &Checker, stmt: &Stmt) { pub(crate) fn str_or_repr_defined_in_stub(checker: &Checker, stmt: &Stmt) {
let Stmt::FunctionDef(ast::StmtFunctionDef { let Stmt::FunctionDef(func_def) = stmt else {
name,
decorator_list,
returns,
parameters,
..
}) = stmt
else {
return; 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 { let Some(returns) = returns else {
return; return;
}; };

View File

@ -196,15 +196,14 @@ pub(crate) fn unused_private_type_var(checker: &Checker, scope: &Scope) {
let Some(source) = binding.source else { let Some(source) = binding.source else {
continue; continue;
}; };
let stmt @ Stmt::Assign(ast::StmtAssign { targets, value, .. }) = let stmt = checker.semantic().statement(source);
checker.semantic().statement(source) let Stmt::Assign(assign) = stmt else {
else {
continue; continue;
}; };
let [Expr::Name(ast::ExprName { id, .. })] = &targets[..] else { let [Expr::Name(ast::ExprName { id, .. })] = &assign.targets[..] else {
continue; continue;
}; };
let Expr::Call(ast::ExprCall { func, .. }) = value.as_ref() else { let Expr::Call(ast::ExprCall { func, .. }) = assign.value.as_ref() else {
continue; 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> { fn extract_type_alias_name<'a>(stmt: &'a ast::Stmt, semantic: &SemanticModel) -> Option<&'a str> {
match stmt { match stmt {
ast::Stmt::AnnAssign(ast::StmtAnnAssign { ast::Stmt::AnnAssign(ann_assign) => {
target, annotation, .. let ast::ExprName { id, .. } = ann_assign.target.as_name_expr()?;
}) => { if semantic.match_typing_expr(&ann_assign.annotation, "TypeAlias") {
let ast::ExprName { id, .. } = target.as_name_expr()?;
if semantic.match_typing_expr(annotation, "TypeAlias") {
Some(id) Some(id)
} else { } else {
None None
} }
} }
ast::Stmt::TypeAlias(ast::StmtTypeAlias { name, .. }) => { ast::Stmt::TypeAlias(type_alias) => {
let ast::ExprName { id, .. } = name.as_name_expr()?; let ast::ExprName { id, .. } = type_alias.name.as_name_expr()?;
Some(id) Some(id)
} }
_ => None, _ => None,
@ -388,9 +385,9 @@ fn extract_typeddict_name<'a>(stmt: &'a Stmt, semantic: &SemanticModel) -> Optio
// class Bar(typing.TypedDict, typing.Generic[T]): // class Bar(typing.TypedDict, typing.Generic[T]):
// y: T // y: T
// ``` // ```
Stmt::ClassDef(class_def @ ast::StmtClassDef { name, .. }) => { Stmt::ClassDef(class_def) => {
if class_def.bases().iter().any(is_typeddict) { if class_def.bases().iter().any(is_typeddict) {
Some(name) Some(&class_def.name)
} else { } else {
None None
} }
@ -402,12 +399,12 @@ fn extract_typeddict_name<'a>(stmt: &'a Stmt, semantic: &SemanticModel) -> Optio
// import typing // import typing
// Baz = typing.TypedDict("Baz", {"z": bytes}) // Baz = typing.TypedDict("Baz", {"z": bytes})
// ``` // ```
Stmt::Assign(ast::StmtAssign { targets, value, .. }) => { Stmt::Assign(assign) => {
let [target] = targets.as_slice() else { let [target] = assign.targets.as_slice() else {
return None; return None;
}; };
let ast::ExprName { id, .. } = target.as_name_expr()?; 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 } if is_typeddict(func) { Some(id) } else { None }
} }
_ => None, _ => None,

View File

@ -369,10 +369,10 @@ impl Violation for PytestUnittestRaisesAssertion {
/// PT027 /// PT027
pub(crate) fn unittest_raises_assertion_call(checker: &Checker, call: &ast::ExprCall) { pub(crate) fn unittest_raises_assertion_call(checker: &Checker, call: &ast::ExprCall) {
// Bindings in `with` statements are handled by `unittest_raises_assertion_bindings`. // 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); 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() AnyNodeRef::from(&item.context_expr).ptr_eq(call_ref) && item.optional_vars.is_some()
}) { }) {
return; return;
@ -390,7 +390,11 @@ pub(crate) fn unittest_raises_assertion_binding(checker: &Checker, binding: &Bin
let semantic = checker.semantic(); 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; return;
}; };

View File

@ -220,11 +220,11 @@ pub(crate) fn complex_raises(checker: &Checker, stmt: &Stmt, items: &[WithItem],
if raises_called { if raises_called {
let is_too_complex = if let [stmt] = body { let is_too_complex = if let [stmt] = body {
match stmt { 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. // Allow function and class definitions to test decorators.
Stmt::ClassDef(_) | Stmt::FunctionDef(_) => false, Stmt::ClassDef(_) | Stmt::FunctionDef(_) => false,
// Allow empty `for` loops to test iterators. // 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::Pass(_)] => false,
[Stmt::Expr(ast::StmtExpr { value, .. })] => !value.is_ellipsis_literal_expr(), [Stmt::Expr(ast::StmtExpr { value, .. })] => !value.is_ellipsis_literal_expr(),
_ => true, _ => true,

View File

@ -162,12 +162,12 @@ impl TryFrom<&str> for UnittestAssert {
} }
fn assert(expr: &Expr, msg: Option<&Expr>) -> Stmt { fn assert(expr: &Expr, msg: Option<&Expr>) -> Stmt {
Stmt::Assert(ast::StmtAssert { Stmt::Assert(Box::new(ast::StmtAssert {
test: Box::new(expr.clone()), test: Box::new(expr.clone()),
msg: msg.map(|msg| Box::new(msg.clone())), msg: msg.map(|msg| Box::new(msg.clone())),
range: TextRange::default(), range: TextRange::default(),
node_index: ruff_python_ast::AtomicNodeIndex::NONE, node_index: ruff_python_ast::AtomicNodeIndex::NONE,
}) }))
} }
fn compare(left: &Expr, cmp_op: CmpOp, right: &Expr) -> Expr { fn compare(left: &Expr, cmp_op: CmpOp, right: &Expr) -> Expr {

View File

@ -206,11 +206,11 @@ pub(crate) fn complex_warns(checker: &Checker, stmt: &Stmt, items: &[WithItem],
if warns_called { if warns_called {
let is_too_complex = if let [stmt] = body { let is_too_complex = if let [stmt] = body {
match stmt { 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. // Allow function and class definitions to test decorators.
Stmt::ClassDef(_) | Stmt::FunctionDef(_) => false, Stmt::ClassDef(_) | Stmt::FunctionDef(_) => false,
// Allow empty `for` loops to test iterators. // 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::Pass(_)] => false,
[Stmt::Expr(ast::StmtExpr { value, .. })] => !value.is_ellipsis_literal_expr(), [Stmt::Expr(ast::StmtExpr { value, .. })] => !value.is_ellipsis_literal_expr(),
_ => true, _ => true,

View File

@ -448,12 +448,12 @@ fn is_noreturn_func(func: &Expr, semantic: &SemanticModel) -> bool {
return false; return false;
}; };
let Stmt::FunctionDef(ast::StmtFunctionDef { returns, .. }) = semantic.statement(node_id) let Stmt::FunctionDef(node) = semantic.statement(node_id)
else { else {
return false; return false;
}; };
let Some(returns) = returns.as_ref() else { let Some(returns) = node.returns.as_ref() else {
return false; 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 { fn has_implicit_return(checker: &Checker, stmt: &Stmt) -> bool {
match stmt { match stmt {
Stmt::If(ast::StmtIf { Stmt::If(node) => {
body, if node
elif_else_clauses, .body
..
}) => {
if body
.last() .last()
.is_some_and(|last| has_implicit_return(checker, last)) .is_some_and(|last| has_implicit_return(checker, last))
{ {
return true; return true;
} }
if elif_else_clauses.iter().any(|clause| { if node.elif_else_clauses.iter().any(|clause| {
clause clause
.body .body
.last() .last()
@ -504,25 +501,33 @@ fn has_implicit_return(checker: &Checker, stmt: &Stmt) -> bool {
// Check if we don't have an else clause // Check if we don't have an else clause
matches!( matches!(
elif_else_clauses.last(), node.elif_else_clauses.last(),
None | Some(ast::ElifElseClause { test: Some(_), .. }) None | Some(ast::ElifElseClause { test: Some(_), .. })
) )
} }
Stmt::Assert(ast::StmtAssert { test, .. }) if is_const_false(test) => false, Stmt::Assert(node) if is_const_false(&node.test) => false,
Stmt::While(ast::StmtWhile { test, .. }) if is_const_true(test) => false, Stmt::While(node) if is_const_true(&node.test) => false,
Stmt::For(ast::StmtFor { orelse, .. }) | Stmt::While(ast::StmtWhile { orelse, .. }) => { Stmt::For(node) => {
if let Some(last_stmt) = orelse.last() { if let Some(last_stmt) = node.orelse.last() {
has_implicit_return(checker, last_stmt) has_implicit_return(checker, last_stmt)
} else { } else {
true 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 case.body
.last() .last()
.is_some_and(|last| has_implicit_return(checker, last)) .is_some_and(|last| has_implicit_return(checker, last))
}), }),
Stmt::With(ast::StmtWith { body, .. }) => body Stmt::With(node) => node
.body
.last() .last()
.is_some_and(|last_stmt| has_implicit_return(checker, last_stmt)), .is_some_and(|last_stmt| has_implicit_return(checker, last_stmt)),
Stmt::Return(_) | Stmt::Raise(_) | Stmt::Try(_) => false, Stmt::Return(_) | Stmt::Raise(_) | Stmt::Try(_) => false,

View File

@ -62,11 +62,11 @@ impl<'semantic, 'data> ReturnVisitor<'semantic, 'data> {
impl<'a> Visitor<'a> for ReturnVisitor<'_, 'a> { impl<'a> Visitor<'a> for ReturnVisitor<'_, 'a> {
fn visit_stmt(&mut self, stmt: &'a Stmt) { fn visit_stmt(&mut self, stmt: &'a Stmt) {
match stmt { match stmt {
Stmt::ClassDef(ast::StmtClassDef { decorator_list, .. }) => { Stmt::ClassDef(node) => {
// Visit the decorators, etc. // Visit the decorators, etc.
self.sibling = Some(stmt); self.sibling = Some(stmt);
self.parents.push(stmt); self.parents.push(stmt);
for decorator in decorator_list { for decorator in &node.decorator_list {
visitor::walk_decorator(self, decorator); visitor::walk_decorator(self, decorator);
} }
self.parents.pop(); self.parents.pop();
@ -74,12 +74,15 @@ impl<'a> Visitor<'a> for ReturnVisitor<'_, 'a> {
// But don't recurse into the body. // But don't recurse into the body.
return; return;
} }
Stmt::FunctionDef(ast::StmtFunctionDef { Stmt::FunctionDef(node) => {
let ast::StmtFunctionDef {
parameters, parameters,
decorator_list, decorator_list,
returns, returns,
range: _,
node_index: _,
.. ..
}) => { } = &**node;
// Visit the decorators, etc. // Visit the decorators, etc.
self.sibling = Some(stmt); self.sibling = Some(stmt);
self.parents.push(stmt); self.parents.push(stmt);
@ -95,24 +98,30 @@ impl<'a> Visitor<'a> for ReturnVisitor<'_, 'a> {
// But don't recurse into the body. // But don't recurse into the body.
return; return;
} }
Stmt::Global(ast::StmtGlobal { Stmt::Global(node) => {
let ast::StmtGlobal {
names, names,
range: _, range: _,
node_index: _, node_index: _,
}) } = &**node;
| Stmt::Nonlocal(ast::StmtNonlocal {
names,
range: _,
node_index: _,
}) => {
self.stack self.stack
.non_locals .non_locals
.extend(names.iter().map(Identifier::as_str)); .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` // Ex) `x: int`
if value.is_none() { if node.value.is_none() {
if let Expr::Name(name) = target.as_ref() { if let Expr::Name(name) = node.target.as_ref() {
self.stack.annotations.insert(name.id.as_str()); self.stack.annotations.insert(name.id.as_str());
} }
} }
@ -140,11 +149,11 @@ impl<'a> Visitor<'a> for ReturnVisitor<'_, 'a> {
// x = f.read() // x = f.read()
// return x // return x
// ``` // ```
Stmt::With(with) => { Stmt::With(with_node) => {
if let Some(stmt_assign) = 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(( self.stack.assignment_return.push((
stmt_assign, stmt_assign,
stmt_return, stmt_return,
@ -159,11 +168,14 @@ impl<'a> Visitor<'a> for ReturnVisitor<'_, 'a> {
self.stack.returns.push(stmt_return); self.stack.returns.push(stmt_return);
} }
Stmt::If(ast::StmtIf { Stmt::If(node) => {
let ast::StmtIf {
body, body,
elif_else_clauses, elif_else_clauses,
range: _,
node_index: _,
.. ..
}) => { } = &**node;
if let Some(first) = elif_else_clauses.first() { if let Some(first) = elif_else_clauses.first() {
self.stack.elifs_elses.push((body, first)); self.stack.elifs_elses.push((body, first));
} }

View File

@ -140,10 +140,10 @@ fn is_dunder_method(name: &str) -> bool {
} }
fn is_exception_check(stmt: &Stmt) -> bool { fn is_exception_check(stmt: &Stmt) -> bool {
let Stmt::If(ast::StmtIf { body, .. }) = stmt else { let Stmt::If(node) = stmt else {
return false; return false;
}; };
matches!(body.as_slice(), [Stmt::Raise(_)]) matches!(node.body.as_slice(), [Stmt::Raise(_)])
} }
/// SIM201 /// SIM201

View File

@ -68,18 +68,10 @@ impl Violation for MultipleWithStatements {
/// Returns a boolean indicating whether it's an async with statement, the items /// Returns a boolean indicating whether it's an async with statement, the items
/// and body. /// and body.
fn next_with(body: &[Stmt]) -> Option<(bool, &[WithItem], &[Stmt])> { fn next_with(body: &[Stmt]) -> Option<(bool, &[WithItem], &[Stmt])> {
let [ let [Stmt::With(node)] = body else {
Stmt::With(ast::StmtWith {
is_async,
items,
body,
..
}),
] = body
else {
return None; 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 /// 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(): // with B(), C():
// print("hello") // print("hello")
// ``` // ```
if let Some(Stmt::With(ast::StmtWith { body, .. })) = with_parent { if let Some(Stmt::With(node)) = with_parent {
if body.len() == 1 { if node.body.len() == 1 {
return; return;
} }
} }

View File

@ -230,21 +230,13 @@ fn nested_if_body(stmt_if: &ast::StmtIf) -> Option<NestedIf<'_>> {
/// ... /// ...
/// ``` /// ```
fn find_last_nested_if(body: &[Stmt]) -> Option<&Expr> { fn find_last_nested_if(body: &[Stmt]) -> Option<&Expr> {
let [ let [Stmt::If(node)] = body else {
Stmt::If(ast::StmtIf {
test,
body: inner_body,
elif_else_clauses,
..
}),
] = body
else {
return None; return None;
}; };
if !elif_else_clauses.is_empty() { if !node.elif_else_clauses.is_empty() {
return None; 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. /// Returns `true` if an expression is an `if __name__ == "__main__":` check.

View File

@ -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 /// If the statement is an index increment statement (e.g., `i += 1`), return
/// the name of the index variable. /// the name of the index variable.
fn match_index_increment(stmt: &Stmt) -> Option<&ast::ExprName> { fn match_index_increment(stmt: &Stmt) -> Option<&ast::ExprName> {
let Stmt::AugAssign(ast::StmtAugAssign { let Stmt::AugAssign(node) = stmt else {
target,
op: Operator::Add,
value,
..
}) = stmt
else {
return None; 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!( if matches!(
value.as_ref(), node.value.as_ref(),
Expr::NumberLiteral(ast::ExprNumberLiteral { Expr::NumberLiteral(ast::ExprNumberLiteral {
value: Number::Int(Int::ONE), value: Number::Int(Int::ONE),
.. ..

View File

@ -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 { let [else_body_stmt] = else_body.as_slice() else {
return; return;
}; };
let Stmt::Assign(ast::StmtAssign { let Stmt::Assign(body_node) = &body_stmt else {
targets: body_var,
value: body_value,
..
}) = &body_stmt
else {
return; return;
}; };
let [body_var] = body_var.as_slice() else { let [body_var] = body_node.targets.as_slice() else {
return; return;
}; };
let Stmt::Assign(ast::StmtAssign { let Stmt::Assign(orelse_node) = &else_body_stmt else {
targets: orelse_var,
value: orelse_value,
..
}) = &else_body_stmt
else {
return; return;
}; };
let [orelse_var] = orelse_var.as_slice() else { let [orelse_var] = orelse_node.targets.as_slice() else {
return; 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[..] { let (expected_var, expected_value, default_var, default_value) = match ops[..] {
[CmpOp::In] => (body_var, body_value, orelse_var, orelse_value.as_ref()), [CmpOp::In] => (body_var, &body_node.value, orelse_var, orelse_node.value.as_ref()),
[CmpOp::NotIn] => (orelse_var, orelse_value, body_var, body_value.as_ref()), [CmpOp::NotIn] => (orelse_var, &orelse_node.value, body_var, body_node.value.as_ref()),
_ => { _ => {
return; return;
} }

View File

@ -112,27 +112,13 @@ pub(crate) fn if_else_block_instead_of_if_exp(checker: &Checker, stmt_if: &ast::
else { else {
return; return;
}; };
let [ let [Stmt::Assign(body_node)] = body.as_slice() else {
Stmt::Assign(ast::StmtAssign {
targets: body_targets,
value: body_value,
..
}),
] = body.as_slice()
else {
return; return;
}; };
let [ let [Stmt::Assign(else_node)] = else_body.as_slice() else {
Stmt::Assign(ast::StmtAssign {
targets: else_targets,
value: else_value,
..
}),
] = else_body.as_slice()
else {
return; 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; return;
}; };
let Expr::Name(ast::ExprName { id: body_id, .. }) = body_target else { 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. // Avoid suggesting ternary for `if (yield ...)`-style checks.
// TODO(charlie): Fix precedence handling for yields in generator. // TODO(charlie): Fix precedence handling for yields in generator.
if matches!( if matches!(
body_value.as_ref(), body_node.value.as_ref(),
Expr::Yield(_) | Expr::YieldFrom(_) | Expr::Await(_) Expr::Yield(_) | Expr::YieldFrom(_) | Expr::Await(_)
) { ) {
return; return;
} }
if matches!( if matches!(
else_value.as_ref(), else_node.value.as_ref(),
Expr::Yield(_) | Expr::YieldFrom(_) | Expr::Await(_) Expr::Yield(_) | Expr::YieldFrom(_) | Expr::Await(_)
) { ) {
return; 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 `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` // - 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` // - Otherwise, replace with `target_var = body_value if test else else_value`
let (contents, assignment_kind) = match (test, body_value) { let (contents, assignment_kind) = match (test, &body_node.value) {
(test_node, body_node) (test_node, body_val_node)
if ComparableExpr::from(test_node) == ComparableExpr::from(body_node) if ComparableExpr::from(test_node) == ComparableExpr::from(body_val_node.as_ref())
&& !contains_effect(test_node, |id| checker.semantic().has_builtin_binding(id)) => && !contains_effect(test_node, |id| checker.semantic().has_builtin_binding(id)) =>
{ {
let target_var = &body_target; 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) (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| { if (test_node.as_unary_op_expr().is_some_and(|op_expr| {
op_expr.op.is_not() op_expr.op.is_not()
&& ComparableExpr::from(&op_expr.operand) == ComparableExpr::from(body_node) && ComparableExpr::from(&op_expr.operand) == ComparableExpr::from(body_val_node.as_ref())
}) || body_node.as_unary_op_expr().is_some_and(|op_expr| { }) || body_val_node.as_ref().as_unary_op_expr().is_some_and(|op_expr| {
op_expr.op.is_not() op_expr.op.is_not()
&& ComparableExpr::from(&op_expr.operand) == ComparableExpr::from(test_node) && ComparableExpr::from(&op_expr.operand) == ComparableExpr::from(test_node)
})) && !contains_effect(test_node, |id| { })) && !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 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) (checker.generator().stmt(&binary), AssignmentKind::Binary)
} }
_ => { _ => {
let target_var = &body_target; 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) (checker.generator().stmt(&ternary), AssignmentKind::Ternary)
} }
}; };

View File

@ -92,15 +92,9 @@ impl Violation for NeedlessBool {
/// SIM103 /// SIM103
pub(crate) fn needless_bool(checker: &Checker, stmt: &Stmt) { pub(crate) fn needless_bool(checker: &Checker, stmt: &Stmt) {
let Stmt::If(stmt_if) = stmt else { return }; 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) // 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: // if-else case:
// ```python // ```python
// if x > 0: // if x > 0:
@ -115,8 +109,8 @@ pub(crate) fn needless_bool(checker: &Checker, stmt: &Stmt) {
.. ..
}, },
] => ( ] => (
if_test.as_ref(), stmt_if.test.as_ref(),
if_body, stmt_if.body.as_slice(),
else_body.as_slice(), else_body.as_slice(),
stmt_if.range(), stmt_if.range(),
), ),
@ -143,7 +137,7 @@ pub(crate) fn needless_bool(checker: &Checker, stmt: &Stmt) {
}, },
] => ( ] => (
elif_test, elif_test,
elif_body, elif_body.as_slice(),
else_body.as_slice(), else_body.as_slice(),
TextRange::new(elif_range.start(), else_range.end()), 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. // 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; return;
} }
@ -175,8 +169,8 @@ pub(crate) fn needless_bool(checker: &Checker, stmt: &Stmt) {
} }
( (
if_test.as_ref(), stmt_if.test.as_ref(),
if_body, stmt_if.body.as_slice(),
std::slice::from_ref(next_stmt), std::slice::from_ref(next_stmt),
TextRange::new(stmt_if.start(), next_stmt.end()), 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, op: ast::UnaryOp::Not,
operand, operand,
.. ..
}) => Some((**operand).clone()), }) => Some(operand.clone()),
Expr::Compare(ast::ExprCompare { Expr::Compare(ast::ExprCompare {
ops, ops,
@ -252,26 +246,26 @@ pub(crate) fn needless_bool(checker: &Checker, stmt: &Stmt) {
unreachable!("Single comparison with multiple comparators"); unreachable!("Single comparison with multiple comparators");
}; };
Some(Expr::Compare(ast::ExprCompare { Some(Box::new(Expr::Compare(ast::ExprCompare {
ops: Box::new([op.negate()]), ops: Box::new([op.negate()]),
left: left.clone(), left: left.clone(),
comparators: Box::new([right.clone()]), comparators: Box::new([right.clone()]),
range: TextRange::default(), range: TextRange::default(),
node_index: ruff_python_ast::AtomicNodeIndex::NONE, node_index: ruff_python_ast::AtomicNodeIndex::NONE,
})) })))
} }
_ => Some(Expr::UnaryOp(ast::ExprUnaryOp { _ => Some(Box::new(Expr::UnaryOp(ast::ExprUnaryOp {
op: ast::UnaryOp::Not, op: ast::UnaryOp::Not,
operand: Box::new(if_test.clone()), operand: Box::new(if_test.clone()),
range: TextRange::default(), range: TextRange::default(),
node_index: ruff_python_ast::AtomicNodeIndex::NONE, node_index: ruff_python_ast::AtomicNodeIndex::NONE,
})), }))),
} }
} else if if_test.is_compare_expr() { } else if if_test.is_compare_expr() {
// If the condition is a comparison, we can replace it with the condition, since we // If the condition is a comparison, we can replace it with the condition, since we
// know it's a boolean. // know it's a boolean.
Some(if_test.clone()) Some(Box::new(if_test.clone()))
} else if checker.semantic().has_builtin_binding("bool") { } else if checker.semantic().has_builtin_binding("bool") {
// Otherwise, we need to wrap the condition in a call to `bool`. // Otherwise, we need to wrap the condition in a call to `bool`.
let func_node = ast::ExprName { let func_node = ast::ExprName {
@ -291,7 +285,7 @@ pub(crate) fn needless_bool(checker: &Checker, stmt: &Stmt) {
range: TextRange::default(), range: TextRange::default(),
node_index: ruff_python_ast::AtomicNodeIndex::NONE, node_index: ruff_python_ast::AtomicNodeIndex::NONE,
}; };
Some(Expr::Call(call_node)) Some(Box::new(Expr::Call(call_node)))
} else { } else {
None None
} }
@ -300,7 +294,7 @@ pub(crate) fn needless_bool(checker: &Checker, stmt: &Stmt) {
// Generate the replacement `return` statement. // Generate the replacement `return` statement.
let replacement = condition.as_ref().map(|expr| { let replacement = condition.as_ref().map(|expr| {
Stmt::Return(ast::StmtReturn { Stmt::Return(ast::StmtReturn {
value: Some(Box::new(expr.clone())), value: Some(expr.clone()),
range: TextRange::default(), range: TextRange::default(),
node_index: ruff_python_ast::AtomicNodeIndex::NONE, node_index: ruff_python_ast::AtomicNodeIndex::NONE,
}) })

View File

@ -68,8 +68,8 @@ fn match_async_exit_stack(semantic: &SemanticModel) -> bool {
return false; return false;
} }
for parent in semantic.current_statements() { for parent in semantic.current_statements() {
if let Stmt::With(ast::StmtWith { items, .. }) = parent { if let Stmt::With(node) = parent {
for item in items { for item in &node.items {
if let Expr::Call(ast::ExprCall { func, .. }) = &item.context_expr { if let Expr::Call(ast::ExprCall { func, .. }) = &item.context_expr {
if semantic if semantic
.resolve_qualified_name(func) .resolve_qualified_name(func)
@ -102,8 +102,8 @@ fn match_exit_stack(semantic: &SemanticModel) -> bool {
return false; return false;
} }
for parent in semantic.current_statements() { for parent in semantic.current_statements() {
if let Stmt::With(ast::StmtWith { items, .. }) = parent { if let Stmt::With(node) = parent {
for item in items { for item in &node.items {
if let Expr::Call(ast::ExprCall { func, .. }) = &item.context_expr { if let Expr::Call(ast::ExprCall { func, .. }) = &item.context_expr {
if semantic if semantic
.resolve_qualified_name(func) .resolve_qualified_name(func)

View File

@ -269,27 +269,25 @@ struct Terminal<'a> {
} }
fn match_loop(stmt: &Stmt) -> Option<Loop<'_>> { fn match_loop(stmt: &Stmt) -> Option<Loop<'_>> {
let Stmt::For(ast::StmtFor { let Stmt::For(for_stmt) = stmt else {
body, target, iter, ..
}) = stmt
else {
return None; 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 loop itself should contain a single `if` statement, with a single `return` statement in
// the body. // the body.
let [ let [Stmt::If(if_stmt)] = body.as_slice() else {
Stmt::If(ast::StmtIf { return None;
};
let ast::StmtIf {
body: nested_body, body: nested_body,
test: nested_test, test: nested_test,
elif_else_clauses: nested_elif_else_clauses, elif_else_clauses: nested_elif_else_clauses,
range: _, range: _,
node_index: _, node_index: _,
}), } = &**if_stmt;
] = body.as_slice()
else {
return None;
};
if !nested_elif_else_clauses.is_empty() { if !nested_elif_else_clauses.is_empty() {
return None; return None;
} }
@ -326,9 +324,10 @@ fn match_loop(stmt: &Stmt) -> Option<Loop<'_>> {
/// return False /// return False
/// ``` /// ```
fn match_else_return(stmt: &Stmt) -> Option<Terminal<'_>> { fn match_else_return(stmt: &Stmt) -> Option<Terminal<'_>> {
let Stmt::For(ast::StmtFor { orelse, .. }) = stmt else { let Stmt::For(for_stmt) = stmt else {
return None; return None;
}; };
let ast::StmtFor { orelse, .. } = &**for_stmt;
// The `else` block has to contain a single `return True` or `return False`. // The `else` block has to contain a single `return True` or `return False`.
let [ let [
@ -366,9 +365,10 @@ fn match_else_return(stmt: &Stmt) -> Option<Terminal<'_>> {
/// return False /// return False
/// ``` /// ```
fn match_sibling_return<'a>(stmt: &'a Stmt, sibling: &'a Stmt) -> Option<Terminal<'a>> { fn match_sibling_return<'a>(stmt: &'a Stmt, sibling: &'a Stmt) -> Option<Terminal<'a>> {
let Stmt::For(ast::StmtFor { orelse, .. }) = stmt else { let Stmt::For(for_stmt) = stmt else {
return None; return None;
}; };
let ast::StmtFor { orelse, .. } = &**for_stmt;
// The loop itself shouldn't have an `else` block. // The loop itself shouldn't have an `else` block.
if !orelse.is_empty() { if !orelse.is_empty() {

View File

@ -4,7 +4,8 @@ use ruff_python_ast::{self as ast, Expr, Stmt};
pub(super) fn has_slots(body: &[Stmt]) -> bool { pub(super) fn has_slots(body: &[Stmt]) -> bool {
for stmt in body { for stmt in body {
match stmt { match stmt {
Stmt::Assign(ast::StmtAssign { targets, .. }) => { Stmt::Assign(assign) => {
let targets = &assign.targets;
for target in targets { for target in targets {
if let Expr::Name(ast::ExprName { id, .. }) = target { if let Expr::Name(ast::ExprName { id, .. }) = target {
if id.as_str() == "__slots__" { 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 let Expr::Name(ast::ExprName { id, .. }) = target.as_ref() {
if id.as_str() == "__slots__" { if id.as_str() == "__slots__" {
return true; return true;

View File

@ -92,9 +92,11 @@ impl<'a> BannedModuleImportPolicies<'a> {
pub(crate) fn new(stmt: &'a Stmt, checker: &Checker) -> Self { pub(crate) fn new(stmt: &'a Stmt, checker: &Checker) -> Self {
match stmt { match stmt {
Stmt::Import(import) => Self::Import(import), 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( let module = resolve_imported_module_path(
*level, level,
module.as_deref(), module.as_deref(),
checker.module.qualified_name(), checker.module.qualified_name(),
); );

View File

@ -91,9 +91,10 @@ fn fix_banned_relative_import(
return None; return None;
} }
let Stmt::ImportFrom(ast::StmtImportFrom { names, .. }) = stmt else { let Stmt::ImportFrom(import_from) = stmt else {
panic!("Expected Stmt::ImportFrom"); panic!("Expected Stmt::ImportFrom");
}; };
let names = &import_from.names;
let node = ast::StmtImportFrom { let node = ast::StmtImportFrom {
module: Some(Identifier::new( module: Some(Identifier::new(
module_path.to_string(), module_path.to_string(),

View File

@ -158,10 +158,10 @@ pub(crate) fn unquoted_type_alias(checker: &Checker, binding: &Binding) {
return; return;
} }
let Some(Stmt::AnnAssign(ast::StmtAnnAssign { let Some(Stmt::AnnAssign(node)) = binding.statement(checker.semantic()) else {
value: Some(expr), .. return;
})) = binding.statement(checker.semantic()) };
else { let Some(expr) = &node.value else {
return; return;
}; };

View File

@ -1,5 +1,5 @@
use ruff_python_ast as ast; 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_macros::{ViolationMetadata, derive_message_formats};
use ruff_python_semantic::analyze::{function_type, visibility}; use ruff_python_semantic::analyze::{function_type, visibility};
@ -389,14 +389,20 @@ pub(crate) fn is_not_implemented_stub_with_variable(
_ => &function_def.body, _ => &function_def.body,
}; };
let [ let [stmt1, stmt2] = statements else {
Stmt::Assign(ast::StmtAssign { targets, value, .. }), return false;
Stmt::Raise(StmtRaise { };
exc: Some(exception),
.. let Stmt::Assign(assign_node) = stmt1 else {
}), return false;
] = statements };
else { 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; return false;
}; };

View File

@ -23,11 +23,13 @@ pub(crate) fn annotate_imports<'a>(
.iter() .iter()
.map(|import| { .map(|import| {
match import { match import {
Stmt::Import(ast::StmtImport { Stmt::Import(import_stmt) => {
let ast::StmtImport {
names, names,
range, range,
node_index: _, node_index: _,
}) => { } = &**import_stmt;
// Find comments above. // Find comments above.
let mut atop = vec![]; let mut atop = vec![];
while let Some(comment) = while let Some(comment) =
@ -58,13 +60,15 @@ pub(crate) fn annotate_imports<'a>(
inline, inline,
} }
} }
Stmt::ImportFrom(ast::StmtImportFrom { Stmt::ImportFrom(import_from_stmt) => {
let ast::StmtImportFrom {
module, module,
names, names,
level, level,
range: _, range: _,
node_index: _, node_index: _,
}) => { } = &**import_from_stmt;
// Find comments above. // Find comments above.
let mut atop = vec![]; let mut atop = vec![];
while let Some(comment) = while let Some(comment) =

View File

@ -183,87 +183,77 @@ impl<'a> StatementVisitor<'a> for BlockBuilder<'a> {
let prev_nested = self.nested; let prev_nested = self.nested;
self.nested = true; self.nested = true;
match stmt { match stmt {
Stmt::FunctionDef(ast::StmtFunctionDef { body, .. }) => { Stmt::FunctionDef(node) => {
for stmt in body { for stmt in &node.body {
self.visit_stmt(stmt); self.visit_stmt(stmt);
} }
self.finalize(None); self.finalize(None);
} }
Stmt::ClassDef(ast::StmtClassDef { body, .. }) => { Stmt::ClassDef(node) => {
for stmt in body { for stmt in &node.body {
self.visit_stmt(stmt); self.visit_stmt(stmt);
} }
self.finalize(None); self.finalize(None);
} }
Stmt::For(ast::StmtFor { body, orelse, .. }) => { Stmt::For(node) => {
for stmt in body { for stmt in &node.body {
self.visit_stmt(stmt); self.visit_stmt(stmt);
} }
self.finalize(None); self.finalize(None);
for stmt in orelse { for stmt in &node.orelse {
self.visit_stmt(stmt); self.visit_stmt(stmt);
} }
self.finalize(None); self.finalize(None);
} }
Stmt::While(ast::StmtWhile { body, orelse, .. }) => { Stmt::While(node) => {
for stmt in body { for stmt in &node.body {
self.visit_stmt(stmt); self.visit_stmt(stmt);
} }
self.finalize(None); self.finalize(None);
for stmt in orelse { for stmt in &node.orelse {
self.visit_stmt(stmt); self.visit_stmt(stmt);
} }
self.finalize(None); self.finalize(None);
} }
Stmt::If(ast::StmtIf { Stmt::If(node) => {
body, for stmt in &node.body {
elif_else_clauses,
..
}) => {
for stmt in body {
self.visit_stmt(stmt); self.visit_stmt(stmt);
} }
self.finalize(None); self.finalize(None);
for clause in elif_else_clauses { for clause in &node.elif_else_clauses {
self.visit_elif_else_clause(clause); self.visit_elif_else_clause(clause);
} }
} }
Stmt::With(ast::StmtWith { body, .. }) => { Stmt::With(node) => {
for stmt in body { for stmt in &node.body {
self.visit_stmt(stmt); self.visit_stmt(stmt);
} }
self.finalize(None); self.finalize(None);
} }
Stmt::Match(ast::StmtMatch { cases, .. }) => { Stmt::Match(node) => {
for match_case in cases { for match_case in &node.cases {
self.visit_match_case(match_case); self.visit_match_case(match_case);
} }
} }
Stmt::Try(ast::StmtTry { Stmt::Try(node) => {
body, for except_handler in &node.handlers {
handlers,
orelse,
finalbody,
..
}) => {
for except_handler in handlers {
self.visit_except_handler(except_handler); self.visit_except_handler(except_handler);
} }
for stmt in body { for stmt in &node.body {
self.visit_stmt(stmt); self.visit_stmt(stmt);
} }
self.finalize(None); self.finalize(None);
for stmt in orelse { for stmt in &node.orelse {
self.visit_stmt(stmt); self.visit_stmt(stmt);
} }
self.finalize(None); self.finalize(None);
for stmt in finalbody { for stmt in &node.finalbody {
self.visit_stmt(stmt); self.visit_stmt(stmt);
} }
self.finalize(None); self.finalize(None);

View File

@ -59,30 +59,30 @@ impl AlwaysFixableViolation for MissingRequiredImport {
fn includes_import(stmt: &Stmt, target: &NameImport) -> bool { fn includes_import(stmt: &Stmt, target: &NameImport) -> bool {
match target { match target {
NameImport::Import(target) => { NameImport::Import(target) => {
let Stmt::Import(ast::StmtImport { let Stmt::Import(import_stmt) = &stmt else {
return false;
};
let ast::StmtImport {
names, names,
range: _, range: _,
node_index: _, node_index: _,
}) = &stmt } = &**import_stmt;
else {
return false;
};
names.iter().any(|alias| { names.iter().any(|alias| {
alias.name == target.name.name alias.name == target.name.name
&& alias.asname.as_deref() == target.name.as_name.as_deref() && alias.asname.as_deref() == target.name.as_name.as_deref()
}) })
} }
NameImport::ImportFrom(target) => { NameImport::ImportFrom(target) => {
let Stmt::ImportFrom(ast::StmtImportFrom { let Stmt::ImportFrom(import_from_stmt) = &stmt else {
return false;
};
let ast::StmtImportFrom {
module, module,
names, names,
level, level,
range: _, range: _,
node_index: _, node_index: _,
}) = &stmt } = &**import_from_stmt;
else {
return false;
};
module.as_deref() == target.module.as_deref() module.as_deref() == target.module.as_deref()
&& *level == target.level && *level == target.level
&& names.iter().any(|alias| { && names.iter().any(|alias| {

View File

@ -71,39 +71,35 @@ fn get_complexity_number(stmts: &[Stmt]) -> usize {
let mut complexity = 0; let mut complexity = 0;
for stmt in stmts { for stmt in stmts {
match stmt { match stmt {
Stmt::If(ast::StmtIf { Stmt::If(if_stmt) => {
body,
elif_else_clauses,
..
}) => {
complexity += 1; complexity += 1;
complexity += get_complexity_number(body); complexity += get_complexity_number(&if_stmt.body);
for clause in elif_else_clauses { for clause in &if_stmt.elif_else_clauses {
if clause.test.is_some() { if clause.test.is_some() {
complexity += 1; complexity += 1;
} }
complexity += get_complexity_number(&clause.body); complexity += get_complexity_number(&clause.body);
} }
} }
Stmt::For(ast::StmtFor { body, orelse, .. }) => { Stmt::For(for_stmt) => {
complexity += 1; complexity += 1;
complexity += get_complexity_number(body); complexity += get_complexity_number(&for_stmt.body);
complexity += get_complexity_number(orelse); complexity += get_complexity_number(&for_stmt.orelse);
} }
Stmt::With(ast::StmtWith { body, .. }) => { Stmt::With(with_stmt) => {
complexity += get_complexity_number(body); complexity += get_complexity_number(&with_stmt.body);
} }
Stmt::While(ast::StmtWhile { body, orelse, .. }) => { Stmt::While(while_stmt) => {
complexity += 1; complexity += 1;
complexity += get_complexity_number(body); complexity += get_complexity_number(&while_stmt.body);
complexity += get_complexity_number(orelse); complexity += get_complexity_number(&while_stmt.orelse);
} }
Stmt::Match(ast::StmtMatch { cases, .. }) => { Stmt::Match(match_stmt) => {
for case in cases { for case in &match_stmt.cases {
complexity += 1; complexity += 1;
complexity += get_complexity_number(&case.body); 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. // The complexity of an irrefutable pattern is similar to an `else` block of an `if` statement.
// //
// For example: // For example:
@ -121,20 +117,14 @@ fn get_complexity_number(stmts: &[Stmt]) -> usize {
} }
} }
} }
Stmt::Try(ast::StmtTry { Stmt::Try(try_stmt) => {
body, complexity += get_complexity_number(&try_stmt.body);
handlers, if !try_stmt.orelse.is_empty() {
orelse,
finalbody,
..
}) => {
complexity += get_complexity_number(body);
if !orelse.is_empty() {
complexity += 1; complexity += 1;
} }
complexity += get_complexity_number(orelse); complexity += get_complexity_number(&try_stmt.orelse);
complexity += get_complexity_number(finalbody); complexity += get_complexity_number(&try_stmt.finalbody);
for handler in handlers { for handler in &try_stmt.handlers {
complexity += 1; complexity += 1;
let ExceptHandler::ExceptHandler(ast::ExceptHandlerExceptHandler { let ExceptHandler::ExceptHandler(ast::ExceptHandlerExceptHandler {
body, .. body, ..
@ -142,12 +132,12 @@ fn get_complexity_number(stmts: &[Stmt]) -> usize {
complexity += get_complexity_number(body); complexity += get_complexity_number(body);
} }
} }
Stmt::FunctionDef(ast::StmtFunctionDef { body, .. }) => { Stmt::FunctionDef(func_def) => {
complexity += 1; complexity += 1;
complexity += get_complexity_number(body); complexity += get_complexity_number(&func_def.body);
} }
Stmt::ClassDef(ast::StmtClassDef { body, .. }) => { Stmt::ClassDef(class_def) => {
complexity += get_complexity_number(body); complexity += get_complexity_number(&class_def.body);
} }
_ => {} _ => {}
} }

View File

@ -3,7 +3,7 @@ use ruff_python_ast::name::QualifiedName;
use ruff_python_ast::statement_visitor::StatementVisitor; use ruff_python_ast::statement_visitor::StatementVisitor;
use ruff_python_ast::visitor::Visitor; use ruff_python_ast::visitor::Visitor;
use ruff_python_ast::visitor::{walk_expr, walk_stmt}; 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; use ruff_python_semantic::SemanticModel;
/// AST visitor that searches an AST tree for [`ast::StmtImportFrom`] nodes /// AST visitor that searches an AST tree for [`ast::StmtImportFrom`] nodes
@ -28,7 +28,9 @@ impl StatementVisitor<'_> for ImportSearcher<'_> {
if self.found_import { if self.found_import {
return; 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) if module.as_ref().is_some_and(|module| module == self.module)
&& names.iter().any(|Alias { name, .. }| name == self.name) && names.iter().any(|Alias { name, .. }| name == self.name)
{ {

View File

@ -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. /// Returns `true` if the statement is an assignment to a named tuple.
pub(super) fn is_named_tuple_assignment(stmt: &Stmt, semantic: &SemanticModel) -> bool { 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; 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; return false;
}; };
semantic semantic
@ -45,10 +45,10 @@ pub(super) fn is_typed_dict_assignment(stmt: &Stmt, semantic: &SemanticModel) ->
return false; return false;
} }
let Stmt::Assign(ast::StmtAssign { value, .. }) = stmt else { let Stmt::Assign(node) = stmt else {
return false; 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; return false;
}; };
semantic.match_typing_expr(func, "TypedDict") semantic.match_typing_expr(func, "TypedDict")
@ -60,10 +60,10 @@ pub(super) fn is_type_var_assignment(stmt: &Stmt, semantic: &SemanticModel) -> b
return false; return false;
} }
let Stmt::Assign(ast::StmtAssign { value, .. }) = stmt else { let Stmt::Assign(node) = stmt else {
return false; 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; return false;
}; };
semantic 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`. /// Returns `true` if the statement is an assignment to a `TypeAlias`.
pub(super) fn is_type_alias_assignment(stmt: &Stmt, semantic: &SemanticModel) -> bool { pub(super) fn is_type_alias_assignment(stmt: &Stmt, semantic: &SemanticModel) -> bool {
match stmt { match stmt {
Stmt::AnnAssign(ast::StmtAnnAssign { annotation, .. }) => { Stmt::AnnAssign(node) => {
semantic.match_typing_expr(annotation, "TypeAlias") semantic.match_typing_expr(&node.annotation, "TypeAlias")
} }
Stmt::TypeAlias(_) => true, Stmt::TypeAlias(_) => true,
_ => false, _ => false,
@ -157,11 +157,15 @@ pub(super) fn is_django_model_import(name: &str, stmt: &Stmt, semantic: &Semanti
} }
match stmt { match stmt {
Stmt::AnnAssign(ast::StmtAnnAssign { Stmt::AnnAssign(node) => {
value: Some(value), .. if let Some(value) = &node.value {
}) => match_model_import(name, value.as_ref(), semantic),
Stmt::Assign(ast::StmtAssign { value, .. }) => {
match_model_import(name, value.as_ref(), semantic) match_model_import(name, value.as_ref(), semantic)
} else {
false
}
}
Stmt::Assign(node) => {
match_model_import(name, node.value.as_ref(), semantic)
} }
_ => false, _ => false,
} }

View File

@ -92,23 +92,16 @@ pub(crate) fn manual_dict_comprehension(checker: &Checker, for_stmt: &ast::StmtF
// if idx % 2 == 0: // if idx % 2 == 0:
// result[name] = idx // result[name] = idx
// ``` // ```
[ [Stmt::If(node)] => {
Stmt::If(ast::StmtIf {
body,
elif_else_clauses,
test,
..
}),
] => {
// TODO(charlie): If there's an `else` clause, verify that the `else` has the // TODO(charlie): If there's an `else` clause, verify that the `else` has the
// same structure. // same structure.
if !elif_else_clauses.is_empty() { if !node.elif_else_clauses.is_empty() {
return; return;
} }
let [stmt] = body.as_slice() else { let [stmt] = node.body.as_slice() else {
return; return;
}; };
(stmt, Some(test)) (stmt, Some(&node.test))
} }
// ```python // ```python
// for idx, name in enumerate(names): // for idx, name in enumerate(names):
@ -118,15 +111,12 @@ pub(crate) fn manual_dict_comprehension(checker: &Checker, for_stmt: &ast::StmtF
_ => return, _ => return,
}; };
let Stmt::Assign(ast::StmtAssign { let Stmt::Assign(node) = stmt else {
targets,
value,
range,
node_index: _,
}) = stmt
else {
return; return;
}; };
let targets = &node.targets;
let value = &node.value;
let range = &node.range;
let [ let [
Expr::Subscript(ast::ExprSubscript { 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()) { if is_fix_manual_dict_comprehension_enabled(checker.settings()) {
let binding_stmt = binding.statement(checker.semantic()); let binding_stmt = binding.statement(checker.semantic());
let binding_value = binding_stmt.and_then(|binding_stmt| match binding_stmt { let binding_value = binding_stmt.and_then(|binding_stmt| match binding_stmt {
ast::Stmt::AnnAssign(assign) => assign.value.as_deref(), ast::Stmt::AnnAssign(node) => node.value.as_deref(),
ast::Stmt::Assign(assign) => Some(&assign.value), ast::Stmt::Assign(node) => Some(&node.value),
_ => None, _ => 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. // 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 { let binding_has_one_target = binding_stmt.is_some_and(|binding_stmt| match binding_stmt {
ast::Stmt::AnnAssign(_) => true, ast::Stmt::AnnAssign(_) => true,
ast::Stmt::Assign(assign) => assign.targets.len() == 1, ast::Stmt::Assign(node) => node.targets.len() == 1,
_ => false, _ => false,
}); });
// If the binding gets used in between the assignment and the for loop, a comprehension is no longer safe // If the binding gets used in between the assignment and the for loop, a comprehension is no longer safe

View File

@ -109,21 +109,14 @@ pub(crate) fn manual_list_comprehension(checker: &Checker, for_stmt: &ast::StmtF
// if z: // if z:
// filtered.append(x) // filtered.append(x)
// ``` // ```
[ [ast::Stmt::If(node)] => {
ast::Stmt::If(ast::StmtIf { if !node.elif_else_clauses.is_empty() {
body,
elif_else_clauses,
test,
..
}),
] => {
if !elif_else_clauses.is_empty() {
return; return;
} }
let [stmt] = body.as_slice() else { let [stmt] = node.body.as_slice() else {
return; return;
}; };
(stmt, Some(test)) (stmt, Some(&node.test))
} }
// ```python // ```python
// for x in y: // 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_stmt = list_binding.statement(checker.semantic());
let list_binding_value = list_binding_stmt.and_then(|binding_stmt| match binding_stmt { let list_binding_value = list_binding_stmt.and_then(|binding_stmt| match binding_stmt {
ast::Stmt::AnnAssign(assign) => assign.value.as_deref(), ast::Stmt::AnnAssign(node) => node.value.as_deref(),
ast::Stmt::Assign(assign) => Some(&assign.value), ast::Stmt::Assign(node) => Some(&node.value),
_ => None, _ => 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. // 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 { let binding_has_one_target = list_binding_stmt.is_some_and(|binding_stmt| match binding_stmt {
ast::Stmt::AnnAssign(_) => true, ast::Stmt::AnnAssign(_) => true,
ast::Stmt::Assign(assign) => assign.targets.len() == 1, ast::Stmt::Assign(node) => node.targets.len() == 1,
_ => false, _ => false,
}); });
@ -464,8 +457,8 @@ fn convert_to_list_extend(
let binding_stmt = binding.statement(semantic); let binding_stmt = binding.statement(semantic);
let binding_stmt_range = binding_stmt let binding_stmt_range = binding_stmt
.and_then(|stmt| match stmt { .and_then(|stmt| match stmt {
ast::Stmt::AnnAssign(assign) => Some(assign.range), ast::Stmt::AnnAssign(node) => Some(node.range),
ast::Stmt::Assign(assign) => Some(assign.range), ast::Stmt::Assign(node) => Some(node.range),
_ => None, _ => None,
}) })
.ok_or(anyhow!( .ok_or(anyhow!(

View File

@ -1,6 +1,6 @@
use ruff_macros::{ViolationMetadata, derive_message_formats}; use ruff_macros::{ViolationMetadata, derive_message_formats};
use ruff_python_ast::statement_visitor::{StatementVisitor, walk_stmt}; 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 ruff_text_size::Ranged;
use crate::Violation; use crate::Violation;
@ -93,9 +93,11 @@ pub(crate) fn try_except_in_loop(checker: &Checker, body: &[Stmt]) {
return; return;
} }
let [Stmt::Try(ast::StmtTry { handlers, body, .. })] = body else { let [Stmt::Try(try_stmt)] = body else {
return; return;
}; };
let handlers = &try_stmt.handlers;
let body = &try_stmt.body;
let Some(handler) = handlers.first() else { let Some(handler) = handlers.first() else {
return; return;

View File

@ -207,7 +207,7 @@ fn match_mutation(stmt: &Stmt, id: &str) -> bool {
target_id == id target_id == id
} }
// Ex) `foo[0] = bar` // 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::ExprSubscript { value: target, .. }) = target.as_subscript_expr() {
if let Some(ast::ExprName { id: target_id, .. }) = target.as_name_expr() { if let Some(ast::ExprName { id: target_id, .. }) = target.as_name_expr() {
return target_id == id; return target_id == id;
@ -216,16 +216,16 @@ fn match_mutation(stmt: &Stmt, id: &str) -> bool {
false false
}), }),
// Ex) `foo += bar` // Ex) `foo += bar`
Stmt::AugAssign(ast::StmtAugAssign { target, .. }) => { Stmt::AugAssign(node) => {
if let Some(ast::ExprName { id: target_id, .. }) = target.as_name_expr() { if let Some(ast::ExprName { id: target_id, .. }) = node.target.as_name_expr() {
target_id == id target_id == id
} else { } else {
false false
} }
} }
// Ex) `foo[0]: int = bar` // Ex) `foo[0]: int = bar`
Stmt::AnnAssign(ast::StmtAnnAssign { target, .. }) => { Stmt::AnnAssign(node) => {
if let Some(ast::ExprSubscript { value: target, .. }) = target.as_subscript_expr() { if let Some(ast::ExprSubscript { value: target, .. }) = node.target.as_subscript_expr() {
if let Some(ast::ExprName { id: target_id, .. }) = target.as_name_expr() { if let Some(ast::ExprName { id: target_id, .. }) = target.as_name_expr() {
return target_id == id; return target_id == id;
} }
@ -233,7 +233,7 @@ fn match_mutation(stmt: &Stmt, id: &str) -> bool {
false false
} }
// Ex) `del foo[0]` // 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::ExprSubscript { value: target, .. }) = target.as_subscript_expr() {
if let Some(ast::ExprName { id: target_id, .. }) = target.as_name_expr() { if let Some(ast::ExprName { id: target_id, .. }) = target.as_name_expr() {
return target_id == id; return target_id == id;

View File

@ -1,6 +1,6 @@
use ruff_macros::{ViolationMetadata, derive_message_formats}; use ruff_macros::{ViolationMetadata, derive_message_formats};
use ruff_python_ast::identifier::except; 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::Violation;
use crate::checkers::ast::Checker; use crate::checkers::ast::Checker;
@ -65,7 +65,7 @@ pub(crate) fn bare_except(
if type_.is_none() if type_.is_none()
&& !body && !body
.iter() .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())); checker.report_diagnostic(BareExcept, except(handler, checker.locator().contents()));
} }

View File

@ -223,7 +223,7 @@ fn function(
..parameter.clone() ..parameter.clone()
}) })
.collect::<Vec<_>>(); .collect::<Vec<_>>();
let func = Stmt::FunctionDef(ast::StmtFunctionDef { let func = Stmt::FunctionDef(Box::new(ast::StmtFunctionDef {
is_async: false, is_async: false,
name: Identifier::new(name.to_string(), TextRange::default()), name: Identifier::new(name.to_string(), TextRange::default()),
parameters: Box::new(Parameters { parameters: Box::new(Parameters {
@ -237,13 +237,13 @@ fn function(
type_params: None, type_params: None,
range: TextRange::default(), range: TextRange::default(),
node_index: ruff_python_ast::AtomicNodeIndex::NONE, node_index: ruff_python_ast::AtomicNodeIndex::NONE,
}); }));
let generated = checker.generator().stmt(&func); let generated = checker.generator().stmt(&func);
return replace_trailing_ellipsis_with_original_expr(generated, lambda, stmt, checker); 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, is_async: false,
name: Identifier::new(name.to_string(), TextRange::default()), name: Identifier::new(name.to_string(), TextRange::default()),
parameters: Box::new(parameters), parameters: Box::new(parameters),
@ -253,7 +253,7 @@ fn function(
type_params: None, type_params: None,
range: TextRange::default(), range: TextRange::default(),
node_index: ruff_python_ast::AtomicNodeIndex::NONE, node_index: ruff_python_ast::AtomicNodeIndex::NONE,
}); }));
let generated = checker.generator().stmt(&function); let generated = checker.generator().stmt(&function);
replace_trailing_ellipsis_with_original_expr(generated, lambda, stmt, checker) replace_trailing_ellipsis_with_original_expr(generated, lambda, stmt, checker)

View File

@ -942,8 +942,8 @@ impl<'a> Visitor<'a> for BodyVisitor<'a> {
fn visit_stmt(&mut self, stmt: &'a Stmt) { fn visit_stmt(&mut self, stmt: &'a Stmt) {
match stmt { match stmt {
Stmt::Raise(ast::StmtRaise { exc, .. }) => { Stmt::Raise(node) => {
if let Some(exc) = exc.as_ref() { if let Some(exc) = node.exc.as_ref() {
// First try to resolve the exception directly // First try to resolve the exception directly
if let Some(qualified_name) = if let Some(qualified_name) =
self.semantic.resolve_qualified_name(map_callable(exc)) self.semantic.resolve_qualified_name(map_callable(exc))

View File

@ -300,9 +300,9 @@ fn find_dunder_all_exprs<'a>(semantic: &'a SemanticModel) -> Vec<&'a ast::Expr>
_ => None, _ => None,
}?; }?;
match stmt { match stmt {
Stmt::Assign(ast::StmtAssign { value, .. }) => Some(&**value), Stmt::Assign(node) => Some(&*node.value),
Stmt::AnnAssign(ast::StmtAnnAssign { value, .. }) => value.as_deref(), Stmt::AnnAssign(node) => node.value.as_deref(),
Stmt::AugAssign(ast::StmtAugAssign { value, .. }) => Some(&**value), Stmt::AugAssign(node) => Some(&*node.value),
_ => None, _ => None,
} }
}) })

View File

@ -4,7 +4,7 @@ use ruff_macros::{ViolationMetadata, derive_message_formats};
use ruff_python_ast::helpers::contains_effect; use ruff_python_ast::helpers::contains_effect;
use ruff_python_ast::token::parenthesized_range; use ruff_python_ast::token::parenthesized_range;
use ruff_python_ast::token::{TokenKind, Tokens}; 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_python_semantic::Binding;
use ruff_text_size::{Ranged, TextRange, TextSize}; use ruff_text_size::{Ranged, TextRange, TextSize};
@ -161,14 +161,14 @@ fn remove_unused_variable(binding: &Binding, checker: &Checker) -> Option<Fix> {
let isolation = Checker::isolation(checker.semantic().parent_statement_id(node_id)); let isolation = Checker::isolation(checker.semantic().parent_statement_id(node_id));
// First case: simple assignment (`x = 1`) // First case: simple assignment (`x = 1`)
if let Stmt::Assign(ast::StmtAssign { targets, value, .. }) = statement { if let Stmt::Assign(node) = statement {
if let Some(target) = targets if let Some(target) = node.targets
.iter() .iter()
.find(|target| binding.range() == target.range()) .find(|target| binding.range() == target.range())
{ {
if target.is_name_expr() { if target.is_name_expr() {
return if targets.len() > 1 return if node.targets.len() > 1
|| contains_effect(value, |id| checker.semantic().has_builtin_binding(id)) || contains_effect(&node.value, |id| checker.semantic().has_builtin_binding(id))
{ {
// If the expression is complex (`x = foo()`), remove the assignment, // If the expression is complex (`x = foo()`), remove the assignment,
// but preserve the right-hand side. // but preserve the right-hand side.
@ -200,13 +200,9 @@ fn remove_unused_variable(binding: &Binding, checker: &Checker) -> Option<Fix> {
} }
// Second case: simple annotated assignment (`x: int = 1`) // Second case: simple annotated assignment (`x: int = 1`)
if let Stmt::AnnAssign(ast::StmtAnnAssign { if let Stmt::AnnAssign(node) = statement {
target, if let Some(value) = &node.value {
value: Some(value), if node.target.is_name_expr() {
..
}) = statement
{
if target.is_name_expr() {
return if contains_effect(value, |id| checker.semantic().has_builtin_binding(id)) { return if contains_effect(value, |id| checker.semantic().has_builtin_binding(id)) {
// If the expression is complex (`x = foo()`), remove the assignment, // If the expression is complex (`x = foo()`), remove the assignment,
// but preserve the right-hand side. // but preserve the right-hand side.
@ -223,12 +219,13 @@ fn remove_unused_variable(binding: &Binding, checker: &Checker) -> Option<Fix> {
}; };
} }
} }
}
// Third case: with_item (`with foo() as x:`) // 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`. // Find the binding that matches the given `Range`.
// TODO(charlie): Store the `WithItem` in the `Binding`. // 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 let Some(optional_vars) = &item.optional_vars {
if optional_vars.range() == binding.range() { if optional_vars.range() == binding.range() {
// Find the first token before the `as` keyword. // Find the first token before the `as` keyword.

View File

@ -119,22 +119,22 @@ impl Visitor<'_> for SequenceIndexVisitor<'_> {
return; return;
} }
match stmt { match stmt {
Stmt::Assign(ast::StmtAssign { targets, value, .. }) => { Stmt::Assign(node) => {
self.modified = targets.iter().any(|target| self.is_assignment(target)); self.modified = node.targets.iter().any(|target| self.is_assignment(target));
self.visit_expr(value); self.visit_expr(&node.value);
} }
Stmt::AnnAssign(ast::StmtAnnAssign { target, value, .. }) => { Stmt::AnnAssign(node) => {
if let Some(value) = value { if let Some(value) = &node.value {
self.modified = self.is_assignment(target); self.modified = self.is_assignment(&node.target);
self.visit_expr(value); self.visit_expr(value);
} }
} }
Stmt::AugAssign(ast::StmtAugAssign { target, value, .. }) => { Stmt::AugAssign(node) => {
self.modified = self.is_assignment(target); self.modified = self.is_assignment(&node.target);
self.visit_expr(value); self.visit_expr(&node.value);
} }
Stmt::Delete(ast::StmtDelete { targets, .. }) => { Stmt::Delete(node) => {
self.modified = targets.iter().any(|target| self.is_assignment(target)); self.modified = node.targets.iter().any(|target| self.is_assignment(target));
} }
_ => visitor::walk_stmt(self, stmt), _ => visitor::walk_stmt(self, stmt),
} }

View File

@ -64,12 +64,10 @@ impl Violation for CollapsibleElseIf {
/// PLR5501 /// PLR5501
pub(crate) fn collapsible_else_if(checker: &Checker, stmt: &Stmt) { pub(crate) fn collapsible_else_if(checker: &Checker, stmt: &Stmt) {
let Stmt::If(ast::StmtIf { let Stmt::If(if_stmt) = stmt else {
elif_else_clauses, ..
}) = stmt
else {
return; return;
}; };
let elif_else_clauses = &if_stmt.elif_else_clauses;
let Some( let Some(
else_clause @ ElifElseClause { else_clause @ ElifElseClause {
@ -79,7 +77,10 @@ pub(crate) fn collapsible_else_if(checker: &Checker, stmt: &Stmt) {
else { else {
return; return;
}; };
let [first @ Stmt::If(ast::StmtIf { .. })] = body.as_slice() else { let [first] = body.as_slice() else {
return;
};
let Stmt::If(_) = first else {
return; return;
}; };

View File

@ -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_macros::{ViolationMetadata, derive_message_formats};
use ruff_text_size::Ranged; use ruff_text_size::Ranged;
@ -54,28 +54,27 @@ fn traverse_body(checker: &Checker, body: &[Stmt]) {
} }
match stmt { match stmt {
Stmt::If(ast::StmtIf { Stmt::If(if_stmt) => {
body, traverse_body(checker, &if_stmt.body);
elif_else_clauses, for clause in &if_stmt.elif_else_clauses {
..
}) => {
traverse_body(checker, body);
for clause in elif_else_clauses {
traverse_body(checker, &clause.body); traverse_body(checker, &clause.body);
} }
} }
Stmt::Try(ast::StmtTry { body, orelse, .. }) => { Stmt::Try(try_stmt) => {
traverse_body(checker, body); traverse_body(checker, &try_stmt.body);
traverse_body(checker, orelse); traverse_body(checker, &try_stmt.orelse);
} }
Stmt::For(ast::StmtFor { orelse, .. }) | Stmt::While(ast::StmtWhile { orelse, .. }) => { Stmt::For(for_stmt) => {
traverse_body(checker, orelse); traverse_body(checker, &for_stmt.orelse);
} }
Stmt::With(ast::StmtWith { body, .. }) => { Stmt::While(while_stmt) => {
traverse_body(checker, body); traverse_body(checker, &while_stmt.orelse);
} }
Stmt::Match(ast::StmtMatch { cases, .. }) => { Stmt::With(with_stmt) => {
for case in cases { traverse_body(checker, &with_stmt.body);
}
Stmt::Match(match_stmt) => {
for case in &match_stmt.cases {
traverse_body(checker, &case.body); traverse_body(checker, &case.body);
} }
} }

View File

@ -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_macros::{ViolationMetadata, derive_message_formats};
use ruff_python_semantic::analyze::typing::is_dict; 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 { let (value, annotation) = match statement {
Stmt::Assign(assign_stmt) => (assign_stmt.value.as_ref(), None), Stmt::Assign(node) => (node.value.as_ref(), None),
Stmt::AnnAssign(ast::StmtAnnAssign { Stmt::AnnAssign(node) => {
value: Some(value), if let Some(value) = &node.value {
annotation, (value.as_ref(), Some(node.annotation.as_ref()))
.. } else {
}) => (value.as_ref(), Some(annotation.as_ref())), return false;
}
}
_ => return false, _ => return false,
}; };

View File

@ -95,16 +95,11 @@ pub(crate) fn if_stmt_min_max(checker: &Checker, stmt_if: &ast::StmtIf) {
return; return;
} }
let [ let [body @ Stmt::Assign(node)] = body.as_slice() else {
body @ Stmt::Assign(ast::StmtAssign {
targets: body_targets,
value: body_value,
..
}),
] = body.as_slice()
else {
return; return;
}; };
let body_targets = &node.targets;
let body_value = &node.value;
let [body_target] = body_targets.as_slice() else { let [body_target] = body_targets.as_slice() else {
return; return;
}; };

View File

@ -113,7 +113,9 @@ fn get_undecorated_methods(checker: &Checker, class_stmt: &Stmt, method_type: &M
// gather all explicit *method calls // gather all explicit *method calls
for stmt in &class_def.body { 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 { if let Expr::Call(ast::ExprCall {
func, arguments, .. func, arguments, ..
}) = value.as_ref() }) = 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 { for stmt in &class_def.body {
if let Stmt::FunctionDef(ast::StmtFunctionDef { if let Stmt::FunctionDef(node) = stmt {
name, let name = &node.name;
decorator_list, let decorator_list = &node.decorator_list;
..
}) = stmt
{
let Some(decorator_call_statement) = explicit_decorator_calls.get(name.id()) else { let Some(decorator_call_statement) = explicit_decorator_calls.get(name.id()) else {
continue; continue;
}; };

View File

@ -107,13 +107,13 @@ fn is_attributes_not_in_slots(body: &[Stmt]) -> Vec<AttributeAssignment<'_>> {
for statement in body { for statement in body {
match statement { match statement {
// Ex) `__slots__ = ("name",)` // Ex) `__slots__ = ("name",)`
Stmt::Assign(ast::StmtAssign { targets, value, .. }) => { Stmt::Assign(node) => {
let [Expr::Name(ast::ExprName { id, .. })] = targets.as_slice() else { let [Expr::Name(ast::ExprName { id, .. })] = node.targets.as_slice() else {
continue; continue;
}; };
if id == "__slots__" { if id == "__slots__" {
for attribute in slots_attributes(value) { for attribute in slots_attributes(&node.value) {
if let Some(attribute) = attribute { if let Some(attribute) = attribute {
slots.insert(attribute); slots.insert(attribute);
} else { } else {
@ -124,12 +124,12 @@ fn is_attributes_not_in_slots(body: &[Stmt]) -> Vec<AttributeAssignment<'_>> {
} }
// Ex) `__slots__: Tuple[str, ...] = ("name",)` // Ex) `__slots__: Tuple[str, ...] = ("name",)`
Stmt::AnnAssign(ast::StmtAnnAssign { Stmt::AnnAssign(node) => {
target, let Some(value) = &node.value else {
value: Some(value), continue;
.. };
}) => {
let Expr::Name(ast::ExprName { id, .. }) = target.as_ref() else { let Expr::Name(ast::ExprName { id, .. }) = node.target.as_ref() else {
continue; continue;
}; };
@ -145,13 +145,13 @@ fn is_attributes_not_in_slots(body: &[Stmt]) -> Vec<AttributeAssignment<'_>> {
} }
// Ex) `__slots__ += ("name",)` // Ex) `__slots__ += ("name",)`
Stmt::AugAssign(ast::StmtAugAssign { target, value, .. }) => { Stmt::AugAssign(node) => {
let Expr::Name(ast::ExprName { id, .. }) = target.as_ref() else { let Expr::Name(ast::ExprName { id, .. }) = node.target.as_ref() else {
continue; continue;
}; };
if id == "__slots__" { if id == "__slots__" {
for attribute in slots_attributes(value) { for attribute in slots_attributes(&node.value) {
if let Some(attribute) = attribute { if let Some(attribute) = attribute {
slots.insert(attribute); slots.insert(attribute);
} else { } else {
@ -170,11 +170,11 @@ fn is_attributes_not_in_slots(body: &[Stmt]) -> Vec<AttributeAssignment<'_>> {
// And, collect all the property name with setter. // And, collect all the property name with setter.
for statement in body { for statement in body {
let Stmt::FunctionDef(ast::StmtFunctionDef { decorator_list, .. }) = statement else { let Stmt::FunctionDef(node) = statement else {
continue; continue;
}; };
for decorator in decorator_list { for decorator in &node.decorator_list {
let Some(ast::ExprAttribute { value, attr, .. }) = let Some(ast::ExprAttribute { value, attr, .. }) =
decorator.expression.as_attribute_expr() decorator.expression.as_attribute_expr()
else { else {
@ -193,16 +193,16 @@ fn is_attributes_not_in_slots(body: &[Stmt]) -> Vec<AttributeAssignment<'_>> {
// Second, find any assignments that aren't included in `__slots__`. // Second, find any assignments that aren't included in `__slots__`.
let mut assignments = vec![]; let mut assignments = vec![];
for statement in body { for statement in body {
let Stmt::FunctionDef(ast::StmtFunctionDef { name, body, .. }) = statement else { let Stmt::FunctionDef(node) = statement else {
continue; continue;
}; };
if name == "__init__" { if node.name.as_str() == "__init__" {
for statement in body { for statement in &node.body {
match statement { match statement {
// Ex) `self.name = name` // Ex) `self.name = name`
Stmt::Assign(ast::StmtAssign { targets, .. }) => { Stmt::Assign(assign_node) => {
let [Expr::Attribute(attribute)] = targets.as_slice() else { let [Expr::Attribute(attribute)] = assign_node.targets.as_slice() else {
continue; continue;
}; };
let Expr::Name(ast::ExprName { id, .. }) = attribute.value.as_ref() else { let Expr::Name(ast::ExprName { id, .. }) = attribute.value.as_ref() else {
@ -217,8 +217,8 @@ fn is_attributes_not_in_slots(body: &[Stmt]) -> Vec<AttributeAssignment<'_>> {
} }
// Ex) `self.name: str = name` // Ex) `self.name: str = name`
Stmt::AnnAssign(ast::StmtAnnAssign { target, .. }) => { Stmt::AnnAssign(ann_node) => {
let Expr::Attribute(attribute) = target.as_ref() else { let Expr::Attribute(attribute) = ann_node.target.as_ref() else {
continue; continue;
}; };
let Expr::Name(ast::ExprName { id, .. }) = attribute.value.as_ref() else { let Expr::Name(ast::ExprName { id, .. }) = attribute.value.as_ref() else {
@ -233,8 +233,8 @@ fn is_attributes_not_in_slots(body: &[Stmt]) -> Vec<AttributeAssignment<'_>> {
} }
// Ex) `self.name += name` // Ex) `self.name += name`
Stmt::AugAssign(ast::StmtAugAssign { target, .. }) => { Stmt::AugAssign(aug_node) => {
let Expr::Attribute(attribute) = target.as_ref() else { let Expr::Attribute(attribute) = aug_node.target.as_ref() else {
continue; continue;
}; };
let Expr::Name(ast::ExprName { id, .. }) = attribute.value.as_ref() else { let Expr::Name(ast::ExprName { id, .. }) = attribute.value.as_ref() else {

View File

@ -149,9 +149,9 @@ impl<'b> StatementVisitor<'b> for InnerForWithAssignTargetsVisitor<'_, 'b> {
fn visit_stmt(&mut self, stmt: &'b Stmt) { fn visit_stmt(&mut self, stmt: &'b Stmt) {
// Collect target expressions. // Collect target expressions.
match stmt { match stmt {
Stmt::For(ast::StmtFor { target, .. }) => { Stmt::For(node) => {
self.assignment_targets.extend( 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 { ExprWithInnerBindingKind {
expr, expr,
binding_kind: InnerBindingKind::For, 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( 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| ExprWithInnerBindingKind {
expr, expr,
binding_kind: InnerBindingKind::With, 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 // Check for single-target assignments which are of the
// form `x = cast(..., x)`. // form `x = cast(..., x)`.
if targets if node
.targets
.first() .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; return;
} }
self.assignment_targets.extend( 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| ExprWithInnerBindingKind {
expr, expr,
binding_kind: InnerBindingKind::Assignment, 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( 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 { ExprWithInnerBindingKind {
expr, expr,
binding_kind: InnerBindingKind::Assignment, binding_kind: InnerBindingKind::Assignment,
@ -197,12 +198,12 @@ impl<'b> StatementVisitor<'b> for InnerForWithAssignTargetsVisitor<'_, 'b> {
}), }),
); );
} }
Stmt::AnnAssign(ast::StmtAnnAssign { target, value, .. }) => { Stmt::AnnAssign(node) => {
if value.is_none() { if node.value.is_none() {
return; return;
} }
self.assignment_targets.extend( 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 { ExprWithInnerBindingKind {
expr, expr,
binding_kind: InnerBindingKind::Assignment, binding_kind: InnerBindingKind::Assignment,
@ -345,9 +346,9 @@ fn assignment_targets_from_assign_targets<'a>(
/// PLW2901 /// PLW2901
pub(crate) fn redefined_loop_name(checker: &Checker, stmt: &Stmt) { pub(crate) fn redefined_loop_name(checker: &Checker, stmt: &Stmt) {
let (outer_assignment_targets, inner_assignment_targets) = match stmt { let (outer_assignment_targets, inner_assignment_targets) = match stmt {
Stmt::With(ast::StmtWith { items, body, .. }) => { Stmt::With(node) => {
let outer_assignment_targets: Vec<ExprWithOuterBindingKind> = let outer_assignment_targets: Vec<ExprWithOuterBindingKind> =
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 { .map(|expr| ExprWithOuterBindingKind {
expr, expr,
binding_kind: OuterBindingKind::With, 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, dummy_variable_rgx: &checker.settings().dummy_variable_rgx,
assignment_targets: vec![], assignment_targets: vec![],
}; };
for stmt in body { for stmt in &node.body {
visitor.visit_stmt(stmt); visitor.visit_stmt(stmt);
} }
(outer_assignment_targets, visitor.assignment_targets) (outer_assignment_targets, visitor.assignment_targets)
} }
Stmt::For(ast::StmtFor { target, body, .. }) => { Stmt::For(node) => {
let outer_assignment_targets: Vec<ExprWithOuterBindingKind> = let outer_assignment_targets: Vec<ExprWithOuterBindingKind> =
assignment_targets_from_expr(target, &checker.settings().dummy_variable_rgx) assignment_targets_from_expr(&node.target, &checker.settings().dummy_variable_rgx)
.map(|expr| ExprWithOuterBindingKind { .map(|expr| ExprWithOuterBindingKind {
expr, expr,
binding_kind: OuterBindingKind::For, 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, dummy_variable_rgx: &checker.settings().dummy_variable_rgx,
assignment_targets: vec![], assignment_targets: vec![],
}; };
for stmt in body { for stmt in &node.body {
visitor.visit_stmt(stmt); visitor.visit_stmt(stmt);
} }
(outer_assignment_targets, visitor.assignment_targets) (outer_assignment_targets, visitor.assignment_targets)

View File

@ -121,23 +121,23 @@ fn slots_members(body: &[Stmt]) -> FxHashSet<Slot<'_>> {
for stmt in body { for stmt in body {
match stmt { match stmt {
// Ex) `__slots__ = ("name",)` // Ex) `__slots__ = ("name",)`
Stmt::Assign(ast::StmtAssign { targets, value, .. }) => { Stmt::Assign(node) => {
let [Expr::Name(ast::ExprName { id, .. })] = targets.as_slice() else { let [Expr::Name(ast::ExprName { id, .. })] = node.targets.as_slice() else {
continue; continue;
}; };
if id == "__slots__" { if id == "__slots__" {
members.extend(slots_attributes(value)); members.extend(slots_attributes(&node.value));
} }
} }
// Ex) `__slots__: Tuple[str, ...] = ("name",)` // Ex) `__slots__: Tuple[str, ...] = ("name",)`
Stmt::AnnAssign(ast::StmtAnnAssign { Stmt::AnnAssign(node) => {
target, let Some(value) = &node.value else {
value: Some(value), continue;
.. };
}) => {
let Expr::Name(ast::ExprName { id, .. }) = target.as_ref() else { let Expr::Name(ast::ExprName { id, .. }) = node.target.as_ref() else {
continue; continue;
}; };
@ -147,13 +147,13 @@ fn slots_members(body: &[Stmt]) -> FxHashSet<Slot<'_>> {
} }
// Ex) `__slots__ += ("name",)` // Ex) `__slots__ += ("name",)`
Stmt::AugAssign(ast::StmtAugAssign { target, value, .. }) => { Stmt::AugAssign(node) => {
let Expr::Name(ast::ExprName { id, .. }) = target.as_ref() else { let Expr::Name(ast::ExprName { id, .. }) = node.target.as_ref() else {
continue; continue;
}; };
if id == "__slots__" { if id == "__slots__" {
members.extend(slots_attributes(value)); members.extend(slots_attributes(&node.value));
} }
} }
_ => {} _ => {}

View File

@ -62,23 +62,20 @@ impl Violation for SingleStringSlots {
pub(crate) fn single_string_slots(checker: &Checker, class: &StmtClassDef) { pub(crate) fn single_string_slots(checker: &Checker, class: &StmtClassDef) {
for stmt in &class.body { for stmt in &class.body {
match stmt { match stmt {
Stmt::Assign(ast::StmtAssign { targets, value, .. }) => { Stmt::Assign(assign) => {
for target in targets { for target in &assign.targets {
if let Expr::Name(ast::ExprName { id, .. }) = target { if let Expr::Name(ast::ExprName { id, .. }) = target {
if id.as_str() == "__slots__" { if id.as_str() == "__slots__" {
if matches!(value.as_ref(), Expr::StringLiteral(_) | Expr::FString(_)) { if matches!(assign.value.as_ref(), Expr::StringLiteral(_) | Expr::FString(_)) {
checker.report_diagnostic(SingleStringSlots, stmt.identifier()); checker.report_diagnostic(SingleStringSlots, stmt.identifier());
} }
} }
} }
} }
} }
Stmt::AnnAssign(ast::StmtAnnAssign { Stmt::AnnAssign(ann_assign) => {
target, if let Some(value) = &ann_assign.value {
value: Some(value), if let Expr::Name(ast::ExprName { id, .. }) = ann_assign.target.as_ref() {
..
}) => {
if let Expr::Name(ast::ExprName { id, .. }) = target.as_ref() {
if id.as_str() == "__slots__" { if id.as_str() == "__slots__" {
if matches!(value.as_ref(), Expr::StringLiteral(_) | Expr::FString(_)) { if matches!(value.as_ref(), Expr::StringLiteral(_) | Expr::FString(_)) {
checker.report_diagnostic(SingleStringSlots, stmt.identifier()); checker.report_diagnostic(SingleStringSlots, stmt.identifier());
@ -86,6 +83,7 @@ pub(crate) fn single_string_slots(checker: &Checker, class: &StmtClassDef) {
} }
} }
} }
}
_ => {} _ => {}
} }
} }

View File

@ -79,7 +79,8 @@ impl<'a> Visitor<'a> for GeneratorAnalyzer<'a, '_> {
fn visit_stmt(&mut self, stmt: &'a ast::Stmt) { fn visit_stmt(&mut self, stmt: &'a ast::Stmt) {
match stmt { match stmt {
ast::Stmt::FunctionDef(_) => {} ast::Stmt::FunctionDef(_) => {}
ast::Stmt::Raise(raise @ ast::StmtRaise { exc: Some(exc), .. }) => { ast::Stmt::Raise(raise) => {
if let Some(exc) = &raise.exc {
if self if self
.checker .checker
.semantic() .semantic()
@ -87,6 +88,7 @@ impl<'a> Visitor<'a> for GeneratorAnalyzer<'a, '_> {
{ {
self.stop_iteration_raises.push(raise); self.stop_iteration_raises.push(raise);
} }
}
walk_stmt(self, stmt); walk_stmt(self, stmt);
} }
_ => walk_stmt(self, stmt), _ => walk_stmt(self, stmt),

View File

@ -166,57 +166,54 @@ fn num_branches(stmts: &[Stmt]) -> usize {
stmts stmts
.iter() .iter()
.map(|stmt| match stmt { .map(|stmt| match stmt {
Stmt::If(ast::StmtIf { Stmt::If(node) => {
body, 1 + num_branches(&node.body)
elif_else_clauses, + node.elif_else_clauses.len()
.. + node.elif_else_clauses
}) => {
1 + num_branches(body)
+ elif_else_clauses.len()
+ elif_else_clauses
.iter() .iter()
.map(|clause| num_branches(&clause.body)) .map(|clause| num_branches(&clause.body))
.sum::<usize>() .sum::<usize>()
} }
Stmt::Match(ast::StmtMatch { cases, .. }) => { Stmt::Match(node) => {
cases.len() node.cases.len()
+ cases + node.cases
.iter() .iter()
.map(|case| num_branches(&case.body)) .map(|case| num_branches(&case.body))
.sum::<usize>() .sum::<usize>()
} }
// The `with` statement is not considered a branch but the statements inside the `with` should be counted. // 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::With(node) => num_branches(&node.body),
Stmt::For(ast::StmtFor { body, orelse, .. }) Stmt::For(node) => {
| Stmt::While(ast::StmtWhile { body, orelse, .. }) => { 1 + num_branches(&node.body)
1 + num_branches(body) + (if node.orelse.is_empty() {
+ (if orelse.is_empty() {
0 0
} else { } else {
1 + num_branches(orelse) 1 + num_branches(&node.orelse)
}) })
} }
Stmt::Try(ast::StmtTry { Stmt::While(node) => {
body, 1 + num_branches(&node.body)
handlers, + (if node.orelse.is_empty() {
orelse, 0
finalbody, } else {
.. 1 + num_branches(&node.orelse)
}) => { })
}
Stmt::Try(node) => {
// Count each `except` clause as a branch; the `else` and `finally` clauses also // Count each `except` clause as a branch; the `else` and `finally` clauses also
// count, but the `try` clause itself does not. // count, but the `try` clause itself does not.
num_branches(body) num_branches(&node.body)
+ (if orelse.is_empty() { + (if node.orelse.is_empty() {
0 0
} else { } else {
1 + num_branches(orelse) 1 + num_branches(&node.orelse)
}) })
+ (if finalbody.is_empty() { + (if node.finalbody.is_empty() {
0 0
} else { } else {
1 + num_branches(finalbody) 1 + num_branches(&node.finalbody)
}) })
+ handlers + node.handlers
.iter() .iter()
.map(|handler| { .map(|handler| {
1 + { 1 + {

View File

@ -95,39 +95,29 @@ fn is_nested_block(stmt: &Stmt) -> bool {
/// Returns `true` if the given statement is a leaf node. /// Returns `true` if the given statement is a leaf node.
fn has_nested_block(stmt: &Stmt) -> bool { fn has_nested_block(stmt: &Stmt) -> bool {
match stmt { match stmt {
Stmt::If(ast::StmtIf { Stmt::If(node) => {
body, node.body.iter().any(is_nested_block)
elif_else_clauses, || node.elif_else_clauses
..
}) => {
body.iter().any(is_nested_block)
|| elif_else_clauses
.iter() .iter()
.any(|elif_else| elif_else.body.iter().any(is_nested_block)) .any(|elif_else| elif_else.body.iter().any(is_nested_block))
} }
Stmt::While(ast::StmtWhile { body, orelse, .. }) => { Stmt::While(node) => {
body.iter().any(is_nested_block) || orelse.iter().any(is_nested_block) node.body.iter().any(is_nested_block) || node.orelse.iter().any(is_nested_block)
} }
Stmt::For(ast::StmtFor { body, orelse, .. }) => { Stmt::For(node) => {
body.iter().any(is_nested_block) || orelse.iter().any(is_nested_block) node.body.iter().any(is_nested_block) || node.orelse.iter().any(is_nested_block)
} }
Stmt::Try(ast::StmtTry { Stmt::Try(node) => {
body, node.body.iter().any(is_nested_block)
handlers, || node.handlers.iter().any(|handler| match handler {
orelse,
finalbody,
..
}) => {
body.iter().any(is_nested_block)
|| handlers.iter().any(|handler| match handler {
ExceptHandler::ExceptHandler(ast::ExceptHandlerExceptHandler { ExceptHandler::ExceptHandler(ast::ExceptHandlerExceptHandler {
body, .. body, ..
}) => body.iter().any(is_nested_block), }) => body.iter().any(is_nested_block),
}) })
|| orelse.iter().any(is_nested_block) || node.orelse.iter().any(is_nested_block)
|| finalbody.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, _ => false,
} }
} }

View File

@ -69,54 +69,44 @@ fn num_statements(stmts: &[Stmt]) -> usize {
let mut count = 0; let mut count = 0;
for stmt in stmts { for stmt in stmts {
match stmt { match stmt {
Stmt::If(ast::StmtIf { Stmt::If(node) => {
body,
elif_else_clauses,
..
}) => {
count += 1; count += 1;
count += num_statements(body); count += num_statements(&node.body);
for clause in elif_else_clauses { for clause in &node.elif_else_clauses {
count += 1; count += 1;
count += num_statements(&clause.body); count += num_statements(&clause.body);
} }
} }
Stmt::For(ast::StmtFor { body, orelse, .. }) => { Stmt::For(node) => {
count += num_statements(body); count += num_statements(&node.body);
count += num_statements(orelse); count += num_statements(&node.orelse);
} }
Stmt::While(ast::StmtWhile { body, orelse, .. }) => { Stmt::While(node) => {
count += 1; count += 1;
count += num_statements(body); count += num_statements(&node.body);
count += num_statements(orelse); count += num_statements(&node.orelse);
} }
Stmt::Match(ast::StmtMatch { cases, .. }) => { Stmt::Match(node) => {
count += 1; count += 1;
for case in cases { for case in &node.cases {
count += 1; count += 1;
count += num_statements(&case.body); count += num_statements(&case.body);
} }
} }
Stmt::Try(ast::StmtTry { Stmt::Try(node) => {
body,
handlers,
orelse,
finalbody,
..
}) => {
count += 1; count += 1;
count += num_statements(body); count += num_statements(&node.body);
if !orelse.is_empty() { if !node.orelse.is_empty() {
count += 1 + num_statements(orelse); count += 1 + num_statements(&node.orelse);
} }
if !finalbody.is_empty() { if !node.finalbody.is_empty() {
// Unclear why, but follow Pylint's convention. // 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; count += 1;
} }
for handler in handlers { for handler in &node.handlers {
count += 1; count += 1;
let ExceptHandler::ExceptHandler(ast::ExceptHandlerExceptHandler { let ExceptHandler::ExceptHandler(ast::ExceptHandlerExceptHandler {
body, .. body, ..
@ -124,10 +114,13 @@ fn num_statements(stmts: &[Stmt]) -> usize {
count += num_statements(body); count += num_statements(body);
} }
} }
Stmt::FunctionDef(ast::StmtFunctionDef { body, .. }) Stmt::FunctionDef(node) => {
| Stmt::With(ast::StmtWith { body, .. }) => {
count += 1; count += 1;
count += num_statements(body); count += num_statements(&node.body);
}
Stmt::With(node) => {
count += 1;
count += num_statements(&node.body);
} }
Stmt::Return(_) => {} Stmt::Return(_) => {}
_ => { _ => {

View File

@ -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. /// Returns `true` if the given body contains a `break` statement.
fn loop_exits_early(body: &[Stmt]) -> bool { fn loop_exits_early(body: &[Stmt]) -> bool {
body.iter().any(|stmt| match stmt { body.iter().any(|stmt| match stmt {
Stmt::If(ast::StmtIf { Stmt::If(node) => {
body, loop_exits_early(&node.body)
elif_else_clauses, || node.elif_else_clauses
..
}) => {
loop_exits_early(body)
|| elif_else_clauses
.iter() .iter()
.any(|clause| loop_exits_early(&clause.body)) .any(|clause| loop_exits_early(&clause.body))
} }
Stmt::With(ast::StmtWith { body, .. }) => loop_exits_early(body), Stmt::With(node) => loop_exits_early(&node.body),
Stmt::Match(ast::StmtMatch { cases, .. }) => cases Stmt::Match(node) => node.cases
.iter() .iter()
.any(|MatchCase { body, .. }| loop_exits_early(body)), .any(|MatchCase { body, .. }| loop_exits_early(body)),
Stmt::Try(ast::StmtTry { Stmt::Try(node) => {
body, loop_exits_early(&node.body)
handlers, || loop_exits_early(&node.orelse)
orelse, || loop_exits_early(&node.finalbody)
finalbody, || node.handlers.iter().any(|handler| match handler {
..
}) => {
loop_exits_early(body)
|| loop_exits_early(orelse)
|| loop_exits_early(finalbody)
|| handlers.iter().any(|handler| match handler {
ExceptHandler::ExceptHandler(ast::ExceptHandlerExceptHandler { ExceptHandler::ExceptHandler(ast::ExceptHandlerExceptHandler {
body, .. body, ..
}) => loop_exits_early(body), }) => loop_exits_early(body),
}) })
} }
Stmt::For(ast::StmtFor { orelse, .. }) | Stmt::While(ast::StmtWhile { orelse, .. }) => { Stmt::For(node) => loop_exits_early(&node.orelse),
loop_exits_early(orelse) Stmt::While(node) => loop_exits_early(&node.orelse),
}
Stmt::Break(_) => true, Stmt::Break(_) => true,
_ => false, _ => false,
}) })

View File

@ -55,11 +55,12 @@ where
/// UP023 /// UP023
pub(crate) fn deprecated_c_element_tree(checker: &Checker, stmt: &Stmt) { pub(crate) fn deprecated_c_element_tree(checker: &Checker, stmt: &Stmt) {
match stmt { match stmt {
Stmt::Import(ast::StmtImport { Stmt::Import(import_stmt) => {
let ast::StmtImport {
names, names,
range: _, range: _,
node_index: _, node_index: _,
}) => { } = &**import_stmt;
// Ex) `import xml.etree.cElementTree as ET` // Ex) `import xml.etree.cElementTree as ET`
for name in names { for name in names {
if &name.name == "xml.etree.cElementTree" && name.asname.is_some() { 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 { Stmt::ImportFrom(import_from_stmt) => {
let ast::StmtImportFrom {
module, module,
names, names,
level, level,
range: _, range: _,
node_index: _, node_index: _,
}) => { } = &**import_from_stmt;
if *level > 0 { if *level > 0 {
// Ex) `import .xml.etree.cElementTree as ET` // Ex) `import .xml.etree.cElementTree as ET`
} else if let Some(module) = module { } else if let Some(module) = module {

View File

@ -277,11 +277,12 @@ pub(crate) fn deprecated_mock_attribute(checker: &Checker, attribute: &ast::Expr
/// UP026 /// UP026
pub(crate) fn deprecated_mock_import(checker: &Checker, stmt: &Stmt) { pub(crate) fn deprecated_mock_import(checker: &Checker, stmt: &Stmt) {
match stmt { match stmt {
Stmt::Import(ast::StmtImport { Stmt::Import(node) => {
let ast::StmtImport {
names, names,
range: _, range: _,
node_index: _, node_index: _,
}) => { } = &**node;
// Find all `mock` imports. // Find all `mock` imports.
if names if names
.iter() .iter()
@ -326,12 +327,16 @@ pub(crate) fn deprecated_mock_import(checker: &Checker, stmt: &Stmt) {
} }
} }
} }
Stmt::ImportFrom(ast::StmtImportFrom { Stmt::ImportFrom(node) => {
let ast::StmtImportFrom {
module: Some(module), module: Some(module),
level, level,
names, names,
.. range: _,
}) => { node_index: _,
} = &**node else {
return;
};
if *level > 0 { if *level > 0 {
return; return;
} }

View File

@ -97,15 +97,13 @@ pub(crate) fn super_call_with_parameters(checker: &Checker, call: &ast::ExprCall
}; };
// Find the enclosing function definition (if any). // Find the enclosing function definition (if any).
let Some( let Some(func_stmt) = parents.find(|stmt| stmt.is_function_def_stmt()) else {
func_stmt @ Stmt::FunctionDef(ast::StmtFunctionDef {
parameters: parent_parameters,
..
}),
) = parents.find(|stmt| stmt.is_function_def_stmt())
else {
return; return;
}; };
let Stmt::FunctionDef(func_def) = func_stmt else {
return;
};
let parent_parameters = &func_def.parameters;
if is_builtins_super(checker.semantic(), call) if is_builtins_super(checker.semantic(), call)
&& !has_local_dunder_class_var_ref(checker.semantic(), func_stmt) && !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). // Find the enclosing class definition (if any).
let Some(Stmt::ClassDef(ast::StmtClassDef { let Some(class_stmt) = parents.find(|stmt| stmt.is_class_def_stmt()) else {
name: parent_name,
decorator_list,
..
})) = parents.find(|stmt| stmt.is_class_def_stmt())
else {
return; return;
}; };
let Stmt::ClassDef(class_def) = class_stmt else {
return;
};
let parent_name = &class_def.name;
let decorator_list = &class_def.decorator_list;
let ( let (
Expr::Name(ast::ExprName { Expr::Name(ast::ExprName {
@ -252,15 +250,13 @@ impl ClassCellReferenceFinder {
impl<'a> Visitor<'a> for ClassCellReferenceFinder { impl<'a> Visitor<'a> for ClassCellReferenceFinder {
fn visit_stmt(&mut self, stmt: &'a Stmt) { fn visit_stmt(&mut self, stmt: &'a Stmt) {
match stmt { if stmt.is_class_def_stmt() {
Stmt::ClassDef(_) => {} return;
_ => { }
if !self.has_class_cell { if !self.has_class_cell {
walk_stmt(self, stmt); walk_stmt(self, stmt);
} }
} }
}
}
fn visit_expr(&mut self, expr: &'a Expr) { fn visit_expr(&mut self, expr: &'a Expr) {
if expr.as_name_expr().is_some_and(|name| { if expr.as_name_expr().is_some_and(|name| {

View File

@ -140,7 +140,8 @@ pub(crate) fn unnecessary_future_import(checker: &Checker, scope: &Scope) {
}; };
let stmt = checker.semantic().statement(node_id); 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 let Some(alias) = names
.iter() .iter()
.find(|alias| alias.name.as_str() == binding.name(checker.source())) .find(|alias| alias.name.as_str() == binding.name(checker.source()))

View File

@ -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 // 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. // would remove the rest of the expression.
let replacement = match with_stmt.body.as_slice() { let replacement = match with_stmt.body.as_slice() {
[Stmt::Assign(ast::StmtAssign { targets, value, .. })] if value.range() == expr.range() => { [Stmt::Assign(assign_node)] if assign_node.value.range() == expr.range() => {
match targets.as_slice() { match assign_node.targets.as_slice() {
[Expr::Name(name)] => { [Expr::Name(name)] => {
format!( format!(
"{name} = {binding}({filename_code}).{suggestion}", "{name} = {binding}({filename_code}).{suggestion}",
@ -214,23 +214,18 @@ fn generate_fix(
_ => return None, _ => return None,
} }
} }
[ [Stmt::AnnAssign(ann_assign_node)] if ann_assign_node.value.as_ref().is_some_and(|v| v.range() == expr.range()) => {
Stmt::AnnAssign(ast::StmtAnnAssign { match ann_assign_node.target.as_ref() {
target,
annotation,
value: Some(value),
..
}),
] if value.range() == expr.range() => match target.as_ref() {
Expr::Name(name) => { Expr::Name(name) => {
format!( format!(
"{var}: {ann} = {binding}({filename_code}).{suggestion}", "{var}: {ann} = {binding}({filename_code}).{suggestion}",
var = name.id, var = name.id,
ann = locator.slice(annotation.range()) ann = locator.slice(ann_assign_node.annotation.range())
) )
} }
_ => return None, _ => return None,
}, }
}
_ => return None, _ => return None,
}; };

View File

@ -100,7 +100,7 @@ pub(crate) fn reimplemented_operator(checker: &Checker, target: &FunctionLike) {
|| checker || checker
.semantic() .semantic()
.current_statements() .current_statements()
.any(|stmt| matches!(stmt, Stmt::AnnAssign(_) | Stmt::Assign(_)))) .any(|stmt| stmt.is_ann_assign_stmt() || stmt.is_assign_stmt()))
{ {
return; return;
} }
@ -179,7 +179,7 @@ impl FunctionLike<'_> {
match self { match self {
Self::Lambda(expr) => Some(&expr.body), Self::Lambda(expr) => Some(&expr.body),
Self::Function(stmt) => match stmt.body.as_slice() { 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, _ => None,
}, },
} }

View File

@ -65,12 +65,12 @@ pub(crate) fn assert_with_print_message(checker: &Checker, stmt: &ast::StmtAsser
// This is the confirmed rule condition // This is the confirmed rule condition
let mut diagnostic = checker.report_diagnostic(AssertWithPrintMessage, call.range()); let mut diagnostic = checker.report_diagnostic(AssertWithPrintMessage, call.range());
diagnostic.set_fix(Fix::unsafe_edit(Edit::range_replacement( 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(), test: stmt.test.clone(),
msg: print_arguments::to_expr(&call.arguments, checker).map(Box::new), msg: print_arguments::to_expr(&call.arguments, checker).map(Box::new),
range: TextRange::default(), range: TextRange::default(),
node_index: ruff_python_ast::AtomicNodeIndex::NONE, node_index: ruff_python_ast::AtomicNodeIndex::NONE,
})), }))),
// We have to replace the entire statement, // We have to replace the entire statement,
// as the `print` could be empty and thus `call.range()` // as the `print` could be empty and thus `call.range()`
// will cease to exist. // will cease to exist.

View File

@ -153,12 +153,14 @@ pub(crate) fn asyncio_dangling_binding(scope: &Scope, checker: &Checker) {
}; };
match semantic.statement(source) { match semantic.statement(source) {
Stmt::Assign(ast::StmtAssign { value, targets, .. }) if targets.len() == 1 => { 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); asyncio_dangling_task(checker, value, semantic);
} }
Stmt::AnnAssign(ast::StmtAnnAssign { }
value: Some(value), ..
}) => asyncio_dangling_task(checker, value, semantic),
_ => {} _ => {}
} }
} }

View File

@ -121,17 +121,16 @@ pub(crate) fn function_call_in_dataclass_default(
.collect(); .collect();
for statement in &class_def.body { for statement in &class_def.body {
let Stmt::AnnAssign(ast::StmtAnnAssign { let Stmt::AnnAssign(node) = statement else {
annotation, continue;
value: Some(expr), };
.. let Some(expr) = &node.value else {
}) = statement
else {
continue; continue;
}; };
let Expr::Call(ast::ExprCall { func, .. }) = expr.as_ref() else { let Expr::Call(ast::ExprCall { func, .. }) = expr.as_ref() else {
continue; continue;
}; };
let annotation = &node.annotation;
let is_field = is_dataclass_field(func, checker.semantic(), dataclass_kind); 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) .lookup_attribute_in_scope(func, scope_id)
.is_some_and(|id| { .is_some_and(|id| {
let binding = &semantic.binding(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; return false;
}; };

View File

@ -1,6 +1,6 @@
use ruff_macros::{ViolationMetadata, derive_message_formats}; use ruff_macros::{ViolationMetadata, derive_message_formats};
use ruff_python_ast::helpers::is_dunder; 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 ruff_text_size::Ranged;
use crate::Violation; 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 { for statement in &class_def.body {
let Stmt::Assign(StmtAssign { targets, .. }) = statement else { let Stmt::Assign(assign_stmt) = statement else {
continue; continue;
}; };
let targets = &assign_stmt.targets;
if targets.len() > 1 { if targets.len() > 1 {
continue; continue;
} }

View File

@ -139,7 +139,7 @@ pub(crate) fn legacy_raises_warns_deprecated_call(checker: &Checker, call: &ast:
&& !has_trailing_content(stmt.end(), checker.source()) && !has_trailing_content(stmt.end(), checker.source())
{ {
if let Some(with_stmt) = try_fix_legacy_call(context_type, stmt, semantic) { 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 first_line = checker.locator().line_str(stmt.start());
let indentation = leading_indentation(first_line); let indentation = leading_indentation(first_line);
let mut indented = String::new(); let mut indented = String::new();
@ -196,19 +196,19 @@ fn try_fix_legacy_call(
None None
} }
} }
Stmt::Assign(ast::StmtAssign { targets, value, .. }) => { Stmt::Assign(node) => {
let call = value.as_call_expr().filter(|call| { let call = node.value.as_call_expr().filter(|call| {
PytestContextType::from_expr_name(&call.func, semantic) == Some(context_type) PytestContextType::from_expr_name(&call.func, semantic) == Some(context_type)
})?; })?;
let (optional_vars, assign_targets) = match context_type { let (optional_vars, assign_targets) = match context_type {
PytestContextType::Raises => { PytestContextType::Raises => {
let [target] = targets.as_slice() else { let [target] = node.targets.as_slice() else {
return None; return None;
}; };
(Some(target), None) (Some(target), None)
} }
PytestContextType::Warns | PytestContextType::DeprecatedCall => { 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 { let body = if let Some(assign_targets) = assign_targets {
Stmt::Assign(ast::StmtAssign { Stmt::Assign(Box::new(ast::StmtAssign {
node_index: AtomicNodeIndex::NONE, node_index: AtomicNodeIndex::NONE,
range: TextRange::default(), range: TextRange::default(),
targets: assign_targets.to_vec(), targets: assign_targets.to_vec(),
value: Box::new(func_call.into()), value: Box::new(func_call.into()),
}) }))
} else { } else {
Stmt::Expr(StmtExpr { Stmt::Expr(StmtExpr {
node_index: AtomicNodeIndex::NONE, node_index: AtomicNodeIndex::NONE,

View File

@ -102,23 +102,19 @@ pub(crate) fn mutable_class_default(checker: &Checker, class_def: &ast::StmtClas
for statement in &class_def.body { for statement in &class_def.body {
match statement { match statement {
Stmt::AnnAssign(ast::StmtAnnAssign { Stmt::AnnAssign(node) => {
annotation, if let ast::Expr::Name(ast::ExprName { id, .. }) = node.target.as_ref() {
target, if is_class_var_annotation(&node.annotation, checker.semantic()) {
value: Some(value),
..
}) => {
if let ast::Expr::Name(ast::ExprName { id, .. }) = target.as_ref() {
if is_class_var_annotation(annotation, checker.semantic()) {
class_var_targets.insert(id); class_var_targets.insert(id);
} }
} }
if !is_special_attribute(target) if let Some(value) = &node.value {
if !is_special_attribute(&node.target)
&& is_mutable_expr(value, checker.semantic()) && is_mutable_expr(value, checker.semantic())
&& !is_class_var_annotation(annotation, checker.semantic()) && !is_class_var_annotation(&node.annotation, checker.semantic())
&& !is_final_annotation(annotation, checker.semantic()) && !is_final_annotation(&node.annotation, checker.semantic())
&& !is_immutable_annotation(annotation, checker.semantic(), &[]) && !is_immutable_annotation(&node.annotation, checker.semantic(), &[])
{ {
if dataclass_kind(class_def, checker.semantic()).is_some() { if dataclass_kind(class_def, checker.semantic()).is_some() {
continue; continue;
@ -132,20 +128,21 @@ pub(crate) fn mutable_class_default(checker: &Checker, class_def: &ast::StmtClas
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) is_special_attribute(target)
|| target || target
.as_name_expr() .as_name_expr()
.is_some_and(|name| class_var_targets.contains(&name.id)) .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. // Avoid, e.g., Pydantic and msgspec models, which end up copying defaults on instance creation.
if has_default_copy_semantics(class_def, checker.semantic()) { if has_default_copy_semantics(class_def, checker.semantic()) {
return; return;
} }
checker.report_diagnostic(MutableClassDefault, value.range()); checker.report_diagnostic(MutableClassDefault, node.value.range());
} }
} }
_ => (), _ => (),

View File

@ -74,12 +74,12 @@ pub(crate) fn mutable_dataclass_default(checker: &Checker, class_def: &ast::Stmt
} }
for statement in &class_def.body { for statement in &class_def.body {
let Stmt::AnnAssign(ast::StmtAnnAssign { let Stmt::AnnAssign(ann_assign_stmt) = statement else {
annotation, continue;
value: Some(value), };
..
}) = statement let annotation = &ann_assign_stmt.annotation;
else { let Some(value) = &ann_assign_stmt.value else {
continue; continue;
}; };

View File

@ -120,15 +120,16 @@ pub(crate) fn sort_dunder_slots(checker: &Checker, binding: &Binding) {
}; };
let (target, value) = match stmt { let (target, value) = match stmt {
ast::Stmt::Assign(ast::StmtAssign { targets, value, .. }) => match targets.as_slice() { ast::Stmt::Assign(node) => match node.targets.as_slice() {
[target] => (target, &**value), [target] => (target, &*node.value),
_ => return, _ => return,
}, },
ast::Stmt::AnnAssign(ast::StmtAnnAssign { ast::Stmt::AnnAssign(node) => {
target, let Some(value) = &node.value else {
value: Some(value), return;
.. };
}) => (&**target, &**value), (&*node.target, &**value)
}
_ => return, _ => return,
}; };

View File

@ -64,11 +64,19 @@ impl<'a> source_order::SourceOrderVisitor<'a> for AsyncExprVisitor {
} }
fn visit_stmt(&mut self, stmt: &'a Stmt) { fn visit_stmt(&mut self, stmt: &'a Stmt) {
match stmt { match stmt {
Stmt::With(ast::StmtWith { is_async: true, .. }) => { Stmt::With(with_stmt) => {
if with_stmt.is_async {
self.found_await_or_async = true; self.found_await_or_async = true;
} else {
source_order::walk_stmt(self, stmt);
} }
Stmt::For(ast::StmtFor { is_async: true, .. }) => { }
Stmt::For(for_stmt) => {
if for_stmt.is_async {
self.found_await_or_async = true; self.found_await_or_async = true;
} else {
source_order::walk_stmt(self, stmt);
}
} }
// avoid counting inner classes' or functions' bodies toward the search // avoid counting inner classes' or functions' bodies toward the search
Stmt::FunctionDef(function_def) => { Stmt::FunctionDef(function_def) => {

View File

@ -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_macros::{ViolationMetadata, derive_message_formats};
use ruff_python_ast::{ use ruff_python_ast::{
@ -98,14 +98,15 @@ pub(crate) fn raise_within_try(checker: &Checker, body: &[Stmt], handlers: &[Exc
.collect(); .collect();
for stmt in raises { for stmt in raises {
let Stmt::Raise(ast::StmtRaise { let Stmt::Raise(node) = stmt
exc: Some(exception),
..
}) = stmt
else { else {
continue; continue;
}; };
let Some(exception) = &node.exc else {
continue;
};
// We can't check exception sub-classes without a type-checker implementation, so let's // We can't check exception sub-classes without a type-checker implementation, so let's
// just catch the blanket `Exception` for now. // just catch the blanket `Exception` for now.
if comparables.contains(&ComparableExpr::from(map_callable(exception))) if comparables.contains(&ComparableExpr::from(map_callable(exception)))

View File

@ -105,11 +105,13 @@ fn check_body(checker: &Checker, body: &[Stmt]) {
if has_control_flow(item) { if has_control_flow(item) {
return; return;
} }
if let Stmt::Raise(ast::StmtRaise { exc: Some(exc), .. }) = &item { if let Stmt::Raise(node) = &item {
if let Some(exc) = &node.exc {
check_raise(checker, exc, item); check_raise(checker, exc, item);
} }
} }
} }
}
/// Returns `true` if the given expression is a builtin exception. /// Returns `true` if the given expression is a builtin exception.
/// ///
@ -155,8 +157,8 @@ pub(crate) fn type_check_without_type_error(
.. ..
} = stmt_if; } = stmt_if;
if let Some(Stmt::If(ast::StmtIf { test, .. })) = parent { if let Some(Stmt::If(node)) = parent {
if !check_type_check_test(checker.semantic(), test) { if !check_type_check_test(checker.semantic(), &node.test) {
return; return;
} }
} }

View File

@ -43,12 +43,15 @@ impl Violation for UselessTryExcept {
pub(crate) fn useless_try_except(checker: &Checker, handlers: &[ExceptHandler]) { pub(crate) fn useless_try_except(checker: &Checker, handlers: &[ExceptHandler]) {
if handlers.iter().all(|handler| { if handlers.iter().all(|handler| {
let ExceptHandler::ExceptHandler(ExceptHandlerExceptHandler { name, body, .. }) = handler; let ExceptHandler::ExceptHandler(ExceptHandlerExceptHandler { name, body, .. }) = handler;
let Some(Stmt::Raise(ast::StmtRaise { let Some(Stmt::Raise(raise_stmt)) = &body.first() else {
exc, cause: None, ..
})) = &body.first()
else {
return false; return false;
}; };
if raise_stmt.cause.is_some() {
return false;
}
let exc = &raise_stmt.exc;
if let Some(expr) = exc { if let Some(expr) = exc {
// E.g., `except ... as e: raise e` // E.g., `except ... as e: raise e`
if let Expr::Name(ast::ExprName { id, .. }) = expr.as_ref() { if let Expr::Name(ast::ExprName { id, .. }) = expr.as_ref() {

View File

@ -95,13 +95,11 @@ struct RaiseStatementVisitor<'a> {
impl<'a> StatementVisitor<'a> for RaiseStatementVisitor<'a> { impl<'a> StatementVisitor<'a> for RaiseStatementVisitor<'a> {
fn visit_stmt(&mut self, stmt: &'a Stmt) { fn visit_stmt(&mut self, stmt: &'a Stmt) {
match stmt { match stmt {
Stmt::Raise(raise @ ast::StmtRaise { .. }) => { Stmt::Raise(raise) => {
self.raises.push(raise); self.raises.push(raise);
} }
Stmt::Try(ast::StmtTry { Stmt::Try(try_stmt) => {
body, finalbody, .. for stmt in try_stmt.body.iter().chain(&try_stmt.finalbody) {
}) => {
for stmt in body.iter().chain(finalbody) {
walk_stmt(self, stmt); walk_stmt(self, stmt);
} }
} }

View File

@ -77,6 +77,10 @@ fields = [{ name = "body", type = "Box<Expr>" }]
add_suffix_to_is_methods = true add_suffix_to_is_methods = true
anynode_is_label = "statement" anynode_is_label = "statement"
doc = "See also [stmt](https://docs.python.org/3/library/ast.html#ast.stmt)" 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] [Stmt.nodes.StmtFunctionDef]
doc = """See also [FunctionDef](https://docs.python.org/3/library/ast.html#ast.FunctionDef) doc = """See also [FunctionDef](https://docs.python.org/3/library/ast.html#ast.FunctionDef)

View File

@ -122,6 +122,8 @@ class Group:
add_suffix_to_is_methods: bool add_suffix_to_is_methods: bool
anynode_is_label: str anynode_is_label: str
doc: str | None doc: str | None
box_variants: bool
unboxed_variants: set[str]
def __init__(self, group_name: str, group: dict[str, Any]) -> None: def __init__(self, group_name: str, group: dict[str, Any]) -> None:
self.name = group_name 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.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.anynode_is_label = group.get("anynode_is_label", to_snake_case(group_name))
self.doc = group.get("doc") self.doc = group.get("doc")
self.box_variants = group.get("box_variants", False)
self.unboxed_variants = set(group.get("unboxed_variants", []))
self.nodes = [ self.nodes = [
Node(self, node_name, node) for node_name, node in group["nodes"].items() 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 @dataclass
class Node: class Node:
@ -321,10 +329,22 @@ def write_owned_enum(out: list[str], ast: Ast) -> None:
out.append('#[cfg_attr(feature = "get-size", derive(get_size2::GetSize))]') out.append('#[cfg_attr(feature = "get-size", derive(get_size2::GetSize))]')
out.append(f"pub enum {group.owned_enum_ty} {{") out.append(f"pub enum {group.owned_enum_ty} {{")
for node in group.nodes: for node in group.nodes:
if group.is_boxed(node.name):
out.append(f"{node.variant}(Box<{node.ty}>),")
else:
out.append(f"{node.variant}({node.ty}),") out.append(f"{node.variant}({node.ty}),")
out.append("}") out.append("}")
for node in group.nodes: for node in group.nodes:
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""" out.append(f"""
impl From<{node.ty}> for {group.owned_enum_ty} {{ impl From<{node.ty}> for {group.owned_enum_ty} {{
fn from(node: {node.ty}) -> Self {{ fn from(node: {node.ty}) -> Self {{
@ -369,6 +389,9 @@ def write_owned_enum(out: list[str], ast: Ast) -> None:
match_arm = f"Self::{variant_name}" match_arm = f"Self::{variant_name}"
if group.add_suffix_to_is_methods: if group.add_suffix_to_is_methods:
is_name = to_snake_case(node.variant + group.name) 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: if len(group.nodes) > 1:
out.append(f""" out.append(f"""
#[inline] #[inline]
@ -379,7 +402,7 @@ def write_owned_enum(out: list[str], ast: Ast) -> None:
#[inline] #[inline]
pub fn {is_name}(self) -> Option<{node.ty}> {{ pub fn {is_name}(self) -> Option<{node.ty}> {{
match self {{ match self {{
{match_arm}(val) => Some(val), {match_arm}(val) => Some({unbox}val),
_ => None, _ => None,
}} }}
}} }}
@ -387,7 +410,7 @@ def write_owned_enum(out: list[str], ast: Ast) -> None:
#[inline] #[inline]
pub fn expect_{is_name}(self) -> {node.ty} {{ pub fn expect_{is_name}(self) -> {node.ty} {{
match self {{ match self {{
{match_arm}(val) => val, {match_arm}(val) => {unbox}val,
_ => panic!("called expect on {{self:?}}"), _ => panic!("called expect on {{self:?}}"),
}} }}
}} }}
@ -418,14 +441,14 @@ def write_owned_enum(out: list[str], ast: Ast) -> None:
#[inline] #[inline]
pub fn {is_name}(self) -> Option<{node.ty}> {{ pub fn {is_name}(self) -> Option<{node.ty}> {{
match self {{ match self {{
{match_arm}(val) => Some(val), {match_arm}(val) => Some({unbox}val),
}} }}
}} }}
#[inline] #[inline]
pub fn expect_{is_name}(self) -> {node.ty} {{ pub fn expect_{is_name}(self) -> {node.ty} {{
match self {{ match self {{
{match_arm}(val) => val, {match_arm}(val) => {unbox}val,
}} }}
}} }}

View File

@ -1594,7 +1594,8 @@ pub enum ComparableStmt<'a> {
impl<'a> From<&'a ast::Stmt> for ComparableStmt<'a> { impl<'a> From<&'a ast::Stmt> for ComparableStmt<'a> {
fn from(stmt: &'a ast::Stmt) -> Self { fn from(stmt: &'a ast::Stmt) -> Self {
match stmt { match stmt {
ast::Stmt::FunctionDef(ast::StmtFunctionDef { ast::Stmt::FunctionDef(node) => {
let ast::StmtFunctionDef {
is_async, is_async,
name, name,
parameters, parameters,
@ -1604,7 +1605,8 @@ impl<'a> From<&'a ast::Stmt> for ComparableStmt<'a> {
type_params, type_params,
range: _, range: _,
node_index: _, node_index: _,
}) => Self::FunctionDef(StmtFunctionDef { } = &**node;
Self::FunctionDef(StmtFunctionDef {
is_async: *is_async, is_async: *is_async,
name: name.as_str(), name: name.as_str(),
parameters: parameters.into(), parameters: parameters.into(),
@ -1612,8 +1614,10 @@ impl<'a> From<&'a ast::Stmt> for ComparableStmt<'a> {
decorator_list: decorator_list.iter().map(Into::into).collect(), decorator_list: decorator_list.iter().map(Into::into).collect(),
returns: returns.as_ref().map(Into::into), returns: returns.as_ref().map(Into::into),
type_params: type_params.as_ref().map(Into::into), type_params: type_params.as_ref().map(Into::into),
}), })
ast::Stmt::ClassDef(ast::StmtClassDef { }
ast::Stmt::ClassDef(node) => {
let ast::StmtClassDef {
name, name,
arguments, arguments,
body, body,
@ -1621,72 +1625,92 @@ impl<'a> From<&'a ast::Stmt> for ComparableStmt<'a> {
type_params, type_params,
range: _, range: _,
node_index: _, node_index: _,
}) => Self::ClassDef(StmtClassDef { } = &**node;
Self::ClassDef(StmtClassDef {
name: name.as_str(), name: name.as_str(),
arguments: arguments.as_ref().map(Into::into).unwrap_or_default(), arguments: arguments.as_ref().map(Into::into).unwrap_or_default(),
body: body.iter().map(Into::into).collect(), body: body.iter().map(Into::into).collect(),
decorator_list: decorator_list.iter().map(Into::into).collect(), decorator_list: decorator_list.iter().map(Into::into).collect(),
type_params: type_params.as_ref().map(Into::into), type_params: type_params.as_ref().map(Into::into),
}), })
}
ast::Stmt::Return(ast::StmtReturn { ast::Stmt::Return(ast::StmtReturn {
value, value,
range: _, range: _,
node_index: _, node_index: _,
}) => Self::Return(StmtReturn { }) => {
Self::Return(StmtReturn {
value: value.as_ref().map(Into::into), value: value.as_ref().map(Into::into),
}), })
ast::Stmt::Delete(ast::StmtDelete { }
ast::Stmt::Delete(node) => {
let ast::StmtDelete {
targets, targets,
range: _, range: _,
node_index: _, node_index: _,
}) => Self::Delete(StmtDelete { } = &**node;
Self::Delete(StmtDelete {
targets: targets.iter().map(Into::into).collect(), targets: targets.iter().map(Into::into).collect(),
}), })
ast::Stmt::TypeAlias(ast::StmtTypeAlias { }
ast::Stmt::TypeAlias(node) => {
let ast::StmtTypeAlias {
range: _, range: _,
node_index: _, node_index: _,
name, name,
type_params, type_params,
value, value,
}) => Self::TypeAlias(StmtTypeAlias { } = &**node;
Self::TypeAlias(StmtTypeAlias {
name: name.into(), name: name.into(),
type_params: type_params.as_ref().map(Into::into), type_params: type_params.as_ref().map(Into::into),
value: value.into(), value: value.into(),
}), })
ast::Stmt::Assign(ast::StmtAssign { }
ast::Stmt::Assign(node) => {
let ast::StmtAssign {
targets, targets,
value, value,
range: _, range: _,
node_index: _, node_index: _,
}) => Self::Assign(StmtAssign { } = &**node;
Self::Assign(StmtAssign {
targets: targets.iter().map(Into::into).collect(), targets: targets.iter().map(Into::into).collect(),
value: value.into(), value: value.into(),
}), })
ast::Stmt::AugAssign(ast::StmtAugAssign { }
ast::Stmt::AugAssign(node) => {
let ast::StmtAugAssign {
target, target,
op, op,
value, value,
range: _, range: _,
node_index: _, node_index: _,
}) => Self::AugAssign(StmtAugAssign { } = &**node;
Self::AugAssign(StmtAugAssign {
target: target.into(), target: target.into(),
op: (*op).into(), op: (*op).into(),
value: value.into(), value: value.into(),
}), })
ast::Stmt::AnnAssign(ast::StmtAnnAssign { }
ast::Stmt::AnnAssign(node) => {
let ast::StmtAnnAssign {
target, target,
annotation, annotation,
value, value,
simple, simple,
range: _, range: _,
node_index: _, node_index: _,
}) => Self::AnnAssign(StmtAnnAssign { } = &**node;
Self::AnnAssign(StmtAnnAssign {
target: target.into(), target: target.into(),
annotation: annotation.into(), annotation: annotation.into(),
value: value.as_ref().map(Into::into), value: value.as_ref().map(Into::into),
simple: *simple, simple: *simple,
}), })
ast::Stmt::For(ast::StmtFor { }
ast::Stmt::For(node) => {
let ast::StmtFor {
is_async, is_async,
target, target,
iter, iter,
@ -1694,65 +1718,83 @@ impl<'a> From<&'a ast::Stmt> for ComparableStmt<'a> {
orelse, orelse,
range: _, range: _,
node_index: _, node_index: _,
}) => Self::For(StmtFor { } = &**node;
Self::For(StmtFor {
is_async: *is_async, is_async: *is_async,
target: target.into(), target: target.into(),
iter: iter.into(), iter: iter.into(),
body: body.iter().map(Into::into).collect(), body: body.iter().map(Into::into).collect(),
orelse: orelse.iter().map(Into::into).collect(), orelse: orelse.iter().map(Into::into).collect(),
}), })
ast::Stmt::While(ast::StmtWhile { }
ast::Stmt::While(node) => {
let ast::StmtWhile {
test, test,
body, body,
orelse, orelse,
range: _, range: _,
node_index: _, node_index: _,
}) => Self::While(StmtWhile { } = &**node;
Self::While(StmtWhile {
test: test.into(), test: test.into(),
body: body.iter().map(Into::into).collect(), body: body.iter().map(Into::into).collect(),
orelse: orelse.iter().map(Into::into).collect(), orelse: orelse.iter().map(Into::into).collect(),
}), })
ast::Stmt::If(ast::StmtIf { }
ast::Stmt::If(node) => {
let ast::StmtIf {
test, test,
body, body,
elif_else_clauses, elif_else_clauses,
range: _, range: _,
node_index: _, node_index: _,
}) => Self::If(StmtIf { } = &**node;
Self::If(StmtIf {
test: test.into(), test: test.into(),
body: body.iter().map(Into::into).collect(), body: body.iter().map(Into::into).collect(),
elif_else_clauses: elif_else_clauses.iter().map(Into::into).collect(), elif_else_clauses: elif_else_clauses.iter().map(Into::into).collect(),
}), })
ast::Stmt::With(ast::StmtWith { }
ast::Stmt::With(node) => {
let ast::StmtWith {
is_async, is_async,
items, items,
body, body,
range: _, range: _,
node_index: _, node_index: _,
}) => Self::With(StmtWith { } = &**node;
Self::With(StmtWith {
is_async: *is_async, is_async: *is_async,
items: items.iter().map(Into::into).collect(), items: items.iter().map(Into::into).collect(),
body: body.iter().map(Into::into).collect(), body: body.iter().map(Into::into).collect(),
}), })
ast::Stmt::Match(ast::StmtMatch { }
ast::Stmt::Match(node) => {
let ast::StmtMatch {
subject, subject,
cases, cases,
range: _, range: _,
node_index: _, node_index: _,
}) => Self::Match(StmtMatch { } = &**node;
Self::Match(StmtMatch {
subject: subject.into(), subject: subject.into(),
cases: cases.iter().map(Into::into).collect(), cases: cases.iter().map(Into::into).collect(),
}), })
ast::Stmt::Raise(ast::StmtRaise { }
ast::Stmt::Raise(node) => {
let ast::StmtRaise {
exc, exc,
cause, cause,
range: _, range: _,
node_index: _, node_index: _,
}) => Self::Raise(StmtRaise { } = &**node;
Self::Raise(StmtRaise {
exc: exc.as_ref().map(Into::into), exc: exc.as_ref().map(Into::into),
cause: cause.as_ref().map(Into::into), cause: cause.as_ref().map(Into::into),
}), })
ast::Stmt::Try(ast::StmtTry { }
ast::Stmt::Try(node) => {
let ast::StmtTry {
body, body,
handlers, handlers,
orelse, orelse,
@ -1760,67 +1802,89 @@ impl<'a> From<&'a ast::Stmt> for ComparableStmt<'a> {
is_star, is_star,
range: _, range: _,
node_index: _, node_index: _,
}) => Self::Try(StmtTry { } = &**node;
Self::Try(StmtTry {
body: body.iter().map(Into::into).collect(), body: body.iter().map(Into::into).collect(),
handlers: handlers.iter().map(Into::into).collect(), handlers: handlers.iter().map(Into::into).collect(),
orelse: orelse.iter().map(Into::into).collect(), orelse: orelse.iter().map(Into::into).collect(),
finalbody: finalbody.iter().map(Into::into).collect(), finalbody: finalbody.iter().map(Into::into).collect(),
is_star: *is_star, is_star: *is_star,
}), })
ast::Stmt::Assert(ast::StmtAssert { }
ast::Stmt::Assert(node) => {
let ast::StmtAssert {
test, test,
msg, msg,
range: _, range: _,
node_index: _, node_index: _,
}) => Self::Assert(StmtAssert { } = &**node;
Self::Assert(StmtAssert {
test: test.into(), test: test.into(),
msg: msg.as_ref().map(Into::into), msg: msg.as_ref().map(Into::into),
}), })
ast::Stmt::Import(ast::StmtImport { }
ast::Stmt::Import(node) => {
let ast::StmtImport {
names, names,
range: _, range: _,
node_index: _, node_index: _,
}) => Self::Import(StmtImport { } = &**node;
Self::Import(StmtImport {
names: names.iter().map(Into::into).collect(), names: names.iter().map(Into::into).collect(),
}), })
ast::Stmt::ImportFrom(ast::StmtImportFrom { }
ast::Stmt::ImportFrom(node) => {
let ast::StmtImportFrom {
module, module,
names, names,
level, level,
range: _, range: _,
node_index: _, node_index: _,
}) => Self::ImportFrom(StmtImportFrom { } = &**node;
Self::ImportFrom(StmtImportFrom {
module: module.as_deref(), module: module.as_deref(),
names: names.iter().map(Into::into).collect(), names: names.iter().map(Into::into).collect(),
level: *level, level: *level,
}), })
ast::Stmt::Global(ast::StmtGlobal { }
ast::Stmt::Global(node) => {
let ast::StmtGlobal {
names, names,
range: _, range: _,
node_index: _, node_index: _,
}) => Self::Global(StmtGlobal { } = &**node;
Self::Global(StmtGlobal {
names: names.iter().map(ast::Identifier::as_str).collect(), names: names.iter().map(ast::Identifier::as_str).collect(),
}), })
ast::Stmt::Nonlocal(ast::StmtNonlocal { }
ast::Stmt::Nonlocal(node) => {
let ast::StmtNonlocal {
names, names,
range: _, range: _,
node_index: _, node_index: _,
}) => Self::Nonlocal(StmtNonlocal { } = &**node;
Self::Nonlocal(StmtNonlocal {
names: names.iter().map(ast::Identifier::as_str).collect(), names: names.iter().map(ast::Identifier::as_str).collect(),
}), })
ast::Stmt::IpyEscapeCommand(ast::StmtIpyEscapeCommand { }
ast::Stmt::IpyEscapeCommand(node) => {
let ast::StmtIpyEscapeCommand {
kind, kind,
value, value,
range: _, range: _,
node_index: _, node_index: _,
}) => Self::IpyEscapeCommand(StmtIpyEscapeCommand { kind: *kind, value }), } = &**node;
Self::IpyEscapeCommand(StmtIpyEscapeCommand { kind: *kind, value })
}
ast::Stmt::Expr(ast::StmtExpr { ast::Stmt::Expr(ast::StmtExpr {
value, value,
range: _, range: _,
node_index: _, node_index: _,
}) => Self::Expr(StmtExpr { }) => {
Self::Expr(StmtExpr {
value: value.into(), value: value.into(),
}), })
}
ast::Stmt::Pass(_) => Self::Pass, ast::Stmt::Pass(_) => Self::Pass,
ast::Stmt::Break(_) => Self::Break, ast::Stmt::Break(_) => Self::Break,
ast::Stmt::Continue(_) => Self::Continue, ast::Stmt::Continue(_) => Self::Continue,

View File

@ -123,42 +123,42 @@ impl Mod {
#[derive(Clone, Debug, PartialEq)] #[derive(Clone, Debug, PartialEq)]
#[cfg_attr(feature = "get-size", derive(get_size2::GetSize))] #[cfg_attr(feature = "get-size", derive(get_size2::GetSize))]
pub enum Stmt { pub enum Stmt {
FunctionDef(crate::StmtFunctionDef), FunctionDef(Box<crate::StmtFunctionDef>),
ClassDef(crate::StmtClassDef), ClassDef(Box<crate::StmtClassDef>),
Return(crate::StmtReturn), Return(crate::StmtReturn),
Delete(crate::StmtDelete), Delete(Box<crate::StmtDelete>),
TypeAlias(crate::StmtTypeAlias), TypeAlias(Box<crate::StmtTypeAlias>),
Assign(crate::StmtAssign), Assign(Box<crate::StmtAssign>),
AugAssign(crate::StmtAugAssign), AugAssign(Box<crate::StmtAugAssign>),
AnnAssign(crate::StmtAnnAssign), AnnAssign(Box<crate::StmtAnnAssign>),
For(crate::StmtFor), For(Box<crate::StmtFor>),
While(crate::StmtWhile), While(Box<crate::StmtWhile>),
If(crate::StmtIf), If(Box<crate::StmtIf>),
With(crate::StmtWith), With(Box<crate::StmtWith>),
Match(crate::StmtMatch), Match(Box<crate::StmtMatch>),
Raise(crate::StmtRaise), Raise(Box<crate::StmtRaise>),
Try(crate::StmtTry), Try(Box<crate::StmtTry>),
Assert(crate::StmtAssert), Assert(Box<crate::StmtAssert>),
Import(crate::StmtImport), Import(Box<crate::StmtImport>),
ImportFrom(crate::StmtImportFrom), ImportFrom(Box<crate::StmtImportFrom>),
Global(crate::StmtGlobal), Global(Box<crate::StmtGlobal>),
Nonlocal(crate::StmtNonlocal), Nonlocal(Box<crate::StmtNonlocal>),
Expr(crate::StmtExpr), Expr(crate::StmtExpr),
Pass(crate::StmtPass), Pass(crate::StmtPass),
Break(crate::StmtBreak), Break(crate::StmtBreak),
Continue(crate::StmtContinue), Continue(crate::StmtContinue),
IpyEscapeCommand(crate::StmtIpyEscapeCommand), IpyEscapeCommand(Box<crate::StmtIpyEscapeCommand>),
} }
impl From<crate::StmtFunctionDef> for Stmt { impl From<crate::StmtFunctionDef> for Stmt {
fn from(node: crate::StmtFunctionDef) -> Self { fn from(node: crate::StmtFunctionDef) -> Self {
Self::FunctionDef(node) Self::FunctionDef(Box::new(node))
} }
} }
impl From<crate::StmtClassDef> for Stmt { impl From<crate::StmtClassDef> for Stmt {
fn from(node: crate::StmtClassDef) -> Self { fn from(node: crate::StmtClassDef) -> Self {
Self::ClassDef(node) Self::ClassDef(Box::new(node))
} }
} }
@ -170,103 +170,103 @@ impl From<crate::StmtReturn> for Stmt {
impl From<crate::StmtDelete> for Stmt { impl From<crate::StmtDelete> for Stmt {
fn from(node: crate::StmtDelete) -> Self { fn from(node: crate::StmtDelete) -> Self {
Self::Delete(node) Self::Delete(Box::new(node))
} }
} }
impl From<crate::StmtTypeAlias> for Stmt { impl From<crate::StmtTypeAlias> for Stmt {
fn from(node: crate::StmtTypeAlias) -> Self { fn from(node: crate::StmtTypeAlias) -> Self {
Self::TypeAlias(node) Self::TypeAlias(Box::new(node))
} }
} }
impl From<crate::StmtAssign> for Stmt { impl From<crate::StmtAssign> for Stmt {
fn from(node: crate::StmtAssign) -> Self { fn from(node: crate::StmtAssign) -> Self {
Self::Assign(node) Self::Assign(Box::new(node))
} }
} }
impl From<crate::StmtAugAssign> for Stmt { impl From<crate::StmtAugAssign> for Stmt {
fn from(node: crate::StmtAugAssign) -> Self { fn from(node: crate::StmtAugAssign) -> Self {
Self::AugAssign(node) Self::AugAssign(Box::new(node))
} }
} }
impl From<crate::StmtAnnAssign> for Stmt { impl From<crate::StmtAnnAssign> for Stmt {
fn from(node: crate::StmtAnnAssign) -> Self { fn from(node: crate::StmtAnnAssign) -> Self {
Self::AnnAssign(node) Self::AnnAssign(Box::new(node))
} }
} }
impl From<crate::StmtFor> for Stmt { impl From<crate::StmtFor> for Stmt {
fn from(node: crate::StmtFor) -> Self { fn from(node: crate::StmtFor) -> Self {
Self::For(node) Self::For(Box::new(node))
} }
} }
impl From<crate::StmtWhile> for Stmt { impl From<crate::StmtWhile> for Stmt {
fn from(node: crate::StmtWhile) -> Self { fn from(node: crate::StmtWhile) -> Self {
Self::While(node) Self::While(Box::new(node))
} }
} }
impl From<crate::StmtIf> for Stmt { impl From<crate::StmtIf> for Stmt {
fn from(node: crate::StmtIf) -> Self { fn from(node: crate::StmtIf) -> Self {
Self::If(node) Self::If(Box::new(node))
} }
} }
impl From<crate::StmtWith> for Stmt { impl From<crate::StmtWith> for Stmt {
fn from(node: crate::StmtWith) -> Self { fn from(node: crate::StmtWith) -> Self {
Self::With(node) Self::With(Box::new(node))
} }
} }
impl From<crate::StmtMatch> for Stmt { impl From<crate::StmtMatch> for Stmt {
fn from(node: crate::StmtMatch) -> Self { fn from(node: crate::StmtMatch) -> Self {
Self::Match(node) Self::Match(Box::new(node))
} }
} }
impl From<crate::StmtRaise> for Stmt { impl From<crate::StmtRaise> for Stmt {
fn from(node: crate::StmtRaise) -> Self { fn from(node: crate::StmtRaise) -> Self {
Self::Raise(node) Self::Raise(Box::new(node))
} }
} }
impl From<crate::StmtTry> for Stmt { impl From<crate::StmtTry> for Stmt {
fn from(node: crate::StmtTry) -> Self { fn from(node: crate::StmtTry) -> Self {
Self::Try(node) Self::Try(Box::new(node))
} }
} }
impl From<crate::StmtAssert> for Stmt { impl From<crate::StmtAssert> for Stmt {
fn from(node: crate::StmtAssert) -> Self { fn from(node: crate::StmtAssert) -> Self {
Self::Assert(node) Self::Assert(Box::new(node))
} }
} }
impl From<crate::StmtImport> for Stmt { impl From<crate::StmtImport> for Stmt {
fn from(node: crate::StmtImport) -> Self { fn from(node: crate::StmtImport) -> Self {
Self::Import(node) Self::Import(Box::new(node))
} }
} }
impl From<crate::StmtImportFrom> for Stmt { impl From<crate::StmtImportFrom> for Stmt {
fn from(node: crate::StmtImportFrom) -> Self { fn from(node: crate::StmtImportFrom) -> Self {
Self::ImportFrom(node) Self::ImportFrom(Box::new(node))
} }
} }
impl From<crate::StmtGlobal> for Stmt { impl From<crate::StmtGlobal> for Stmt {
fn from(node: crate::StmtGlobal) -> Self { fn from(node: crate::StmtGlobal) -> Self {
Self::Global(node) Self::Global(Box::new(node))
} }
} }
impl From<crate::StmtNonlocal> for Stmt { impl From<crate::StmtNonlocal> for Stmt {
fn from(node: crate::StmtNonlocal) -> Self { fn from(node: crate::StmtNonlocal) -> Self {
Self::Nonlocal(node) Self::Nonlocal(Box::new(node))
} }
} }
@ -296,7 +296,7 @@ impl From<crate::StmtContinue> for Stmt {
impl From<crate::StmtIpyEscapeCommand> for Stmt { impl From<crate::StmtIpyEscapeCommand> for Stmt {
fn from(node: crate::StmtIpyEscapeCommand) -> Self { fn from(node: crate::StmtIpyEscapeCommand) -> Self {
Self::IpyEscapeCommand(node) Self::IpyEscapeCommand(Box::new(node))
} }
} }
@ -374,7 +374,7 @@ impl Stmt {
#[inline] #[inline]
pub fn function_def_stmt(self) -> Option<crate::StmtFunctionDef> { pub fn function_def_stmt(self) -> Option<crate::StmtFunctionDef> {
match self { match self {
Self::FunctionDef(val) => Some(val), Self::FunctionDef(val) => Some(*val),
_ => None, _ => None,
} }
} }
@ -382,7 +382,7 @@ impl Stmt {
#[inline] #[inline]
pub fn expect_function_def_stmt(self) -> crate::StmtFunctionDef { pub fn expect_function_def_stmt(self) -> crate::StmtFunctionDef {
match self { match self {
Self::FunctionDef(val) => val, Self::FunctionDef(val) => *val,
_ => panic!("called expect on {self:?}"), _ => panic!("called expect on {self:?}"),
} }
} }
@ -411,7 +411,7 @@ impl Stmt {
#[inline] #[inline]
pub fn class_def_stmt(self) -> Option<crate::StmtClassDef> { pub fn class_def_stmt(self) -> Option<crate::StmtClassDef> {
match self { match self {
Self::ClassDef(val) => Some(val), Self::ClassDef(val) => Some(*val),
_ => None, _ => None,
} }
} }
@ -419,7 +419,7 @@ impl Stmt {
#[inline] #[inline]
pub fn expect_class_def_stmt(self) -> crate::StmtClassDef { pub fn expect_class_def_stmt(self) -> crate::StmtClassDef {
match self { match self {
Self::ClassDef(val) => val, Self::ClassDef(val) => *val,
_ => panic!("called expect on {self:?}"), _ => panic!("called expect on {self:?}"),
} }
} }
@ -485,7 +485,7 @@ impl Stmt {
#[inline] #[inline]
pub fn delete_stmt(self) -> Option<crate::StmtDelete> { pub fn delete_stmt(self) -> Option<crate::StmtDelete> {
match self { match self {
Self::Delete(val) => Some(val), Self::Delete(val) => Some(*val),
_ => None, _ => None,
} }
} }
@ -493,7 +493,7 @@ impl Stmt {
#[inline] #[inline]
pub fn expect_delete_stmt(self) -> crate::StmtDelete { pub fn expect_delete_stmt(self) -> crate::StmtDelete {
match self { match self {
Self::Delete(val) => val, Self::Delete(val) => *val,
_ => panic!("called expect on {self:?}"), _ => panic!("called expect on {self:?}"),
} }
} }
@ -522,7 +522,7 @@ impl Stmt {
#[inline] #[inline]
pub fn type_alias_stmt(self) -> Option<crate::StmtTypeAlias> { pub fn type_alias_stmt(self) -> Option<crate::StmtTypeAlias> {
match self { match self {
Self::TypeAlias(val) => Some(val), Self::TypeAlias(val) => Some(*val),
_ => None, _ => None,
} }
} }
@ -530,7 +530,7 @@ impl Stmt {
#[inline] #[inline]
pub fn expect_type_alias_stmt(self) -> crate::StmtTypeAlias { pub fn expect_type_alias_stmt(self) -> crate::StmtTypeAlias {
match self { match self {
Self::TypeAlias(val) => val, Self::TypeAlias(val) => *val,
_ => panic!("called expect on {self:?}"), _ => panic!("called expect on {self:?}"),
} }
} }
@ -559,7 +559,7 @@ impl Stmt {
#[inline] #[inline]
pub fn assign_stmt(self) -> Option<crate::StmtAssign> { pub fn assign_stmt(self) -> Option<crate::StmtAssign> {
match self { match self {
Self::Assign(val) => Some(val), Self::Assign(val) => Some(*val),
_ => None, _ => None,
} }
} }
@ -567,7 +567,7 @@ impl Stmt {
#[inline] #[inline]
pub fn expect_assign_stmt(self) -> crate::StmtAssign { pub fn expect_assign_stmt(self) -> crate::StmtAssign {
match self { match self {
Self::Assign(val) => val, Self::Assign(val) => *val,
_ => panic!("called expect on {self:?}"), _ => panic!("called expect on {self:?}"),
} }
} }
@ -596,7 +596,7 @@ impl Stmt {
#[inline] #[inline]
pub fn aug_assign_stmt(self) -> Option<crate::StmtAugAssign> { pub fn aug_assign_stmt(self) -> Option<crate::StmtAugAssign> {
match self { match self {
Self::AugAssign(val) => Some(val), Self::AugAssign(val) => Some(*val),
_ => None, _ => None,
} }
} }
@ -604,7 +604,7 @@ impl Stmt {
#[inline] #[inline]
pub fn expect_aug_assign_stmt(self) -> crate::StmtAugAssign { pub fn expect_aug_assign_stmt(self) -> crate::StmtAugAssign {
match self { match self {
Self::AugAssign(val) => val, Self::AugAssign(val) => *val,
_ => panic!("called expect on {self:?}"), _ => panic!("called expect on {self:?}"),
} }
} }
@ -633,7 +633,7 @@ impl Stmt {
#[inline] #[inline]
pub fn ann_assign_stmt(self) -> Option<crate::StmtAnnAssign> { pub fn ann_assign_stmt(self) -> Option<crate::StmtAnnAssign> {
match self { match self {
Self::AnnAssign(val) => Some(val), Self::AnnAssign(val) => Some(*val),
_ => None, _ => None,
} }
} }
@ -641,7 +641,7 @@ impl Stmt {
#[inline] #[inline]
pub fn expect_ann_assign_stmt(self) -> crate::StmtAnnAssign { pub fn expect_ann_assign_stmt(self) -> crate::StmtAnnAssign {
match self { match self {
Self::AnnAssign(val) => val, Self::AnnAssign(val) => *val,
_ => panic!("called expect on {self:?}"), _ => panic!("called expect on {self:?}"),
} }
} }
@ -670,7 +670,7 @@ impl Stmt {
#[inline] #[inline]
pub fn for_stmt(self) -> Option<crate::StmtFor> { pub fn for_stmt(self) -> Option<crate::StmtFor> {
match self { match self {
Self::For(val) => Some(val), Self::For(val) => Some(*val),
_ => None, _ => None,
} }
} }
@ -678,7 +678,7 @@ impl Stmt {
#[inline] #[inline]
pub fn expect_for_stmt(self) -> crate::StmtFor { pub fn expect_for_stmt(self) -> crate::StmtFor {
match self { match self {
Self::For(val) => val, Self::For(val) => *val,
_ => panic!("called expect on {self:?}"), _ => panic!("called expect on {self:?}"),
} }
} }
@ -707,7 +707,7 @@ impl Stmt {
#[inline] #[inline]
pub fn while_stmt(self) -> Option<crate::StmtWhile> { pub fn while_stmt(self) -> Option<crate::StmtWhile> {
match self { match self {
Self::While(val) => Some(val), Self::While(val) => Some(*val),
_ => None, _ => None,
} }
} }
@ -715,7 +715,7 @@ impl Stmt {
#[inline] #[inline]
pub fn expect_while_stmt(self) -> crate::StmtWhile { pub fn expect_while_stmt(self) -> crate::StmtWhile {
match self { match self {
Self::While(val) => val, Self::While(val) => *val,
_ => panic!("called expect on {self:?}"), _ => panic!("called expect on {self:?}"),
} }
} }
@ -744,7 +744,7 @@ impl Stmt {
#[inline] #[inline]
pub fn if_stmt(self) -> Option<crate::StmtIf> { pub fn if_stmt(self) -> Option<crate::StmtIf> {
match self { match self {
Self::If(val) => Some(val), Self::If(val) => Some(*val),
_ => None, _ => None,
} }
} }
@ -752,7 +752,7 @@ impl Stmt {
#[inline] #[inline]
pub fn expect_if_stmt(self) -> crate::StmtIf { pub fn expect_if_stmt(self) -> crate::StmtIf {
match self { match self {
Self::If(val) => val, Self::If(val) => *val,
_ => panic!("called expect on {self:?}"), _ => panic!("called expect on {self:?}"),
} }
} }
@ -781,7 +781,7 @@ impl Stmt {
#[inline] #[inline]
pub fn with_stmt(self) -> Option<crate::StmtWith> { pub fn with_stmt(self) -> Option<crate::StmtWith> {
match self { match self {
Self::With(val) => Some(val), Self::With(val) => Some(*val),
_ => None, _ => None,
} }
} }
@ -789,7 +789,7 @@ impl Stmt {
#[inline] #[inline]
pub fn expect_with_stmt(self) -> crate::StmtWith { pub fn expect_with_stmt(self) -> crate::StmtWith {
match self { match self {
Self::With(val) => val, Self::With(val) => *val,
_ => panic!("called expect on {self:?}"), _ => panic!("called expect on {self:?}"),
} }
} }
@ -818,7 +818,7 @@ impl Stmt {
#[inline] #[inline]
pub fn match_stmt(self) -> Option<crate::StmtMatch> { pub fn match_stmt(self) -> Option<crate::StmtMatch> {
match self { match self {
Self::Match(val) => Some(val), Self::Match(val) => Some(*val),
_ => None, _ => None,
} }
} }
@ -826,7 +826,7 @@ impl Stmt {
#[inline] #[inline]
pub fn expect_match_stmt(self) -> crate::StmtMatch { pub fn expect_match_stmt(self) -> crate::StmtMatch {
match self { match self {
Self::Match(val) => val, Self::Match(val) => *val,
_ => panic!("called expect on {self:?}"), _ => panic!("called expect on {self:?}"),
} }
} }
@ -855,7 +855,7 @@ impl Stmt {
#[inline] #[inline]
pub fn raise_stmt(self) -> Option<crate::StmtRaise> { pub fn raise_stmt(self) -> Option<crate::StmtRaise> {
match self { match self {
Self::Raise(val) => Some(val), Self::Raise(val) => Some(*val),
_ => None, _ => None,
} }
} }
@ -863,7 +863,7 @@ impl Stmt {
#[inline] #[inline]
pub fn expect_raise_stmt(self) -> crate::StmtRaise { pub fn expect_raise_stmt(self) -> crate::StmtRaise {
match self { match self {
Self::Raise(val) => val, Self::Raise(val) => *val,
_ => panic!("called expect on {self:?}"), _ => panic!("called expect on {self:?}"),
} }
} }
@ -892,7 +892,7 @@ impl Stmt {
#[inline] #[inline]
pub fn try_stmt(self) -> Option<crate::StmtTry> { pub fn try_stmt(self) -> Option<crate::StmtTry> {
match self { match self {
Self::Try(val) => Some(val), Self::Try(val) => Some(*val),
_ => None, _ => None,
} }
} }
@ -900,7 +900,7 @@ impl Stmt {
#[inline] #[inline]
pub fn expect_try_stmt(self) -> crate::StmtTry { pub fn expect_try_stmt(self) -> crate::StmtTry {
match self { match self {
Self::Try(val) => val, Self::Try(val) => *val,
_ => panic!("called expect on {self:?}"), _ => panic!("called expect on {self:?}"),
} }
} }
@ -929,7 +929,7 @@ impl Stmt {
#[inline] #[inline]
pub fn assert_stmt(self) -> Option<crate::StmtAssert> { pub fn assert_stmt(self) -> Option<crate::StmtAssert> {
match self { match self {
Self::Assert(val) => Some(val), Self::Assert(val) => Some(*val),
_ => None, _ => None,
} }
} }
@ -937,7 +937,7 @@ impl Stmt {
#[inline] #[inline]
pub fn expect_assert_stmt(self) -> crate::StmtAssert { pub fn expect_assert_stmt(self) -> crate::StmtAssert {
match self { match self {
Self::Assert(val) => val, Self::Assert(val) => *val,
_ => panic!("called expect on {self:?}"), _ => panic!("called expect on {self:?}"),
} }
} }
@ -966,7 +966,7 @@ impl Stmt {
#[inline] #[inline]
pub fn import_stmt(self) -> Option<crate::StmtImport> { pub fn import_stmt(self) -> Option<crate::StmtImport> {
match self { match self {
Self::Import(val) => Some(val), Self::Import(val) => Some(*val),
_ => None, _ => None,
} }
} }
@ -974,7 +974,7 @@ impl Stmt {
#[inline] #[inline]
pub fn expect_import_stmt(self) -> crate::StmtImport { pub fn expect_import_stmt(self) -> crate::StmtImport {
match self { match self {
Self::Import(val) => val, Self::Import(val) => *val,
_ => panic!("called expect on {self:?}"), _ => panic!("called expect on {self:?}"),
} }
} }
@ -1003,7 +1003,7 @@ impl Stmt {
#[inline] #[inline]
pub fn import_from_stmt(self) -> Option<crate::StmtImportFrom> { pub fn import_from_stmt(self) -> Option<crate::StmtImportFrom> {
match self { match self {
Self::ImportFrom(val) => Some(val), Self::ImportFrom(val) => Some(*val),
_ => None, _ => None,
} }
} }
@ -1011,7 +1011,7 @@ impl Stmt {
#[inline] #[inline]
pub fn expect_import_from_stmt(self) -> crate::StmtImportFrom { pub fn expect_import_from_stmt(self) -> crate::StmtImportFrom {
match self { match self {
Self::ImportFrom(val) => val, Self::ImportFrom(val) => *val,
_ => panic!("called expect on {self:?}"), _ => panic!("called expect on {self:?}"),
} }
} }
@ -1040,7 +1040,7 @@ impl Stmt {
#[inline] #[inline]
pub fn global_stmt(self) -> Option<crate::StmtGlobal> { pub fn global_stmt(self) -> Option<crate::StmtGlobal> {
match self { match self {
Self::Global(val) => Some(val), Self::Global(val) => Some(*val),
_ => None, _ => None,
} }
} }
@ -1048,7 +1048,7 @@ impl Stmt {
#[inline] #[inline]
pub fn expect_global_stmt(self) -> crate::StmtGlobal { pub fn expect_global_stmt(self) -> crate::StmtGlobal {
match self { match self {
Self::Global(val) => val, Self::Global(val) => *val,
_ => panic!("called expect on {self:?}"), _ => panic!("called expect on {self:?}"),
} }
} }
@ -1077,7 +1077,7 @@ impl Stmt {
#[inline] #[inline]
pub fn nonlocal_stmt(self) -> Option<crate::StmtNonlocal> { pub fn nonlocal_stmt(self) -> Option<crate::StmtNonlocal> {
match self { match self {
Self::Nonlocal(val) => Some(val), Self::Nonlocal(val) => Some(*val),
_ => None, _ => None,
} }
} }
@ -1085,7 +1085,7 @@ impl Stmt {
#[inline] #[inline]
pub fn expect_nonlocal_stmt(self) -> crate::StmtNonlocal { pub fn expect_nonlocal_stmt(self) -> crate::StmtNonlocal {
match self { match self {
Self::Nonlocal(val) => val, Self::Nonlocal(val) => *val,
_ => panic!("called expect on {self:?}"), _ => panic!("called expect on {self:?}"),
} }
} }
@ -1262,7 +1262,7 @@ impl Stmt {
#[inline] #[inline]
pub fn ipy_escape_command_stmt(self) -> Option<crate::StmtIpyEscapeCommand> { pub fn ipy_escape_command_stmt(self) -> Option<crate::StmtIpyEscapeCommand> {
match self { match self {
Self::IpyEscapeCommand(val) => Some(val), Self::IpyEscapeCommand(val) => Some(*val),
_ => None, _ => None,
} }
} }
@ -1270,7 +1270,7 @@ impl Stmt {
#[inline] #[inline]
pub fn expect_ipy_escape_command_stmt(self) -> crate::StmtIpyEscapeCommand { pub fn expect_ipy_escape_command_stmt(self) -> crate::StmtIpyEscapeCommand {
match self { match self {
Self::IpyEscapeCommand(val) => val, Self::IpyEscapeCommand(val) => *val,
_ => panic!("called expect on {self:?}"), _ => panic!("called expect on {self:?}"),
} }
} }

Some files were not shown because too many files have changed in this diff Show More