Refactor range from Attributed to Nodes (#4422)

This commit is contained in:
Micha Reiser 2023-05-16 08:36:32 +02:00 committed by GitHub
parent 140e0acf54
commit fa26860296
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
330 changed files with 4816 additions and 3946 deletions

24
Cargo.lock generated
View file

@ -1988,6 +1988,16 @@ dependencies = [
"rustpython-parser", "rustpython-parser",
] ]
[[package]]
name = "ruff_source_location"
version = "0.0.0"
source = "git+https://github.com/RustPython/Parser.git?rev=718354673eb2cc9645d63fc0c50b4ad08e5595c2#718354673eb2cc9645d63fc0c50b4ad08e5595c2"
dependencies = [
"memchr",
"once_cell",
"ruff_text_size",
]
[[package]] [[package]]
name = "ruff_testing_macros" name = "ruff_testing_macros"
version = "0.0.0" version = "0.0.0"
@ -2001,7 +2011,7 @@ dependencies = [
[[package]] [[package]]
name = "ruff_text_size" name = "ruff_text_size"
version = "0.0.0" version = "0.0.0"
source = "git+https://github.com/RustPython/Parser.git?rev=a983f4383fb1ad8c1c66acb1d5b0016e59f95a49#a983f4383fb1ad8c1c66acb1d5b0016e59f95a49" source = "git+https://github.com/RustPython/Parser.git?rev=718354673eb2cc9645d63fc0c50b4ad08e5595c2#718354673eb2cc9645d63fc0c50b4ad08e5595c2"
dependencies = [ dependencies = [
"schemars", "schemars",
"serde", "serde",
@ -2072,17 +2082,18 @@ dependencies = [
[[package]] [[package]]
name = "rustpython-ast" name = "rustpython-ast"
version = "0.2.0" version = "0.2.0"
source = "git+https://github.com/RustPython/Parser.git?rev=a983f4383fb1ad8c1c66acb1d5b0016e59f95a49#a983f4383fb1ad8c1c66acb1d5b0016e59f95a49" source = "git+https://github.com/RustPython/Parser.git?rev=718354673eb2cc9645d63fc0c50b4ad08e5595c2#718354673eb2cc9645d63fc0c50b4ad08e5595c2"
dependencies = [ dependencies = [
"is-macro", "is-macro",
"num-bigint", "num-bigint",
"rustpython-parser-core", "rustpython-parser-core",
"static_assertions",
] ]
[[package]] [[package]]
name = "rustpython-format" name = "rustpython-format"
version = "0.2.0" version = "0.2.0"
source = "git+https://github.com/RustPython/Parser.git?rev=a983f4383fb1ad8c1c66acb1d5b0016e59f95a49#a983f4383fb1ad8c1c66acb1d5b0016e59f95a49" source = "git+https://github.com/RustPython/Parser.git?rev=718354673eb2cc9645d63fc0c50b4ad08e5595c2#718354673eb2cc9645d63fc0c50b4ad08e5595c2"
dependencies = [ dependencies = [
"bitflags 2.2.1", "bitflags 2.2.1",
"itertools", "itertools",
@ -2094,7 +2105,7 @@ dependencies = [
[[package]] [[package]]
name = "rustpython-literal" name = "rustpython-literal"
version = "0.2.0" version = "0.2.0"
source = "git+https://github.com/RustPython/Parser.git?rev=a983f4383fb1ad8c1c66acb1d5b0016e59f95a49#a983f4383fb1ad8c1c66acb1d5b0016e59f95a49" source = "git+https://github.com/RustPython/Parser.git?rev=718354673eb2cc9645d63fc0c50b4ad08e5595c2#718354673eb2cc9645d63fc0c50b4ad08e5595c2"
dependencies = [ dependencies = [
"hexf-parse", "hexf-parse",
"lexical-parse-float", "lexical-parse-float",
@ -2105,7 +2116,7 @@ dependencies = [
[[package]] [[package]]
name = "rustpython-parser" name = "rustpython-parser"
version = "0.2.0" version = "0.2.0"
source = "git+https://github.com/RustPython/Parser.git?rev=a983f4383fb1ad8c1c66acb1d5b0016e59f95a49#a983f4383fb1ad8c1c66acb1d5b0016e59f95a49" source = "git+https://github.com/RustPython/Parser.git?rev=718354673eb2cc9645d63fc0c50b4ad08e5595c2#718354673eb2cc9645d63fc0c50b4ad08e5595c2"
dependencies = [ dependencies = [
"anyhow", "anyhow",
"itertools", "itertools",
@ -2127,8 +2138,9 @@ dependencies = [
[[package]] [[package]]
name = "rustpython-parser-core" name = "rustpython-parser-core"
version = "0.2.0" version = "0.2.0"
source = "git+https://github.com/RustPython/Parser.git?rev=a983f4383fb1ad8c1c66acb1d5b0016e59f95a49#a983f4383fb1ad8c1c66acb1d5b0016e59f95a49" source = "git+https://github.com/RustPython/Parser.git?rev=718354673eb2cc9645d63fc0c50b4ad08e5595c2#718354673eb2cc9645d63fc0c50b4ad08e5595c2"
dependencies = [ dependencies = [
"ruff_source_location",
"ruff_text_size", "ruff_text_size",
] ]

View file

@ -31,10 +31,10 @@ proc-macro2 = { version = "1.0.51" }
quote = { version = "1.0.23" } quote = { version = "1.0.23" }
regex = { version = "1.7.1" } regex = { version = "1.7.1" }
rustc-hash = { version = "1.1.0" } rustc-hash = { version = "1.1.0" }
ruff_text_size = { git = "https://github.com/RustPython/Parser.git", rev = "a983f4383fb1ad8c1c66acb1d5b0016e59f95a49" } ruff_text_size = { git = "https://github.com/RustPython/Parser.git", rev = "718354673eb2cc9645d63fc0c50b4ad08e5595c2" }
rustpython-format = { git = "https://github.com/RustPython/Parser.git", rev = "a983f4383fb1ad8c1c66acb1d5b0016e59f95a49" } rustpython-format = { git = "https://github.com/RustPython/Parser.git", rev = "718354673eb2cc9645d63fc0c50b4ad08e5595c2" }
rustpython-literal = { git = "https://github.com/RustPython/Parser.git", rev = "a983f4383fb1ad8c1c66acb1d5b0016e59f95a49" } rustpython-literal = { git = "https://github.com/RustPython/Parser.git", rev = "718354673eb2cc9645d63fc0c50b4ad08e5595c2" }
rustpython-parser = { git = "https://github.com/RustPython/Parser.git", rev = "a983f4383fb1ad8c1c66acb1d5b0016e59f95a49" , default-features = false} rustpython-parser = { git = "https://github.com/RustPython/Parser.git", rev = "718354673eb2cc9645d63fc0c50b4ad08e5595c2" , features = ["all-nodes-with-ranges"]}
schemars = { version = "0.8.12" } schemars = { version = "0.8.12" }
serde = { version = "1.0.152", features = ["derive"] } serde = { version = "1.0.152", features = ["derive"] }
serde_json = { version = "1.0.93", features = ["preserve_order"] } serde_json = { version = "1.0.93", features = ["preserve_order"] }

View file

@ -5,7 +5,7 @@ use libcst_native::{
Codegen, CodegenState, ImportNames, ParenthesizableWhitespace, SmallStatement, Statement, Codegen, CodegenState, ImportNames, ParenthesizableWhitespace, SmallStatement, Statement,
}; };
use ruff_text_size::{TextLen, TextRange, TextSize}; use ruff_text_size::{TextLen, TextRange, TextSize};
use rustpython_parser::ast::{self, ExcepthandlerKind, Expr, Keyword, Stmt, StmtKind}; use rustpython_parser::ast::{self, Excepthandler, Expr, Keyword, Ranged, Stmt};
use rustpython_parser::{lexer, Mode, Tok}; use rustpython_parser::{lexer, Mode, Tok};
use ruff_diagnostics::Edit; use ruff_diagnostics::Edit;
@ -27,22 +27,22 @@ fn has_single_child(body: &[Stmt], deleted: &[&Stmt]) -> 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, deleted: &[&Stmt]) -> Result<bool> { fn is_lone_child(child: &Stmt, parent: &Stmt, deleted: &[&Stmt]) -> Result<bool> {
match &parent.node { match parent {
StmtKind::FunctionDef(ast::StmtFunctionDef { body, .. }) Stmt::FunctionDef(ast::StmtFunctionDef { body, .. })
| StmtKind::AsyncFunctionDef(ast::StmtAsyncFunctionDef { body, .. }) | Stmt::AsyncFunctionDef(ast::StmtAsyncFunctionDef { body, .. })
| StmtKind::ClassDef(ast::StmtClassDef { body, .. }) | Stmt::ClassDef(ast::StmtClassDef { body, .. })
| StmtKind::With(ast::StmtWith { body, .. }) | Stmt::With(ast::StmtWith { body, .. })
| StmtKind::AsyncWith(ast::StmtAsyncWith { body, .. }) => { | Stmt::AsyncWith(ast::StmtAsyncWith { body, .. }) => {
if body.iter().contains(child) { if body.iter().contains(child) {
Ok(has_single_child(body, deleted)) Ok(has_single_child(body, deleted))
} else { } else {
bail!("Unable to find child in parent body") bail!("Unable to find child in parent body")
} }
} }
StmtKind::For(ast::StmtFor { body, orelse, .. }) Stmt::For(ast::StmtFor { body, orelse, .. })
| StmtKind::AsyncFor(ast::StmtAsyncFor { body, orelse, .. }) | Stmt::AsyncFor(ast::StmtAsyncFor { body, orelse, .. })
| StmtKind::While(ast::StmtWhile { body, orelse, .. }) | Stmt::While(ast::StmtWhile { body, orelse, .. })
| StmtKind::If(ast::StmtIf { body, orelse, .. }) => { | Stmt::If(ast::StmtIf { body, orelse, .. }) => {
if body.iter().contains(child) { if body.iter().contains(child) {
Ok(has_single_child(body, deleted)) Ok(has_single_child(body, deleted))
} else if orelse.iter().contains(child) { } else if orelse.iter().contains(child) {
@ -51,17 +51,19 @@ fn is_lone_child(child: &Stmt, parent: &Stmt, deleted: &[&Stmt]) -> Result<bool>
bail!("Unable to find child in parent body") bail!("Unable to find child in parent body")
} }
} }
StmtKind::Try(ast::StmtTry { Stmt::Try(ast::StmtTry {
body, body,
handlers, handlers,
orelse, orelse,
finalbody, finalbody,
range: _,
}) })
| StmtKind::TryStar(ast::StmtTryStar { | Stmt::TryStar(ast::StmtTryStar {
body, body,
handlers, handlers,
orelse, orelse,
finalbody, finalbody,
range: _,
}) => { }) => {
if body.iter().contains(child) { if body.iter().contains(child) {
Ok(has_single_child(body, deleted)) Ok(has_single_child(body, deleted))
@ -69,10 +71,8 @@ fn is_lone_child(child: &Stmt, parent: &Stmt, deleted: &[&Stmt]) -> Result<bool>
Ok(has_single_child(orelse, deleted)) Ok(has_single_child(orelse, deleted))
} else if finalbody.iter().contains(child) { } else if finalbody.iter().contains(child) {
Ok(has_single_child(finalbody, deleted)) Ok(has_single_child(finalbody, deleted))
} else if let Some(body) = handlers.iter().find_map(|handler| match &handler.node { } else if let Some(body) = handlers.iter().find_map(|handler| match handler {
ExcepthandlerKind::ExceptHandler(ast::ExcepthandlerExceptHandler { Excepthandler::ExceptHandler(ast::ExcepthandlerExceptHandler { body, .. }) => {
body, ..
}) => {
if body.iter().contains(child) { if body.iter().contains(child) {
Some(body) Some(body)
} else { } else {
@ -85,7 +85,7 @@ fn is_lone_child(child: &Stmt, parent: &Stmt, deleted: &[&Stmt]) -> Result<bool>
bail!("Unable to find child in parent body") bail!("Unable to find child in parent body")
} }
} }
StmtKind::Match(ast::StmtMatch { cases, .. }) => { Stmt::Match(ast::StmtMatch { cases, .. }) => {
if let Some(body) = cases.iter().find_map(|case| { if let Some(body) = cases.iter().find_map(|case| {
if case.body.iter().contains(child) { if case.body.iter().contains(child) {
Some(&case.body) Some(&case.body)

File diff suppressed because it is too large Load diff

View file

@ -2,7 +2,7 @@
use std::borrow::Cow; use std::borrow::Cow;
use std::path::Path; use std::path::Path;
use rustpython_parser::ast::{self, StmtKind, Suite}; use rustpython_parser::ast::{self, Ranged, Stmt, Suite};
use ruff_diagnostics::Diagnostic; use ruff_diagnostics::Diagnostic;
use ruff_python_ast::helpers::to_module_path; use ruff_python_ast::helpers::to_module_path;
@ -28,18 +28,19 @@ fn extract_import_map(path: &Path, package: Option<&Path>, blocks: &[&Block]) ->
let num_imports = blocks.iter().map(|block| block.imports.len()).sum(); let num_imports = blocks.iter().map(|block| block.imports.len()).sum();
let mut module_imports = Vec::with_capacity(num_imports); let mut module_imports = Vec::with_capacity(num_imports);
for stmt in blocks.iter().flat_map(|block| &block.imports) { for stmt in blocks.iter().flat_map(|block| &block.imports) {
match &stmt.node { match stmt {
StmtKind::Import(ast::StmtImport { names }) => { Stmt::Import(ast::StmtImport { names, range: _ }) => {
module_imports.extend( module_imports.extend(
names names
.iter() .iter()
.map(|name| ModuleImport::new(name.node.name.to_string(), stmt.range())), .map(|name| ModuleImport::new(name.name.to_string(), stmt.range())),
); );
} }
StmtKind::ImportFrom(ast::StmtImportFrom { Stmt::ImportFrom(ast::StmtImportFrom {
module, module,
names, names,
level, level,
range: _,
}) => { }) => {
let level = level.map_or(0, |level| level.to_usize()); let level = level.map_or(0, |level| level.to_usize());
let module = if let Some(module) = module { let module = if let Some(module) = module {
@ -60,10 +61,10 @@ fn extract_import_map(path: &Path, package: Option<&Path>, blocks: &[&Block]) ->
Cow::Owned(module_path[..module_path.len() - level].join(".")) Cow::Owned(module_path[..module_path.len() - level].join("."))
}; };
module_imports.extend(names.iter().map(|name| { module_imports.extend(names.iter().map(|name| {
ModuleImport::new(format!("{}.{}", module, name.node.name), name.range()) ModuleImport::new(format!("{}.{}", module, name.name), name.range())
})); }));
} }
_ => panic!("Expected StmtKind::Import | StmtKind::ImportFrom"), _ => panic!("Expected Stmt::Import | Stmt::ImportFrom"),
} }
} }

View file

@ -1,7 +1,7 @@
use libcst_native::{Expression, NameOrAttribute}; use libcst_native::{Expression, NameOrAttribute};
fn compose_call_path_inner<'a>(expr: &'a Expression, parts: &mut Vec<&'a str>) { fn compose_call_path_inner<'a>(expr: &'a Expression, parts: &mut Vec<&'a str>) {
match &expr { match expr {
Expression::Call(expr) => { Expression::Call(expr) => {
compose_call_path_inner(&expr.func, parts); compose_call_path_inner(&expr.func, parts);
} }

View file

@ -4,7 +4,7 @@
use std::iter::FusedIterator; use std::iter::FusedIterator;
use ruff_text_size::{TextRange, TextSize}; use ruff_text_size::{TextRange, TextSize};
use rustpython_parser::ast::{self, Constant, ExprKind, Stmt, StmtKind, Suite}; use rustpython_parser::ast::{self, Constant, Expr, Ranged, Stmt, Suite};
use rustpython_parser::lexer::LexResult; use rustpython_parser::lexer::LexResult;
use rustpython_parser::Tok; use rustpython_parser::Tok;
@ -79,11 +79,11 @@ struct StringLinesVisitor<'a> {
impl StatementVisitor<'_> for StringLinesVisitor<'_> { impl StatementVisitor<'_> for StringLinesVisitor<'_> {
fn visit_stmt(&mut self, stmt: &Stmt) { fn visit_stmt(&mut self, stmt: &Stmt) {
if let StmtKind::Expr(ast::StmtExpr { value }) = &stmt.node { if let Stmt::Expr(ast::StmtExpr { value, range: _ }) = stmt {
if let ExprKind::Constant(ast::ExprConstant { if let Expr::Constant(ast::ExprConstant {
value: Constant::Str(..), value: Constant::Str(..),
.. ..
}) = &value.node }) = value.as_ref()
{ {
for line in UniversalNewlineIterator::with_offset( for line in UniversalNewlineIterator::with_offset(
self.locator.slice(value.range()), self.locator.slice(value.range()),

View file

@ -1,6 +1,6 @@
//! Extract docstrings from an AST. //! Extract docstrings from an AST.
use rustpython_parser::ast::{self, Constant, Expr, ExprKind, Stmt, StmtKind}; use rustpython_parser::ast::{self, Constant, Expr, Stmt};
use ruff_python_semantic::definition::{Definition, DefinitionId, Definitions, Member, MemberKind}; use ruff_python_semantic::definition::{Definition, DefinitionId, Definitions, Member, MemberKind};
@ -8,13 +8,13 @@ use ruff_python_semantic::definition::{Definition, DefinitionId, Definitions, Me
pub(crate) fn docstring_from(suite: &[Stmt]) -> Option<&Expr> { pub(crate) fn docstring_from(suite: &[Stmt]) -> Option<&Expr> {
let stmt = suite.first()?; let stmt = suite.first()?;
// Require the docstring to be a standalone expression. // Require the docstring to be a standalone expression.
let StmtKind::Expr(ast::StmtExpr { value }) = &stmt.node else { let Stmt::Expr(ast::StmtExpr { value, range: _ }) = stmt else {
return None; return None;
}; };
// Only match strings. // Only match strings.
if !matches!( if !matches!(
&value.node, value.as_ref(),
ExprKind::Constant(ast::ExprConstant { Expr::Constant(ast::ExprConstant {
value: Constant::Str(_), value: Constant::Str(_),
.. ..
}) })
@ -29,10 +29,9 @@ pub(crate) fn extract_docstring<'a>(definition: &'a Definition<'a>) -> Option<&'
match definition { match definition {
Definition::Module(module) => docstring_from(module.python_ast), Definition::Module(module) => docstring_from(module.python_ast),
Definition::Member(member) => { Definition::Member(member) => {
if let StmtKind::ClassDef(ast::StmtClassDef { body, .. }) if let Stmt::ClassDef(ast::StmtClassDef { body, .. })
| StmtKind::FunctionDef(ast::StmtFunctionDef { body, .. }) | Stmt::FunctionDef(ast::StmtFunctionDef { body, .. })
| StmtKind::AsyncFunctionDef(ast::StmtAsyncFunctionDef { body, .. }) = | Stmt::AsyncFunctionDef(ast::StmtAsyncFunctionDef { body, .. }) = &member.stmt
&member.stmt.node
{ {
docstring_from(body) docstring_from(body)
} else { } else {

View file

@ -2,7 +2,7 @@ use std::fmt::{Debug, Formatter};
use std::ops::Deref; use std::ops::Deref;
use ruff_text_size::{TextRange, TextSize}; use ruff_text_size::{TextRange, TextSize};
use rustpython_parser::ast::Expr; use rustpython_parser::ast::{Expr, Ranged};
use ruff_python_semantic::definition::Definition; use ruff_python_semantic::definition::Definition;
@ -29,15 +29,15 @@ impl<'a> Docstring<'a> {
DocstringBody { docstring: self } DocstringBody { docstring: self }
} }
pub(crate) const fn start(&self) -> TextSize { pub(crate) fn start(&self) -> TextSize {
self.expr.start() self.expr.start()
} }
pub(crate) const fn end(&self) -> TextSize { pub(crate) fn end(&self) -> TextSize {
self.expr.end() self.expr.end()
} }
pub(crate) const fn range(&self) -> TextRange { pub(crate) fn range(&self) -> TextRange {
self.expr.range() self.expr.range()
} }

View file

@ -3,7 +3,7 @@
use anyhow::Result; use anyhow::Result;
use libcst_native::{Codegen, CodegenState, ImportAlias, Name, NameOrAttribute}; use libcst_native::{Codegen, CodegenState, ImportAlias, Name, NameOrAttribute};
use ruff_text_size::TextSize; use ruff_text_size::TextSize;
use rustpython_parser::ast::{self, Stmt, StmtKind, Suite}; use rustpython_parser::ast::{self, Ranged, Stmt, Suite};
use rustpython_parser::{lexer, Mode, Tok}; use rustpython_parser::{lexer, Mode, Tok};
use ruff_diagnostics::Edit; use ruff_diagnostics::Edit;
@ -71,7 +71,7 @@ impl<'a> Importer<'a> {
} }
} }
/// Return the top-level [`Stmt`] that imports the given module using `StmtKind::ImportFrom` /// Return the top-level [`Stmt`] that imports the given module using `Stmt::ImportFrom`
/// preceding the given position, if any. /// preceding the given position, if any.
pub fn find_import_from(&self, module: &str, at: TextSize) -> Option<&Stmt> { pub fn find_import_from(&self, module: &str, at: TextSize) -> Option<&Stmt> {
let mut import_from = None; let mut import_from = None;
@ -79,11 +79,11 @@ impl<'a> Importer<'a> {
if stmt.start() >= at { if stmt.start() >= at {
break; break;
} }
if let StmtKind::ImportFrom(ast::StmtImportFrom { if let Stmt::ImportFrom(ast::StmtImportFrom {
module: name, module: name,
level, level,
.. ..
}) = &stmt.node }) = stmt
{ {
if level.map_or(true, |level| level.to_u32() == 0) if level.map_or(true, |level| level.to_u32() == 0)
&& name.as_ref().map_or(false, |name| name == module) && name.as_ref().map_or(false, |name| name == module)
@ -95,7 +95,7 @@ impl<'a> Importer<'a> {
import_from import_from
} }
/// Add the given member to an existing `StmtKind::ImportFrom` statement. /// Add the given member to an existing `Stmt::ImportFrom` statement.
pub fn add_member(&self, stmt: &Stmt, member: &str) -> Result<Edit> { pub fn add_member(&self, stmt: &Stmt, member: &str) -> Result<Edit> {
let mut tree = match_module(self.locator.slice(stmt.range()))?; let mut tree = match_module(self.locator.slice(stmt.range()))?;
let import_from = match_import_from(&mut tree)?; let import_from = match_import_from(&mut tree)?;

View file

@ -1,5 +1,5 @@
use num_bigint::BigInt; use num_bigint::BigInt;
use rustpython_parser::ast::{self, Attributed, Cmpop, Constant, Expr, ExprKind}; use rustpython_parser::ast::{self, Cmpop, Constant, Expr, Ranged};
use ruff_diagnostics::{Diagnostic, Violation}; use ruff_diagnostics::{Diagnostic, Violation};
use ruff_macros::{derive_message_formats, violation}; use ruff_macros::{derive_message_formats, violation};
@ -123,16 +123,17 @@ fn is_sys(checker: &Checker, expr: &Expr, target: &str) -> bool {
/// YTT101, YTT102, YTT301, YTT303 /// YTT101, YTT102, YTT301, YTT303
pub(crate) fn subscript(checker: &mut Checker, value: &Expr, slice: &Expr) { pub(crate) fn subscript(checker: &mut Checker, value: &Expr, slice: &Expr) {
if is_sys(checker, value, "version") { if is_sys(checker, value, "version") {
match &slice.node { match slice {
ExprKind::Slice(ast::ExprSlice { Expr::Slice(ast::ExprSlice {
lower: None, lower: None,
upper: Some(upper), upper: Some(upper),
step: None, step: None,
range: _,
}) => { }) => {
if let ExprKind::Constant(ast::ExprConstant { if let Expr::Constant(ast::ExprConstant {
value: Constant::Int(i), value: Constant::Int(i),
.. ..
}) = &upper.node }) = upper.as_ref()
{ {
if *i == BigInt::from(1) if *i == BigInt::from(1)
&& checker.settings.rules.enabled(Rule::SysVersionSlice1) && checker.settings.rules.enabled(Rule::SysVersionSlice1)
@ -150,7 +151,7 @@ pub(crate) fn subscript(checker: &mut Checker, value: &Expr, slice: &Expr) {
} }
} }
ExprKind::Constant(ast::ExprConstant { Expr::Constant(ast::ExprConstant {
value: Constant::Int(i), value: Constant::Int(i),
.. ..
}) => { }) => {
@ -173,26 +174,22 @@ pub(crate) fn subscript(checker: &mut Checker, value: &Expr, slice: &Expr) {
/// YTT103, YTT201, YTT203, YTT204, YTT302 /// YTT103, YTT201, YTT203, YTT204, YTT302
pub(crate) fn compare(checker: &mut Checker, left: &Expr, ops: &[Cmpop], comparators: &[Expr]) { pub(crate) fn compare(checker: &mut Checker, left: &Expr, ops: &[Cmpop], comparators: &[Expr]) {
match &left.node { match left {
ExprKind::Subscript(ast::ExprSubscript { value, slice, .. }) Expr::Subscript(ast::ExprSubscript { value, slice, .. })
if is_sys(checker, value, "version_info") => if is_sys(checker, value, "version_info") =>
{ {
if let ExprKind::Constant(ast::ExprConstant { if let Expr::Constant(ast::ExprConstant {
value: Constant::Int(i), value: Constant::Int(i),
.. ..
}) = &slice.node }) = slice.as_ref()
{ {
if *i == BigInt::from(0) { if *i == BigInt::from(0) {
if let ( if let (
[Cmpop::Eq | Cmpop::NotEq], [Cmpop::Eq | Cmpop::NotEq],
[Attributed { [Expr::Constant(ast::ExprConstant {
node:
ExprKind::Constant(ast::ExprConstant {
value: Constant::Int(n), value: Constant::Int(n),
.. ..
}), })],
..
}],
) = (ops, comparators) ) = (ops, comparators)
{ {
if *n == BigInt::from(3) if *n == BigInt::from(3)
@ -206,14 +203,10 @@ pub(crate) fn compare(checker: &mut Checker, left: &Expr, ops: &[Cmpop], compara
} else if *i == BigInt::from(1) { } else if *i == BigInt::from(1) {
if let ( if let (
[Cmpop::Lt | Cmpop::LtE | Cmpop::Gt | Cmpop::GtE], [Cmpop::Lt | Cmpop::LtE | Cmpop::Gt | Cmpop::GtE],
[Attributed { [Expr::Constant(ast::ExprConstant {
node:
ExprKind::Constant(ast::ExprConstant {
value: Constant::Int(_), value: Constant::Int(_),
.. ..
}), })],
..
}],
) = (ops, comparators) ) = (ops, comparators)
{ {
if checker.settings.rules.enabled(Rule::SysVersionInfo1CmpInt) { if checker.settings.rules.enabled(Rule::SysVersionInfo1CmpInt) {
@ -226,19 +219,15 @@ pub(crate) fn compare(checker: &mut Checker, left: &Expr, ops: &[Cmpop], compara
} }
} }
ExprKind::Attribute(ast::ExprAttribute { value, attr, .. }) Expr::Attribute(ast::ExprAttribute { value, attr, .. })
if is_sys(checker, value, "version_info") && attr == "minor" => if is_sys(checker, value, "version_info") && attr == "minor" =>
{ {
if let ( if let (
[Cmpop::Lt | Cmpop::LtE | Cmpop::Gt | Cmpop::GtE], [Cmpop::Lt | Cmpop::LtE | Cmpop::Gt | Cmpop::GtE],
[Attributed { [Expr::Constant(ast::ExprConstant {
node:
ExprKind::Constant(ast::ExprConstant {
value: Constant::Int(_), value: Constant::Int(_),
.. ..
}), })],
..
}],
) = (ops, comparators) ) = (ops, comparators)
{ {
if checker if checker
@ -259,14 +248,10 @@ pub(crate) fn compare(checker: &mut Checker, left: &Expr, ops: &[Cmpop], compara
if is_sys(checker, left, "version") { if is_sys(checker, left, "version") {
if let ( if let (
[Cmpop::Lt | Cmpop::LtE | Cmpop::Gt | Cmpop::GtE], [Cmpop::Lt | Cmpop::LtE | Cmpop::Gt | Cmpop::GtE],
[Attributed { [Expr::Constant(ast::ExprConstant {
node:
ExprKind::Constant(ast::ExprConstant {
value: Constant::Str(s), value: Constant::Str(s),
.. ..
}), })],
..
}],
) = (ops, comparators) ) = (ops, comparators)
{ {
if s.len() == 1 { if s.len() == 1 {

View file

@ -1,5 +1,5 @@
use anyhow::{bail, Result}; use anyhow::{bail, Result};
use rustpython_parser::ast::Stmt; use rustpython_parser::ast::{Ranged, Stmt};
use rustpython_parser::{lexer, Mode, Tok}; use rustpython_parser::{lexer, Mode, Tok};
use ruff_diagnostics::Edit; use ruff_diagnostics::Edit;

View file

@ -1,4 +1,4 @@
use rustpython_parser::ast::{self, Arguments, Expr, Stmt, StmtKind}; use rustpython_parser::ast::{self, Arguments, Expr, Stmt};
use ruff_python_ast::cast; use ruff_python_ast::cast;
use ruff_python_semantic::analyze::visibility; use ruff_python_semantic::analyze::visibility;
@ -8,9 +8,9 @@ use crate::checkers::ast::Checker;
pub(super) fn match_function_def( pub(super) fn match_function_def(
stmt: &Stmt, stmt: &Stmt,
) -> (&str, &Arguments, Option<&Expr>, &Vec<Stmt>, &Vec<Expr>) { ) -> (&str, &Arguments, Option<&Expr>, &[Stmt], &[Expr]) {
match &stmt.node { match stmt {
StmtKind::FunctionDef(ast::StmtFunctionDef { Stmt::FunctionDef(ast::StmtFunctionDef {
name, name,
args, args,
returns, returns,
@ -18,7 +18,7 @@ pub(super) fn match_function_def(
decorator_list, decorator_list,
.. ..
}) })
| StmtKind::AsyncFunctionDef(ast::StmtAsyncFunctionDef { | Stmt::AsyncFunctionDef(ast::StmtAsyncFunctionDef {
name, name,
args, args,
returns, returns,

View file

@ -1,4 +1,4 @@
use rustpython_parser::ast::{Expr, ExprKind, Stmt}; use rustpython_parser::ast::{Expr, Ranged, Stmt};
use ruff_diagnostics::{AlwaysAutofixableViolation, Diagnostic, Violation}; use ruff_diagnostics::{AlwaysAutofixableViolation, Diagnostic, Violation};
use ruff_macros::{derive_message_formats, violation}; use ruff_macros::{derive_message_formats, violation};
@ -419,8 +419,8 @@ fn is_none_returning(body: &[Stmt]) -> bool {
visitor.visit_body(body); visitor.visit_body(body);
for expr in visitor.returns.into_iter().flatten() { for expr in visitor.returns.into_iter().flatten() {
if !matches!( if !matches!(
expr.node, expr,
ExprKind::Constant(ref constant) if constant.value.is_none() Expr::Constant(ref constant) if constant.value.is_none()
) { ) {
return false; return false;
} }
@ -495,20 +495,20 @@ pub(crate) fn definition(
) )
{ {
// ANN401 for dynamically typed arguments // ANN401 for dynamically typed arguments
if let Some(annotation) = &arg.node.annotation { if let Some(annotation) = &arg.annotation {
has_any_typed_arg = true; has_any_typed_arg = true;
if checker.settings.rules.enabled(Rule::AnyType) { if checker.settings.rules.enabled(Rule::AnyType) {
check_dynamically_typed( check_dynamically_typed(
checker, checker,
annotation, annotation,
|| arg.node.arg.to_string(), || arg.arg.to_string(),
&mut diagnostics, &mut diagnostics,
is_overridden, is_overridden,
); );
} }
} else { } else {
if !(checker.settings.flake8_annotations.suppress_dummy_args if !(checker.settings.flake8_annotations.suppress_dummy_args
&& checker.settings.dummy_variable_rgx.is_match(&arg.node.arg)) && checker.settings.dummy_variable_rgx.is_match(&arg.arg))
{ {
if checker if checker
.settings .settings
@ -517,7 +517,7 @@ pub(crate) fn definition(
{ {
diagnostics.push(Diagnostic::new( diagnostics.push(Diagnostic::new(
MissingTypeFunctionArgument { MissingTypeFunctionArgument {
name: arg.node.arg.to_string(), name: arg.arg.to_string(),
}, },
arg.range(), arg.range(),
)); ));
@ -528,11 +528,11 @@ pub(crate) fn definition(
// ANN002, ANN401 // ANN002, ANN401
if let Some(arg) = &args.vararg { if let Some(arg) = &args.vararg {
if let Some(expr) = &arg.node.annotation { if let Some(expr) = &arg.annotation {
has_any_typed_arg = true; has_any_typed_arg = true;
if !checker.settings.flake8_annotations.allow_star_arg_any { if !checker.settings.flake8_annotations.allow_star_arg_any {
if checker.settings.rules.enabled(Rule::AnyType) { if checker.settings.rules.enabled(Rule::AnyType) {
let name = &arg.node.arg; let name = &arg.arg;
check_dynamically_typed( check_dynamically_typed(
checker, checker,
expr, expr,
@ -544,12 +544,12 @@ pub(crate) fn definition(
} }
} else { } else {
if !(checker.settings.flake8_annotations.suppress_dummy_args if !(checker.settings.flake8_annotations.suppress_dummy_args
&& checker.settings.dummy_variable_rgx.is_match(&arg.node.arg)) && checker.settings.dummy_variable_rgx.is_match(&arg.arg))
{ {
if checker.settings.rules.enabled(Rule::MissingTypeArgs) { if checker.settings.rules.enabled(Rule::MissingTypeArgs) {
diagnostics.push(Diagnostic::new( diagnostics.push(Diagnostic::new(
MissingTypeArgs { MissingTypeArgs {
name: arg.node.arg.to_string(), name: arg.arg.to_string(),
}, },
arg.range(), arg.range(),
)); ));
@ -560,11 +560,11 @@ pub(crate) fn definition(
// ANN003, ANN401 // ANN003, ANN401
if let Some(arg) = &args.kwarg { if let Some(arg) = &args.kwarg {
if let Some(expr) = &arg.node.annotation { if let Some(expr) = &arg.annotation {
has_any_typed_arg = true; has_any_typed_arg = true;
if !checker.settings.flake8_annotations.allow_star_arg_any { if !checker.settings.flake8_annotations.allow_star_arg_any {
if checker.settings.rules.enabled(Rule::AnyType) { if checker.settings.rules.enabled(Rule::AnyType) {
let name = &arg.node.arg; let name = &arg.arg;
check_dynamically_typed( check_dynamically_typed(
checker, checker,
expr, expr,
@ -576,12 +576,12 @@ pub(crate) fn definition(
} }
} else { } else {
if !(checker.settings.flake8_annotations.suppress_dummy_args if !(checker.settings.flake8_annotations.suppress_dummy_args
&& checker.settings.dummy_variable_rgx.is_match(&arg.node.arg)) && checker.settings.dummy_variable_rgx.is_match(&arg.arg))
{ {
if checker.settings.rules.enabled(Rule::MissingTypeKwargs) { if checker.settings.rules.enabled(Rule::MissingTypeKwargs) {
diagnostics.push(Diagnostic::new( diagnostics.push(Diagnostic::new(
MissingTypeKwargs { MissingTypeKwargs {
name: arg.node.arg.to_string(), name: arg.arg.to_string(),
}, },
arg.range(), arg.range(),
)); ));
@ -593,12 +593,12 @@ pub(crate) fn definition(
// ANN101, ANN102 // ANN101, ANN102
if is_method && !visibility::is_staticmethod(&checker.ctx, cast::decorator_list(stmt)) { if is_method && !visibility::is_staticmethod(&checker.ctx, cast::decorator_list(stmt)) {
if let Some(arg) = args.posonlyargs.first().or_else(|| args.args.first()) { if let Some(arg) = args.posonlyargs.first().or_else(|| args.args.first()) {
if arg.node.annotation.is_none() { if arg.annotation.is_none() {
if visibility::is_classmethod(&checker.ctx, cast::decorator_list(stmt)) { if visibility::is_classmethod(&checker.ctx, cast::decorator_list(stmt)) {
if checker.settings.rules.enabled(Rule::MissingTypeCls) { if checker.settings.rules.enabled(Rule::MissingTypeCls) {
diagnostics.push(Diagnostic::new( diagnostics.push(Diagnostic::new(
MissingTypeCls { MissingTypeCls {
name: arg.node.arg.to_string(), name: arg.arg.to_string(),
}, },
arg.range(), arg.range(),
)); ));
@ -607,7 +607,7 @@ pub(crate) fn definition(
if checker.settings.rules.enabled(Rule::MissingTypeSelf) { if checker.settings.rules.enabled(Rule::MissingTypeSelf) {
diagnostics.push(Diagnostic::new( diagnostics.push(Diagnostic::new(
MissingTypeSelf { MissingTypeSelf {
name: arg.node.arg.to_string(), name: arg.arg.to_string(),
}, },
arg.range(), arg.range(),
)); ));

View file

@ -1,5 +1,5 @@
use rustpython_parser::ast; use rustpython_parser::ast;
use rustpython_parser::ast::{Expr, ExprKind}; use rustpython_parser::ast::{Expr, Ranged};
use ruff_diagnostics::{Diagnostic, Violation}; use ruff_diagnostics::{Diagnostic, Violation};
use ruff_macros::{derive_message_formats, violation}; use ruff_macros::{derive_message_formats, violation};
@ -67,12 +67,13 @@ const BLOCKING_HTTP_CALLS: &[&[&str]] = &[
/// ASYNC100 /// ASYNC100
pub(crate) fn blocking_http_call(checker: &mut Checker, expr: &Expr) { pub(crate) fn blocking_http_call(checker: &mut Checker, expr: &Expr) {
if in_async_function(&checker.ctx) { if in_async_function(&checker.ctx) {
if let ExprKind::Call(ast::ExprCall { func, .. }) = &expr.node { if let Expr::Call(ast::ExprCall { func, .. }) = expr {
if let Some(call_path) = checker.ctx.resolve_call_path(func) { if let Some(call_path) = checker.ctx.resolve_call_path(func) {
if BLOCKING_HTTP_CALLS.contains(&call_path.as_slice()) { if BLOCKING_HTTP_CALLS.contains(&call_path.as_slice()) {
checker checker.diagnostics.push(Diagnostic::new(
.diagnostics BlockingHttpCallInAsyncFunction,
.push(Diagnostic::new(BlockingHttpCallInAsyncFunction, func.range)); func.range(),
));
} }
} }
} }
@ -133,12 +134,12 @@ const OPEN_SLEEP_OR_SUBPROCESS_CALL: &[&[&str]] = &[
/// ASYNC101 /// ASYNC101
pub(crate) fn open_sleep_or_subprocess_call(checker: &mut Checker, expr: &Expr) { pub(crate) fn open_sleep_or_subprocess_call(checker: &mut Checker, expr: &Expr) {
if in_async_function(&checker.ctx) { if in_async_function(&checker.ctx) {
if let ExprKind::Call(ast::ExprCall { func, .. }) = &expr.node { if let Expr::Call(ast::ExprCall { func, .. }) = expr {
if let Some(call_path) = checker.ctx.resolve_call_path(func) { if let Some(call_path) = checker.ctx.resolve_call_path(func) {
if OPEN_SLEEP_OR_SUBPROCESS_CALL.contains(&call_path.as_slice()) { if OPEN_SLEEP_OR_SUBPROCESS_CALL.contains(&call_path.as_slice()) {
checker.diagnostics.push(Diagnostic::new( checker.diagnostics.push(Diagnostic::new(
OpenSleepOrSubprocessInAsyncFunction, OpenSleepOrSubprocessInAsyncFunction,
func.range, func.range(),
)); ));
} }
} }
@ -197,12 +198,12 @@ const UNSAFE_OS_METHODS: &[&[&str]] = &[
/// ASYNC102 /// ASYNC102
pub(crate) fn blocking_os_call(checker: &mut Checker, expr: &Expr) { pub(crate) fn blocking_os_call(checker: &mut Checker, expr: &Expr) {
if in_async_function(&checker.ctx) { if in_async_function(&checker.ctx) {
if let ExprKind::Call(ast::ExprCall { func, .. }) = &expr.node { if let Expr::Call(ast::ExprCall { func, .. }) = expr {
if let Some(call_path) = checker.ctx.resolve_call_path(func) { if let Some(call_path) = checker.ctx.resolve_call_path(func) {
if UNSAFE_OS_METHODS.contains(&call_path.as_slice()) { if UNSAFE_OS_METHODS.contains(&call_path.as_slice()) {
checker checker
.diagnostics .diagnostics
.push(Diagnostic::new(BlockingOsCallInAsyncFunction, func.range)); .push(Diagnostic::new(BlockingOsCallInAsyncFunction, func.range()));
} }
} }
} }

View file

@ -1,6 +1,6 @@
use once_cell::sync::Lazy; use once_cell::sync::Lazy;
use regex::Regex; use regex::Regex;
use rustpython_parser::ast::{self, Constant, Expr, ExprKind}; use rustpython_parser::ast::{self, Constant, Expr};
use crate::checkers::ast::Checker; use crate::checkers::ast::Checker;
@ -9,8 +9,8 @@ static PASSWORD_CANDIDATE_REGEX: Lazy<Regex> = Lazy::new(|| {
}); });
pub(crate) fn string_literal(expr: &Expr) -> Option<&str> { pub(crate) fn string_literal(expr: &Expr) -> Option<&str> {
match &expr.node { match expr {
ExprKind::Constant(ast::ExprConstant { Expr::Constant(ast::ExprConstant {
value: Constant::Str(string), value: Constant::Str(string),
.. ..
}) => Some(string), }) => Some(string),
@ -24,7 +24,7 @@ pub(crate) fn matches_password_name(string: &str) -> bool {
pub(crate) fn is_untyped_exception(type_: Option<&Expr>, checker: &Checker) -> bool { pub(crate) fn is_untyped_exception(type_: Option<&Expr>, checker: &Checker) -> bool {
type_.map_or(true, |type_| { type_.map_or(true, |type_| {
if let ExprKind::Tuple(ast::ExprTuple { elts, .. }) = &type_.node { if let Expr::Tuple(ast::ExprTuple { elts, .. }) = &type_ {
elts.iter().any(|type_| { elts.iter().any(|type_| {
checker checker
.ctx .ctx

View file

@ -1,5 +1,5 @@
use ruff_text_size::{TextLen, TextRange}; use ruff_text_size::{TextLen, TextRange};
use rustpython_parser::ast::Stmt; use rustpython_parser::ast::{Ranged, Stmt};
use ruff_diagnostics::{Diagnostic, Violation}; use ruff_diagnostics::{Diagnostic, Violation};
use ruff_macros::{derive_message_formats, violation}; use ruff_macros::{derive_message_formats, violation};

View file

@ -1,7 +1,7 @@
use num_traits::ToPrimitive; use num_traits::ToPrimitive;
use once_cell::sync::Lazy; use once_cell::sync::Lazy;
use rustc_hash::FxHashMap; use rustc_hash::FxHashMap;
use rustpython_parser::ast::{self, Constant, Expr, ExprKind, Keyword, Operator}; use rustpython_parser::ast::{self, Constant, Expr, Keyword, Operator, Ranged};
use ruff_diagnostics::{Diagnostic, Violation}; use ruff_diagnostics::{Diagnostic, Violation};
use ruff_macros::{derive_message_formats, violation}; use ruff_macros::{derive_message_formats, violation};
@ -69,15 +69,20 @@ static PYSTAT_MAPPING: Lazy<FxHashMap<&'static str, u16>> = Lazy::new(|| {
}); });
fn get_int_value(expr: &Expr) -> Option<u16> { fn get_int_value(expr: &Expr) -> Option<u16> {
match &expr.node { match expr {
ExprKind::Constant(ast::ExprConstant { Expr::Constant(ast::ExprConstant {
value: Constant::Int(value), value: Constant::Int(value),
.. ..
}) => value.to_u16(), }) => value.to_u16(),
ExprKind::Attribute(_) => { Expr::Attribute(_) => {
compose_call_path(expr).and_then(|path| PYSTAT_MAPPING.get(path.as_str()).copied()) compose_call_path(expr).and_then(|path| PYSTAT_MAPPING.get(path.as_str()).copied())
} }
ExprKind::BinOp(ast::ExprBinOp { left, op, right }) => { Expr::BinOp(ast::ExprBinOp {
left,
op,
right,
range: _,
}) => {
if let (Some(left_value), Some(right_value)) = if let (Some(left_value), Some(right_value)) =
(get_int_value(left), get_int_value(right)) (get_int_value(left), get_int_value(right))
{ {

View file

@ -1,4 +1,4 @@
use rustpython_parser::ast::{self, Expr, ExprKind}; use rustpython_parser::ast::{self, Expr, Ranged};
use ruff_diagnostics::{Diagnostic, Violation}; use ruff_diagnostics::{Diagnostic, Violation};
use ruff_macros::{derive_message_formats, violation}; use ruff_macros::{derive_message_formats, violation};
@ -15,7 +15,7 @@ impl Violation for ExecBuiltin {
/// S102 /// S102
pub(crate) fn exec_used(expr: &Expr, func: &Expr) -> Option<Diagnostic> { pub(crate) fn exec_used(expr: &Expr, func: &Expr) -> Option<Diagnostic> {
let ExprKind::Name(ast::ExprName { id, .. }) = &func.node else { let Expr::Name(ast::ExprName { id, .. }) = func else {
return None; return None;
}; };
if id != "exec" { if id != "exec" {

View file

@ -1,4 +1,4 @@
use rustpython_parser::ast::{Arg, Arguments, Expr}; use rustpython_parser::ast::{Arg, Arguments, Expr, Ranged};
use ruff_diagnostics::{Diagnostic, Violation}; use ruff_diagnostics::{Diagnostic, Violation};
use ruff_macros::{derive_message_formats, violation}; use ruff_macros::{derive_message_formats, violation};
@ -23,7 +23,7 @@ impl Violation for HardcodedPasswordDefault {
fn check_password_kwarg(arg: &Arg, default: &Expr) -> Option<Diagnostic> { fn check_password_kwarg(arg: &Arg, default: &Expr) -> Option<Diagnostic> {
string_literal(default).filter(|string| !string.is_empty())?; string_literal(default).filter(|string| !string.is_empty())?;
let kwarg_name = &arg.node.arg; let kwarg_name = &arg.arg;
if !matches_password_name(kwarg_name) { if !matches_password_name(kwarg_name) {
return None; return None;
} }

View file

@ -1,4 +1,4 @@
use rustpython_parser::ast::Keyword; use rustpython_parser::ast::{Keyword, Ranged};
use ruff_diagnostics::{Diagnostic, Violation}; use ruff_diagnostics::{Diagnostic, Violation};
use ruff_macros::{derive_message_formats, violation}; use ruff_macros::{derive_message_formats, violation};
@ -26,8 +26,8 @@ pub(crate) fn hardcoded_password_func_arg(keywords: &[Keyword]) -> Vec<Diagnosti
keywords keywords
.iter() .iter()
.filter_map(|keyword| { .filter_map(|keyword| {
string_literal(&keyword.node.value).filter(|string| !string.is_empty())?; string_literal(&keyword.value).filter(|string| !string.is_empty())?;
let arg = keyword.node.arg.as_ref()?; let arg = keyword.arg.as_ref()?;
if !matches_password_name(arg) { if !matches_password_name(arg) {
return None; return None;
} }

View file

@ -1,4 +1,4 @@
use rustpython_parser::ast::{self, Constant, Expr, ExprKind}; use rustpython_parser::ast::{self, Constant, Expr, Ranged};
use ruff_diagnostics::{Diagnostic, Violation}; use ruff_diagnostics::{Diagnostic, Violation};
use ruff_macros::{derive_message_formats, violation}; use ruff_macros::{derive_message_formats, violation};
@ -22,19 +22,19 @@ impl Violation for HardcodedPasswordString {
} }
fn password_target(target: &Expr) -> Option<&str> { fn password_target(target: &Expr) -> Option<&str> {
let target_name = match &target.node { let target_name = match target {
// variable = "s3cr3t" // variable = "s3cr3t"
ExprKind::Name(ast::ExprName { id, .. }) => id.as_str(), Expr::Name(ast::ExprName { id, .. }) => id.as_str(),
// d["password"] = "s3cr3t" // d["password"] = "s3cr3t"
ExprKind::Subscript(ast::ExprSubscript { slice, .. }) => match &slice.node { Expr::Subscript(ast::ExprSubscript { slice, .. }) => match slice.as_ref() {
ExprKind::Constant(ast::ExprConstant { Expr::Constant(ast::ExprConstant {
value: Constant::Str(string), value: Constant::Str(string),
.. ..
}) => string, }) => string,
_ => return None, _ => return None,
}, },
// obj.password = "s3cr3t" // obj.password = "s3cr3t"
ExprKind::Attribute(ast::ExprAttribute { attr, .. }) => attr, Expr::Attribute(ast::ExprAttribute { attr, .. }) => attr,
_ => return None, _ => return None,
}; };

View file

@ -1,6 +1,6 @@
use once_cell::sync::Lazy; use once_cell::sync::Lazy;
use regex::Regex; use regex::Regex;
use rustpython_parser::ast::{self, Expr, ExprKind, Operator}; use rustpython_parser::ast::{self, Expr, Operator, Ranged};
use ruff_diagnostics::{Diagnostic, Violation}; use ruff_diagnostics::{Diagnostic, Violation};
use ruff_macros::{derive_message_formats, violation}; use ruff_macros::{derive_message_formats, violation};
@ -53,10 +53,10 @@ fn matches_sql_statement(string: &str) -> bool {
} }
fn unparse_string_format_expression(checker: &mut Checker, expr: &Expr) -> Option<String> { fn unparse_string_format_expression(checker: &mut Checker, expr: &Expr) -> Option<String> {
match &expr.node { match expr {
// "select * from table where val = " + "str" + ... // "select * from table where val = " + "str" + ...
// "select * from table where val = %s" % ... // "select * from table where val = %s" % ...
ExprKind::BinOp(ast::ExprBinOp { Expr::BinOp(ast::ExprBinOp {
op: Operator::Add | Operator::Mod, op: Operator::Add | Operator::Mod,
.. ..
}) => { }) => {
@ -67,7 +67,7 @@ fn unparse_string_format_expression(checker: &mut Checker, expr: &Expr) -> Optio
return None; return None;
}; };
// Only evaluate the full BinOp, not the nested components. // Only evaluate the full BinOp, not the nested components.
let ExprKind::BinOp(_ )= &parent.node else { let Expr::BinOp(_ )= parent else {
if any_over_expr(expr, &has_string_literal) { if any_over_expr(expr, &has_string_literal) {
return Some(unparse_expr(expr, checker.stylist)); return Some(unparse_expr(expr, checker.stylist));
} }
@ -75,8 +75,8 @@ fn unparse_string_format_expression(checker: &mut Checker, expr: &Expr) -> Optio
}; };
None None
} }
ExprKind::Call(ast::ExprCall { func, .. }) => { Expr::Call(ast::ExprCall { func, .. }) => {
let ExprKind::Attribute(ast::ExprAttribute { attr, value, .. }) = &func.node else { let Expr::Attribute(ast::ExprAttribute { attr, value, .. }) = func.as_ref() else {
return None; return None;
}; };
// "select * from table where val = {}".format(...) // "select * from table where val = {}".format(...)
@ -86,7 +86,7 @@ fn unparse_string_format_expression(checker: &mut Checker, expr: &Expr) -> Optio
None None
} }
// f"select * from table where val = {val}" // f"select * from table where val = {val}"
ExprKind::JoinedStr(_) => Some(unparse_expr(expr, checker.stylist)), Expr::JoinedStr(_) => Some(unparse_expr(expr, checker.stylist)),
_ => None, _ => None,
} }
} }

View file

@ -1,4 +1,4 @@
use rustpython_parser::ast::Expr; use rustpython_parser::ast::{Expr, Ranged};
use ruff_diagnostics::{Diagnostic, Violation}; use ruff_diagnostics::{Diagnostic, Violation};
use ruff_macros::{derive_message_formats, violation}; use ruff_macros::{derive_message_formats, violation};

View file

@ -1,4 +1,4 @@
use rustpython_parser::ast::{self, Constant, Expr, ExprKind, Keyword}; use rustpython_parser::ast::{self, Constant, Expr, Keyword, Ranged};
use ruff_diagnostics::{Diagnostic, Violation}; use ruff_diagnostics::{Diagnostic, Violation};
use ruff_macros::{derive_message_formats, violation}; use ruff_macros::{derive_message_formats, violation};
@ -26,8 +26,8 @@ const WEAK_HASHES: [&str; 4] = ["md4", "md5", "sha", "sha1"];
fn is_used_for_security(call_args: &SimpleCallArgs) -> bool { fn is_used_for_security(call_args: &SimpleCallArgs) -> bool {
match call_args.keyword_argument("usedforsecurity") { match call_args.keyword_argument("usedforsecurity") {
Some(expr) => !matches!( Some(expr) => !matches!(
&expr.node, expr,
ExprKind::Constant(ast::ExprConstant { Expr::Constant(ast::ExprConstant {
value: Constant::Bool(false), value: Constant::Bool(false),
.. ..
}) })

View file

@ -1,4 +1,4 @@
use rustpython_parser::ast::{self, Constant, Expr, ExprKind, Keyword}; use rustpython_parser::ast::{self, Constant, Expr, Keyword, Ranged};
use ruff_diagnostics::{Diagnostic, Violation}; use ruff_diagnostics::{Diagnostic, Violation};
use ruff_macros::{derive_message_formats, violation}; use ruff_macros::{derive_message_formats, violation};
@ -46,13 +46,13 @@ pub(crate) fn jinja2_autoescape_false(
let call_args = SimpleCallArgs::new(args, keywords); let call_args = SimpleCallArgs::new(args, keywords);
if let Some(autoescape_arg) = call_args.keyword_argument("autoescape") { if let Some(autoescape_arg) = call_args.keyword_argument("autoescape") {
match &autoescape_arg.node { match autoescape_arg {
ExprKind::Constant(ast::ExprConstant { Expr::Constant(ast::ExprConstant {
value: Constant::Bool(true), value: Constant::Bool(true),
.. ..
}) => (), }) => (),
ExprKind::Call(ast::ExprCall { func, .. }) => { Expr::Call(ast::ExprCall { func, .. }) => {
if let ExprKind::Name(ast::ExprName { id, .. }) = &func.node { if let Expr::Name(ast::ExprName { id, .. }) = func.as_ref() {
if id != "select_autoescape" { if id != "select_autoescape" {
checker.diagnostics.push(Diagnostic::new( checker.diagnostics.push(Diagnostic::new(
Jinja2AutoescapeFalse { value: true }, Jinja2AutoescapeFalse { value: true },

View file

@ -1,4 +1,4 @@
use rustpython_parser::ast::{Expr, Keyword}; use rustpython_parser::ast::{Expr, Keyword, Ranged};
use ruff_diagnostics::{Diagnostic, Violation}; use ruff_diagnostics::{Diagnostic, Violation};
use ruff_macros::{derive_message_formats, violation}; use ruff_macros::{derive_message_formats, violation};

View file

@ -1,4 +1,4 @@
use rustpython_parser::ast::{self, Constant, Expr, ExprKind, Keyword}; use rustpython_parser::ast::{self, Constant, Expr, Keyword, Ranged};
use ruff_diagnostics::{Diagnostic, Violation}; use ruff_diagnostics::{Diagnostic, Violation};
use ruff_macros::{derive_message_formats, violation}; use ruff_macros::{derive_message_formats, violation};
@ -56,10 +56,10 @@ pub(crate) fn request_with_no_cert_validation(
}) { }) {
let call_args = SimpleCallArgs::new(args, keywords); let call_args = SimpleCallArgs::new(args, keywords);
if let Some(verify_arg) = call_args.keyword_argument("verify") { if let Some(verify_arg) = call_args.keyword_argument("verify") {
if let ExprKind::Constant(ast::ExprConstant { if let Expr::Constant(ast::ExprConstant {
value: Constant::Bool(false), value: Constant::Bool(false),
.. ..
}) = &verify_arg.node }) = &verify_arg
{ {
checker.diagnostics.push(Diagnostic::new( checker.diagnostics.push(Diagnostic::new(
RequestWithNoCertValidation { RequestWithNoCertValidation {

View file

@ -1,4 +1,4 @@
use rustpython_parser::ast::{self, Constant, Expr, ExprKind, Keyword}; use rustpython_parser::ast::{self, Constant, Expr, Keyword, Ranged};
use ruff_diagnostics::{Diagnostic, Violation}; use ruff_diagnostics::{Diagnostic, Violation};
use ruff_macros::{derive_message_formats, violation}; use ruff_macros::{derive_message_formats, violation};
@ -44,8 +44,8 @@ pub(crate) fn request_without_timeout(
{ {
let call_args = SimpleCallArgs::new(args, keywords); let call_args = SimpleCallArgs::new(args, keywords);
if let Some(timeout_arg) = call_args.keyword_argument("timeout") { if let Some(timeout_arg) = call_args.keyword_argument("timeout") {
if let Some(timeout) = match &timeout_arg.node { if let Some(timeout) = match timeout_arg {
ExprKind::Constant(ast::ExprConstant { Expr::Constant(ast::ExprConstant {
value: value @ Constant::None, value: value @ Constant::None,
.. ..
}) => Some(unparse_constant(value, checker.stylist)), }) => Some(unparse_constant(value, checker.stylist)),

View file

@ -2,7 +2,7 @@
use once_cell::sync::Lazy; use once_cell::sync::Lazy;
use regex::Regex; use regex::Regex;
use rustpython_parser::ast::{self, Constant, Expr, ExprKind, Keyword}; use rustpython_parser::ast::{self, Constant, Expr, Keyword, Ranged};
use ruff_diagnostics::{Diagnostic, Violation}; use ruff_diagnostics::{Diagnostic, Violation};
use ruff_macros::{derive_message_formats, violation}; use ruff_macros::{derive_message_formats, violation};
@ -141,15 +141,9 @@ struct ShellKeyword<'a> {
fn find_shell_keyword<'a>(ctx: &Context, keywords: &'a [Keyword]) -> Option<ShellKeyword<'a>> { fn find_shell_keyword<'a>(ctx: &Context, keywords: &'a [Keyword]) -> Option<ShellKeyword<'a>> {
keywords keywords
.iter() .iter()
.find(|keyword| { .find(|keyword| keyword.arg.as_ref().map_or(false, |arg| arg == "shell"))
keyword
.node
.arg
.as_ref()
.map_or(false, |arg| arg == "shell")
})
.map(|keyword| ShellKeyword { .map(|keyword| ShellKeyword {
truthiness: Truthiness::from_expr(&keyword.node.value, |id| ctx.is_builtin(id)), truthiness: Truthiness::from_expr(&keyword.value, |id| ctx.is_builtin(id)),
keyword, keyword,
}) })
} }
@ -158,8 +152,8 @@ fn find_shell_keyword<'a>(ctx: &Context, keywords: &'a [Keyword]) -> Option<Shel
/// definition: string literals are considered okay, but dynamically-computed values are not. /// definition: string literals are considered okay, but dynamically-computed values are not.
fn shell_call_seems_safe(arg: &Expr) -> bool { fn shell_call_seems_safe(arg: &Expr) -> bool {
matches!( matches!(
arg.node, arg,
ExprKind::Constant(ast::ExprConstant { Expr::Constant(ast::ExprConstant {
value: Constant::Str(_), value: Constant::Str(_),
.. ..
}) })
@ -168,8 +162,8 @@ fn shell_call_seems_safe(arg: &Expr) -> bool {
/// Return the [`Expr`] as a string literal, if it's a string or a list of strings. /// Return the [`Expr`] as a string literal, if it's a string or a list of strings.
fn try_string_literal(expr: &Expr) -> Option<&str> { fn try_string_literal(expr: &Expr) -> Option<&str> {
match &expr.node { match expr {
ExprKind::List(ast::ExprList { elts, .. }) => { Expr::List(ast::ExprList { elts, .. }) => {
if elts.is_empty() { if elts.is_empty() {
None None
} else { } else {

View file

@ -1,5 +1,5 @@
use num_traits::{One, Zero}; use num_traits::{One, Zero};
use rustpython_parser::ast::{self, Constant, Expr, ExprKind, Keyword}; use rustpython_parser::ast::{self, Constant, Expr, Keyword, Ranged};
use ruff_diagnostics::{Diagnostic, Violation}; use ruff_diagnostics::{Diagnostic, Violation};
use ruff_macros::{derive_message_formats, violation}; use ruff_macros::{derive_message_formats, violation};
@ -33,10 +33,10 @@ pub(crate) fn snmp_insecure_version(
{ {
let call_args = SimpleCallArgs::new(args, keywords); let call_args = SimpleCallArgs::new(args, keywords);
if let Some(mp_model_arg) = call_args.keyword_argument("mpModel") { if let Some(mp_model_arg) = call_args.keyword_argument("mpModel") {
if let ExprKind::Constant(ast::ExprConstant { if let Expr::Constant(ast::ExprConstant {
value: Constant::Int(value), value: Constant::Int(value),
.. ..
}) = &mp_model_arg.node }) = &mp_model_arg
{ {
if value.is_zero() || value.is_one() { if value.is_zero() || value.is_one() {
checker checker

View file

@ -1,4 +1,4 @@
use rustpython_parser::ast::{Expr, Keyword}; use rustpython_parser::ast::{Expr, Keyword, Ranged};
use ruff_diagnostics::{Diagnostic, Violation}; use ruff_diagnostics::{Diagnostic, Violation};
use ruff_macros::{derive_message_formats, violation}; use ruff_macros::{derive_message_formats, violation};

View file

@ -1,7 +1,7 @@
//! Check for calls to suspicious functions, or calls into suspicious modules. //! Check for calls to suspicious functions, or calls into suspicious modules.
//! //!
//! See: <https://bandit.readthedocs.io/en/latest/blacklists/blacklist_calls.html> //! See: <https://bandit.readthedocs.io/en/latest/blacklists/blacklist_calls.html>
use rustpython_parser::ast::{self, Expr, ExprKind}; use rustpython_parser::ast::{self, Expr, Ranged};
use ruff_diagnostics::{Diagnostic, DiagnosticKind, Violation}; use ruff_diagnostics::{Diagnostic, DiagnosticKind, Violation};
use ruff_macros::{derive_message_formats, violation}; use ruff_macros::{derive_message_formats, violation};
@ -466,7 +466,7 @@ const SUSPICIOUS_MODULES: &[SuspiciousModule] = &[
/// S001 /// S001
pub(crate) fn suspicious_function_call(checker: &mut Checker, expr: &Expr) { pub(crate) fn suspicious_function_call(checker: &mut Checker, expr: &Expr) {
let ExprKind::Call(ast::ExprCall { func, .. }) = &expr.node else { let Expr::Call(ast::ExprCall { func, .. }) = expr else {
return; return;
}; };

View file

@ -1,4 +1,4 @@
use rustpython_parser::ast::{Excepthandler, Expr, Stmt, StmtKind}; use rustpython_parser::ast::{Excepthandler, Expr, Ranged, Stmt};
use ruff_diagnostics::{Diagnostic, Violation}; use ruff_diagnostics::{Diagnostic, Violation};
use ruff_macros::{derive_message_formats, violation}; use ruff_macros::{derive_message_formats, violation};
@ -26,7 +26,7 @@ pub(crate) fn try_except_continue(
check_typed_exception: bool, check_typed_exception: bool,
) { ) {
if body.len() == 1 if body.len() == 1
&& body[0].node == StmtKind::Continue && body[0].is_continue_stmt()
&& (check_typed_exception || is_untyped_exception(type_, checker)) && (check_typed_exception || is_untyped_exception(type_, checker))
{ {
checker checker

View file

@ -1,4 +1,4 @@
use rustpython_parser::ast::{Excepthandler, Expr, Stmt, StmtKind}; use rustpython_parser::ast::{Excepthandler, Expr, Ranged, Stmt};
use ruff_diagnostics::{Diagnostic, Violation}; use ruff_diagnostics::{Diagnostic, Violation};
use ruff_macros::{derive_message_formats, violation}; use ruff_macros::{derive_message_formats, violation};
@ -26,7 +26,7 @@ pub(crate) fn try_except_pass(
check_typed_exception: bool, check_typed_exception: bool,
) { ) {
if body.len() == 1 if body.len() == 1
&& body[0].node == StmtKind::Pass && body[0].is_pass_stmt()
&& (check_typed_exception || is_untyped_exception(type_, checker)) && (check_typed_exception || is_untyped_exception(type_, checker))
{ {
checker checker

View file

@ -1,4 +1,4 @@
use rustpython_parser::ast::{self, Expr, ExprKind, Keyword}; use rustpython_parser::ast::{self, Expr, Keyword, Ranged};
use ruff_diagnostics::{Diagnostic, Violation}; use ruff_diagnostics::{Diagnostic, Violation};
use ruff_macros::{derive_message_formats, violation}; use ruff_macros::{derive_message_formats, violation};
@ -52,9 +52,9 @@ pub(crate) fn unsafe_yaml_load(
|| call_path.as_slice() == ["yaml", "CSafeLoader"] || call_path.as_slice() == ["yaml", "CSafeLoader"]
}) })
{ {
let loader = match &loader_arg.node { let loader = match loader_arg {
ExprKind::Attribute(ast::ExprAttribute { attr, .. }) => Some(attr.to_string()), Expr::Attribute(ast::ExprAttribute { attr, .. }) => Some(attr.to_string()),
ExprKind::Name(ast::ExprName { id, .. }) => Some(id.to_string()), Expr::Name(ast::ExprName { id, .. }) => Some(id.to_string()),
_ => None, _ => None,
}; };
checker.diagnostics.push(Diagnostic::new( checker.diagnostics.push(Diagnostic::new(

View file

@ -1,4 +1,4 @@
use rustpython_parser::ast::{self, Expr, ExprKind, Stmt, StmtKind}; use rustpython_parser::ast::{self, Expr, Ranged, Stmt};
use ruff_diagnostics::{Diagnostic, Violation}; use ruff_diagnostics::{Diagnostic, Violation};
use ruff_macros::{derive_message_formats, violation}; use ruff_macros::{derive_message_formats, violation};
@ -30,16 +30,16 @@ pub(crate) fn blind_except(
let Some(type_) = type_ else { let Some(type_) = type_ else {
return; return;
}; };
let ExprKind::Name(ast::ExprName { id, .. }) = &type_.node else { let Expr::Name(ast::ExprName { id, .. }) = &type_ else {
return; return;
}; };
for exception in ["BaseException", "Exception"] { for exception in ["BaseException", "Exception"] {
if id == exception && checker.ctx.is_builtin(exception) { if id == exception && checker.ctx.is_builtin(exception) {
// If the exception is re-raised, don't flag an error. // If the exception is re-raised, don't flag an error.
if body.iter().any(|stmt| { if body.iter().any(|stmt| {
if let StmtKind::Raise(ast::StmtRaise { exc, .. }) = &stmt.node { if let Stmt::Raise(ast::StmtRaise { exc, .. }) = stmt {
if let Some(exc) = exc { if let Some(exc) = exc {
if let ExprKind::Name(ast::ExprName { id, .. }) = &exc.node { if let Expr::Name(ast::ExprName { id, .. }) = exc.as_ref() {
name.map_or(false, |name| id == name) name.map_or(false, |name| id == name)
} else { } else {
false false
@ -56,17 +56,17 @@ pub(crate) fn blind_except(
// If the exception is logged, don't flag an error. // If the exception is logged, don't flag an error.
if body.iter().any(|stmt| { if body.iter().any(|stmt| {
if let StmtKind::Expr(ast::StmtExpr { value }) = &stmt.node { if let Stmt::Expr(ast::StmtExpr { value, range: _ }) = stmt {
if let ExprKind::Call(ast::ExprCall { func, keywords, .. }) = &value.node { if let Expr::Call(ast::ExprCall { func, keywords, .. }) = value.as_ref() {
if logging::is_logger_candidate(&checker.ctx, func) { if logging::is_logger_candidate(&checker.ctx, func) {
if let Some(attribute) = func.node.as_attribute_expr() { if let Some(attribute) = func.as_attribute_expr() {
let attr = attribute.attr.as_str(); let attr = attribute.attr.as_str();
if attr == "exception" { if attr == "exception" {
return true; return true;
} }
if attr == "error" { if attr == "error" {
if let Some(keyword) = find_keyword(keywords, "exc_info") { if let Some(keyword) = find_keyword(keywords, "exc_info") {
if is_const_true(&keyword.node.value) { if is_const_true(&keyword.value) {
return true; return true;
} }
} }

View file

@ -1,4 +1,4 @@
use rustpython_parser::ast::{self, Arguments, Constant, Expr, ExprKind}; use rustpython_parser::ast::{self, Arguments, Constant, Expr, Ranged};
use ruff_diagnostics::Violation; use ruff_diagnostics::Violation;
use ruff_diagnostics::{Diagnostic, DiagnosticKind}; use ruff_diagnostics::{Diagnostic, DiagnosticKind};
@ -71,11 +71,11 @@ const FUNC_DEF_NAME_ALLOWLIST: &[&str] = &["__setitem__"];
/// `true`, the function name must be explicitly allowed, and the argument must /// `true`, the function name must be explicitly allowed, and the argument must
/// be either the first or second argument in the call. /// be either the first or second argument in the call.
fn allow_boolean_trap(func: &Expr) -> bool { fn allow_boolean_trap(func: &Expr) -> bool {
if let ExprKind::Attribute(ast::ExprAttribute { attr, .. }) = &func.node { if let Expr::Attribute(ast::ExprAttribute { attr, .. }) = func {
return FUNC_CALL_NAME_ALLOWLIST.contains(&attr.as_ref()); return FUNC_CALL_NAME_ALLOWLIST.contains(&attr.as_ref());
} }
if let ExprKind::Name(ast::ExprName { id, .. }) = &func.node { if let Expr::Name(ast::ExprName { id, .. }) = func {
return FUNC_CALL_NAME_ALLOWLIST.contains(&id.as_ref()); return FUNC_CALL_NAME_ALLOWLIST.contains(&id.as_ref());
} }
@ -84,8 +84,8 @@ fn allow_boolean_trap(func: &Expr) -> bool {
const fn is_boolean_arg(arg: &Expr) -> bool { const fn is_boolean_arg(arg: &Expr) -> bool {
matches!( matches!(
&arg.node, &arg,
ExprKind::Constant(ast::ExprConstant { Expr::Constant(ast::ExprConstant {
value: Constant::Bool(_), value: Constant::Bool(_),
.. ..
}) })
@ -115,17 +115,17 @@ pub(crate) fn check_positional_boolean_in_def(
} }
for arg in arguments.posonlyargs.iter().chain(arguments.args.iter()) { for arg in arguments.posonlyargs.iter().chain(arguments.args.iter()) {
if arg.node.annotation.is_none() { if arg.annotation.is_none() {
continue; continue;
} }
let Some(expr) = &arg.node.annotation else { let Some(expr) = &arg.annotation else {
continue; continue;
}; };
// check for both bool (python class) and 'bool' (string annotation) // check for both bool (python class) and 'bool' (string annotation)
let hint = match &expr.node { let hint = match expr.as_ref() {
ExprKind::Name(name) => &name.id == "bool", Expr::Name(name) => &name.id == "bool",
ExprKind::Constant(ast::ExprConstant { Expr::Constant(ast::ExprConstant {
value: Constant::Str(value), value: Constant::Str(value),
.. ..
}) => value == "bool", }) => value == "bool",

View file

@ -1,4 +1,4 @@
use rustpython_parser::ast::{self, Constant, Expr, ExprKind, Keyword, Stmt, StmtKind}; use rustpython_parser::ast::{self, Constant, Expr, Keyword, Ranged, Stmt};
use ruff_diagnostics::{Diagnostic, Violation}; use ruff_diagnostics::{Diagnostic, Violation};
use ruff_macros::{derive_message_formats, violation}; use ruff_macros::{derive_message_formats, violation};
@ -37,13 +37,9 @@ impl Violation for EmptyMethodWithoutAbstractDecorator {
fn is_abc_class(context: &Context, bases: &[Expr], keywords: &[Keyword]) -> bool { fn is_abc_class(context: &Context, bases: &[Expr], keywords: &[Keyword]) -> bool {
keywords.iter().any(|keyword| { keywords.iter().any(|keyword| {
keyword keyword.arg.as_ref().map_or(false, |arg| arg == "metaclass")
.node
.arg
.as_ref()
.map_or(false, |arg| arg == "metaclass")
&& context && context
.resolve_call_path(&keyword.node.value) .resolve_call_path(&keyword.value)
.map_or(false, |call_path| { .map_or(false, |call_path| {
call_path.as_slice() == ["abc", "ABCMeta"] call_path.as_slice() == ["abc", "ABCMeta"]
}) })
@ -55,10 +51,13 @@ fn is_abc_class(context: &Context, bases: &[Expr], keywords: &[Keyword]) -> bool
} }
fn is_empty_body(body: &[Stmt]) -> bool { fn is_empty_body(body: &[Stmt]) -> bool {
body.iter().all(|stmt| match &stmt.node { body.iter().all(|stmt| match stmt {
StmtKind::Pass => true, Stmt::Pass(_) => true,
StmtKind::Expr(ast::StmtExpr { value }) => match &value.node { Stmt::Expr(ast::StmtExpr {
ExprKind::Constant(ast::ExprConstant { value, .. }) => { value,
range: _range,
}) => match value.as_ref() {
Expr::Constant(ast::ExprConstant { value, .. }) => {
matches!(value, Constant::Str(..) | Constant::Ellipsis) matches!(value, Constant::Str(..) | Constant::Ellipsis)
} }
_ => false, _ => false,
@ -88,24 +87,24 @@ pub(crate) fn abstract_base_class(
for stmt in body { for stmt in body {
// https://github.com/PyCQA/flake8-bugbear/issues/293 // https://github.com/PyCQA/flake8-bugbear/issues/293
// Ignore abc's that declares a class attribute that must be set // Ignore abc's that declares a class attribute that must be set
if let StmtKind::AnnAssign(_) | StmtKind::Assign(_) = &stmt.node { if let Stmt::AnnAssign(_) | Stmt::Assign(_) = stmt {
has_abstract_method = true; has_abstract_method = true;
continue; continue;
} }
let ( let (
StmtKind::FunctionDef(ast::StmtFunctionDef { Stmt::FunctionDef(ast::StmtFunctionDef {
decorator_list, decorator_list,
body, body,
name: method_name, name: method_name,
.. ..
}) | StmtKind::AsyncFunctionDef(ast::StmtAsyncFunctionDef { }) | Stmt::AsyncFunctionDef(ast::StmtAsyncFunctionDef {
decorator_list, decorator_list,
body, body,
name: method_name, name: method_name,
.. ..
}) })
) = &stmt.node else { ) = stmt else {
continue; continue;
}; };

View file

@ -1,5 +1,5 @@
use ruff_text_size::TextRange; use ruff_text_size::TextRange;
use rustpython_parser::ast::{self, Constant, Expr, ExprContext, ExprKind, Stmt, StmtKind}; use rustpython_parser::ast::{self, Constant, Expr, ExprContext, Ranged, Stmt};
use ruff_diagnostics::{AlwaysAutofixableViolation, Diagnostic, Edit, Fix}; use ruff_diagnostics::{AlwaysAutofixableViolation, Diagnostic, Edit, Fix};
use ruff_macros::{derive_message_formats, violation}; use ruff_macros::{derive_message_formats, violation};
@ -23,38 +23,32 @@ impl AlwaysAutofixableViolation for AssertFalse {
} }
fn assertion_error(msg: Option<&Expr>) -> Stmt { fn assertion_error(msg: Option<&Expr>) -> Stmt {
Stmt::new( Stmt::Raise(ast::StmtRaise {
TextRange::default(), range: TextRange::default(),
StmtKind::Raise(ast::StmtRaise { exc: Some(Box::new(Expr::Call(ast::ExprCall {
exc: Some(Box::new(Expr::new( func: Box::new(Expr::Name(ast::ExprName {
TextRange::default(),
ExprKind::Call(ast::ExprCall {
func: Box::new(Expr::new(
TextRange::default(),
ExprKind::Name(ast::ExprName {
id: "AssertionError".into(), id: "AssertionError".into(),
ctx: ExprContext::Load, ctx: ExprContext::Load,
}), range: TextRange::default(),
)), })),
args: if let Some(msg) = msg { args: if let Some(msg) = msg {
vec![msg.clone()] vec![msg.clone()]
} else { } else {
vec![] vec![]
}, },
keywords: vec![], keywords: vec![],
}), range: TextRange::default(),
))), }))),
cause: None, cause: None,
}), })
)
} }
/// B011 /// B011
pub(crate) fn assert_false(checker: &mut Checker, stmt: &Stmt, test: &Expr, msg: Option<&Expr>) { pub(crate) fn assert_false(checker: &mut Checker, stmt: &Stmt, test: &Expr, msg: Option<&Expr>) {
let ExprKind::Constant(ast::ExprConstant { let Expr::Constant(ast::ExprConstant {
value: Constant::Bool(false), value: Constant::Bool(false),
.. ..
} )= &test.node else { } )= &test else {
return; return;
}; };

View file

@ -1,4 +1,4 @@
use rustpython_parser::ast::{self, ExprKind, Stmt, Withitem}; use rustpython_parser::ast::{self, Expr, Ranged, Stmt, Withitem};
use ruff_diagnostics::{Diagnostic, Violation}; use ruff_diagnostics::{Diagnostic, Violation};
use ruff_macros::{derive_message_formats, violation}; use ruff_macros::{derive_message_formats, violation};
@ -55,7 +55,7 @@ pub(crate) fn assert_raises_exception(checker: &mut Checker, stmt: &Stmt, items:
return; return;
}; };
let item_context = &item.context_expr; let item_context = &item.context_expr;
let ExprKind::Call(ast::ExprCall { func, args, keywords }) = &item_context.node else { let Expr::Call(ast::ExprCall { func, args, keywords, range: _ }) = &item_context else {
return; return;
}; };
if args.len() != 1 { if args.len() != 1 {
@ -74,7 +74,7 @@ pub(crate) fn assert_raises_exception(checker: &mut Checker, stmt: &Stmt, items:
} }
let kind = { let kind = {
if matches!(&func.node, ExprKind::Attribute(ast::ExprAttribute { attr, .. }) if attr == "assertRaises") if matches!(func.as_ref(), Expr::Attribute(ast::ExprAttribute { attr, .. }) if attr == "assertRaises")
{ {
AssertionKind::AssertRaises AssertionKind::AssertRaises
} else if checker } else if checker
@ -83,13 +83,9 @@ pub(crate) fn assert_raises_exception(checker: &mut Checker, stmt: &Stmt, items:
.map_or(false, |call_path| { .map_or(false, |call_path| {
call_path.as_slice() == ["pytest", "raises"] call_path.as_slice() == ["pytest", "raises"]
}) })
&& !keywords.iter().any(|keyword| { && !keywords
keyword .iter()
.node .any(|keyword| keyword.arg.as_ref().map_or(false, |arg| arg == "match"))
.arg
.as_ref()
.map_or(false, |arg| arg == "match")
})
{ {
AssertionKind::PytestRaises AssertionKind::PytestRaises
} else { } else {

View file

@ -1,4 +1,4 @@
use rustpython_parser::ast::{self, Expr, ExprKind}; use rustpython_parser::ast::{self, Expr, Ranged};
use ruff_diagnostics::{Diagnostic, Violation}; use ruff_diagnostics::{Diagnostic, Violation};
use ruff_macros::{derive_message_formats, violation}; use ruff_macros::{derive_message_formats, violation};
@ -20,13 +20,13 @@ pub(crate) fn assignment_to_os_environ(checker: &mut Checker, targets: &[Expr])
return; return;
} }
let target = &targets[0]; let target = &targets[0];
let ExprKind::Attribute(ast::ExprAttribute { value, attr, .. }) = &target.node else { let Expr::Attribute(ast::ExprAttribute { value, attr, .. }) = target else {
return; return;
}; };
if attr != "environ" { if attr != "environ" {
return; return;
} }
let ExprKind::Name(ast::ExprName { id, .. } )= &value.node else { let Expr::Name(ast::ExprName { id, .. } )= value.as_ref() else {
return; return;
}; };
if id != "os" { if id != "os" {

View file

@ -1,4 +1,4 @@
use rustpython_parser::ast::{self, Expr, ExprKind}; use rustpython_parser::ast::{self, Expr, Ranged};
use ruff_diagnostics::{Diagnostic, Violation}; use ruff_diagnostics::{Diagnostic, Violation};
use ruff_macros::{derive_message_formats, violation}; use ruff_macros::{derive_message_formats, violation};
@ -36,7 +36,7 @@ pub(crate) fn cached_instance_method(checker: &mut Checker, decorator_list: &[Ex
for decorator in decorator_list { for decorator in decorator_list {
// TODO(charlie): This should take into account `classmethod-decorators` and // TODO(charlie): This should take into account `classmethod-decorators` and
// `staticmethod-decorators`. // `staticmethod-decorators`.
if let ExprKind::Name(ast::ExprName { id, .. }) = &decorator.node { if let Expr::Name(ast::ExprName { id, .. }) = decorator {
if id == "classmethod" || id == "staticmethod" { if id == "classmethod" || id == "staticmethod" {
return; return;
} }
@ -45,8 +45,8 @@ pub(crate) fn cached_instance_method(checker: &mut Checker, decorator_list: &[Ex
for decorator in decorator_list { for decorator in decorator_list {
if is_cache_func( if is_cache_func(
checker, checker,
match &decorator.node { match decorator {
ExprKind::Call(ast::ExprCall { func, .. }) => func, Expr::Call(ast::ExprCall { func, .. }) => func,
_ => decorator, _ => decorator,
}, },
) { ) {

View file

@ -1,4 +1,4 @@
use rustpython_parser::ast::{Expr, ExprKind}; use rustpython_parser::ast::{Expr, Ranged};
use ruff_diagnostics::{Diagnostic, Violation}; use ruff_diagnostics::{Diagnostic, Violation};
use ruff_macros::{derive_message_formats, violation}; use ruff_macros::{derive_message_formats, violation};
@ -17,7 +17,7 @@ impl Violation for CannotRaiseLiteral {
/// B016 /// B016
pub(crate) fn cannot_raise_literal(checker: &mut Checker, expr: &Expr) { pub(crate) fn cannot_raise_literal(checker: &mut Checker, expr: &Expr) {
let ExprKind::Constant ( _) = &expr.node else { let Expr::Constant ( _) = expr else {
return; return;
}; };
checker checker

View file

@ -1,7 +1,7 @@
use itertools::Itertools; use itertools::Itertools;
use ruff_text_size::TextRange; use ruff_text_size::TextRange;
use rustc_hash::{FxHashMap, FxHashSet}; use rustc_hash::{FxHashMap, FxHashSet};
use rustpython_parser::ast::{self, Excepthandler, ExcepthandlerKind, Expr, ExprContext, ExprKind}; use rustpython_parser::ast::{self, Excepthandler, Expr, ExprContext, Ranged};
use ruff_diagnostics::{AlwaysAutofixableViolation, Violation}; use ruff_diagnostics::{AlwaysAutofixableViolation, Violation};
use ruff_diagnostics::{Diagnostic, Edit, Fix}; use ruff_diagnostics::{Diagnostic, Edit, Fix};
@ -49,13 +49,12 @@ impl AlwaysAutofixableViolation for DuplicateHandlerException {
} }
fn type_pattern(elts: Vec<&Expr>) -> Expr { fn type_pattern(elts: Vec<&Expr>) -> Expr {
Expr::new(
TextRange::default(),
ast::ExprTuple { ast::ExprTuple {
elts: elts.into_iter().cloned().collect(), elts: elts.into_iter().cloned().collect(),
ctx: ExprContext::Load, ctx: ExprContext::Load,
}, range: TextRange::default(),
) }
.into()
} }
fn duplicate_handler_exceptions<'a>( fn duplicate_handler_exceptions<'a>(
@ -116,11 +115,11 @@ pub(crate) fn duplicate_exceptions(checker: &mut Checker, handlers: &[Excepthand
let mut seen: FxHashSet<CallPath> = FxHashSet::default(); let mut seen: FxHashSet<CallPath> = FxHashSet::default();
let mut duplicates: FxHashMap<CallPath, Vec<&Expr>> = FxHashMap::default(); let mut duplicates: FxHashMap<CallPath, Vec<&Expr>> = FxHashMap::default();
for handler in handlers { for handler in handlers {
let ExcepthandlerKind::ExceptHandler(ast::ExcepthandlerExceptHandler { type_: Some(type_), .. }) = &handler.node else { let Excepthandler::ExceptHandler(ast::ExcepthandlerExceptHandler { type_: Some(type_), .. }) = handler else {
continue; continue;
}; };
match &type_.node { match type_.as_ref() {
ExprKind::Attribute(_) | ExprKind::Name(_) => { Expr::Attribute(_) | Expr::Name(_) => {
if let Some(call_path) = call_path::collect_call_path(type_) { if let Some(call_path) = call_path::collect_call_path(type_) {
if seen.contains(&call_path) { if seen.contains(&call_path) {
duplicates.entry(call_path).or_default().push(type_); duplicates.entry(call_path).or_default().push(type_);
@ -129,7 +128,7 @@ pub(crate) fn duplicate_exceptions(checker: &mut Checker, handlers: &[Excepthand
} }
} }
} }
ExprKind::Tuple(ast::ExprTuple { elts, .. }) => { Expr::Tuple(ast::ExprTuple { elts, .. }) => {
for (name, expr) in duplicate_handler_exceptions(checker, type_, elts) { for (name, expr) in duplicate_handler_exceptions(checker, type_, elts) {
if seen.contains(&name) { if seen.contains(&name) {
duplicates.entry(name).or_default().push(expr); duplicates.entry(name).or_default().push(expr);

View file

@ -1,5 +1,5 @@
use rustpython_parser::ast::Excepthandler; use rustpython_parser::ast::{self, Ranged};
use rustpython_parser::ast::{self, ExcepthandlerKind, ExprKind}; use rustpython_parser::ast::{Excepthandler, Expr};
use ruff_diagnostics::{Diagnostic, Violation}; use ruff_diagnostics::{Diagnostic, Violation};
use ruff_macros::{derive_message_formats, violation}; use ruff_macros::{derive_message_formats, violation};
@ -18,12 +18,11 @@ impl Violation for ExceptWithEmptyTuple {
/// B029 /// B029
pub(crate) fn except_with_empty_tuple(checker: &mut Checker, excepthandler: &Excepthandler) { pub(crate) fn except_with_empty_tuple(checker: &mut Checker, excepthandler: &Excepthandler) {
let ExcepthandlerKind::ExceptHandler(ast::ExcepthandlerExceptHandler { type_, .. }) = let Excepthandler::ExceptHandler(ast::ExcepthandlerExceptHandler { type_, .. }) = excepthandler;
&excepthandler.node;
let Some(type_) = type_ else { let Some(type_) = type_ else {
return; return;
}; };
let ExprKind::Tuple(ast::ExprTuple { elts, .. }) = &type_.node else { let Expr::Tuple(ast::ExprTuple { elts, .. }) = type_.as_ref() else {
return; return;
}; };
if elts.is_empty() { if elts.is_empty() {

View file

@ -1,6 +1,6 @@
use std::collections::VecDeque; use std::collections::VecDeque;
use rustpython_parser::ast::{self, Excepthandler, ExcepthandlerKind, Expr, ExprKind}; use rustpython_parser::ast::{self, Excepthandler, Expr, Ranged};
use ruff_diagnostics::{Diagnostic, Violation}; use ruff_diagnostics::{Diagnostic, Violation};
use ruff_macros::{derive_message_formats, violation}; use ruff_macros::{derive_message_formats, violation};
@ -17,20 +17,20 @@ impl Violation for ExceptWithNonExceptionClasses {
} }
} }
/// Given an [`Expr`], flatten any [`ExprKind::Starred`] expressions. /// Given an [`Expr`], flatten any [`Expr::Starred`] expressions.
/// This should leave any unstarred iterables alone (subsequently raising a /// This should leave any unstarred iterables alone (subsequently raising a
/// warning for B029). /// warning for B029).
fn flatten_starred_iterables(expr: &Expr) -> Vec<&Expr> { fn flatten_starred_iterables(expr: &Expr) -> Vec<&Expr> {
let ExprKind::Tuple(ast::ExprTuple { elts, .. } )= &expr.node else { let Expr::Tuple(ast::ExprTuple { elts, .. } )= expr else {
return vec![expr]; return vec![expr];
}; };
let mut flattened_exprs: Vec<&Expr> = Vec::with_capacity(elts.len()); let mut flattened_exprs: Vec<&Expr> = Vec::with_capacity(elts.len());
let mut exprs_to_process: VecDeque<&Expr> = elts.iter().collect(); let mut exprs_to_process: VecDeque<&Expr> = elts.iter().collect();
while let Some(expr) = exprs_to_process.pop_front() { while let Some(expr) = exprs_to_process.pop_front() {
match &expr.node { match expr {
ExprKind::Starred(ast::ExprStarred { value, .. }) => match &value.node { Expr::Starred(ast::ExprStarred { value, .. }) => match value.as_ref() {
ExprKind::Tuple(ast::ExprTuple { elts, .. }) Expr::Tuple(ast::ExprTuple { elts, .. })
| ExprKind::List(ast::ExprList { elts, .. }) => { | Expr::List(ast::ExprList { elts, .. }) => {
exprs_to_process.append(&mut elts.iter().collect()); exprs_to_process.append(&mut elts.iter().collect());
} }
_ => flattened_exprs.push(value), _ => flattened_exprs.push(value),
@ -46,15 +46,14 @@ pub(crate) fn except_with_non_exception_classes(
checker: &mut Checker, checker: &mut Checker,
excepthandler: &Excepthandler, excepthandler: &Excepthandler,
) { ) {
let ExcepthandlerKind::ExceptHandler(ast::ExcepthandlerExceptHandler { type_, .. }) = let Excepthandler::ExceptHandler(ast::ExcepthandlerExceptHandler { type_, .. }) = excepthandler;
&excepthandler.node;
let Some(type_) = type_ else { let Some(type_) = type_ else {
return; return;
}; };
for expr in flatten_starred_iterables(type_) { for expr in flatten_starred_iterables(type_) {
if !matches!( if !matches!(
&expr.node, expr,
ExprKind::Subscript(_) | ExprKind::Attribute(_) | ExprKind::Name(_) | ExprKind::Call(_), Expr::Subscript(_) | Expr::Attribute(_) | Expr::Name(_) | Expr::Call(_),
) { ) {
checker checker
.diagnostics .diagnostics

View file

@ -1,4 +1,4 @@
use rustpython_parser::ast::{self, ExprKind, Stmt, StmtKind}; use rustpython_parser::ast::{self, Expr, Stmt};
use ruff_diagnostics::{Diagnostic, Violation}; use ruff_diagnostics::{Diagnostic, Violation};
use ruff_macros::{derive_message_formats, violation}; use ruff_macros::{derive_message_formats, violation};
@ -23,10 +23,10 @@ pub(crate) fn f_string_docstring(checker: &mut Checker, body: &[Stmt]) {
let Some(stmt) = body.first() else { let Some(stmt) = body.first() else {
return; return;
}; };
let StmtKind::Expr(ast::StmtExpr { value }) = &stmt.node else { let Stmt::Expr(ast::StmtExpr { value, range: _ }) = stmt else {
return; return;
}; };
let ExprKind::JoinedStr ( _) = value.node else { let Expr::JoinedStr ( _) = value.as_ref() else {
return; return;
}; };
checker.diagnostics.push(Diagnostic::new( checker.diagnostics.push(Diagnostic::new(

View file

@ -1,5 +1,5 @@
use ruff_text_size::TextRange; use ruff_text_size::TextRange;
use rustpython_parser::ast::{self, Arguments, Constant, Expr, ExprKind}; use rustpython_parser::ast::{self, Arguments, Constant, Expr, Ranged};
use ruff_diagnostics::Violation; use ruff_diagnostics::Violation;
use ruff_diagnostics::{Diagnostic, DiagnosticKind}; use ruff_diagnostics::{Diagnostic, DiagnosticKind};
@ -83,8 +83,8 @@ where
'b: 'a, 'b: 'a,
{ {
fn visit_expr(&mut self, expr: &'b Expr) { fn visit_expr(&mut self, expr: &'b Expr) {
match &expr.node { match expr {
ExprKind::Call(ast::ExprCall { func, args, .. }) => { Expr::Call(ast::ExprCall { func, args, .. }) => {
if !is_mutable_func(self.checker, func) if !is_mutable_func(self.checker, func)
&& !is_immutable_func(&self.checker.ctx, func, &self.extend_immutable_calls) && !is_immutable_func(&self.checker.ctx, func, &self.extend_immutable_calls)
&& !is_nan_or_infinity(func, args) && !is_nan_or_infinity(func, args)
@ -99,14 +99,14 @@ where
} }
visitor::walk_expr(self, expr); visitor::walk_expr(self, expr);
} }
ExprKind::Lambda(_) => {} Expr::Lambda(_) => {}
_ => visitor::walk_expr(self, expr), _ => visitor::walk_expr(self, expr),
} }
} }
} }
fn is_nan_or_infinity(expr: &Expr, args: &[Expr]) -> bool { fn is_nan_or_infinity(expr: &Expr, args: &[Expr]) -> bool {
let ExprKind::Name(ast::ExprName { id, .. }) = &expr.node else { let Expr::Name(ast::ExprName { id, .. }) = expr else {
return false; return false;
}; };
if id != "float" { if id != "float" {
@ -115,10 +115,10 @@ fn is_nan_or_infinity(expr: &Expr, args: &[Expr]) -> bool {
let Some(arg) = args.first() else { let Some(arg) = args.first() else {
return false; return false;
}; };
let ExprKind::Constant(ast::ExprConstant { let Expr::Constant(ast::ExprConstant {
value: Constant::Str(value), value: Constant::Str(value),
.. ..
} )= &arg.node else { } )= arg else {
return false; return false;
}; };
let lowercased = value.to_lowercase(); let lowercased = value.to_lowercase();

View file

@ -1,6 +1,5 @@
use ruff_text_size::TextRange;
use rustc_hash::FxHashSet; use rustc_hash::FxHashSet;
use rustpython_parser::ast::{self, Comprehension, Expr, ExprContext, ExprKind, Stmt, StmtKind}; use rustpython_parser::ast::{self, Comprehension, Expr, ExprContext, Ranged, Stmt};
use ruff_diagnostics::{Diagnostic, Violation}; use ruff_diagnostics::{Diagnostic, Violation};
use ruff_macros::{derive_message_formats, violation}; use ruff_macros::{derive_message_formats, violation};
@ -27,18 +26,18 @@ impl Violation for FunctionUsesLoopVariable {
#[derive(Default)] #[derive(Default)]
struct LoadedNamesVisitor<'a> { struct LoadedNamesVisitor<'a> {
// Tuple of: name, defining expression, and defining range. // Tuple of: name, defining expression, and defining range.
loaded: Vec<(&'a str, &'a Expr, TextRange)>, loaded: Vec<(&'a str, &'a Expr)>,
// Tuple of: name, defining expression, and defining range. // Tuple of: name, defining expression, and defining range.
stored: Vec<(&'a str, &'a Expr, TextRange)>, stored: Vec<(&'a str, &'a Expr)>,
} }
/// `Visitor` to collect all used identifiers in a statement. /// `Visitor` to collect all used identifiers in a statement.
impl<'a> Visitor<'a> for LoadedNamesVisitor<'a> { impl<'a> Visitor<'a> for LoadedNamesVisitor<'a> {
fn visit_expr(&mut self, expr: &'a Expr) { fn visit_expr(&mut self, expr: &'a Expr) {
match &expr.node { match expr {
ExprKind::Name(ast::ExprName { id, ctx }) => match ctx { Expr::Name(ast::ExprName { id, ctx, range: _ }) => match ctx {
ExprContext::Load => self.loaded.push((id, expr, expr.range())), ExprContext::Load => self.loaded.push((id, expr)),
ExprContext::Store => self.stored.push((id, expr, expr.range())), ExprContext::Store => self.stored.push((id, expr)),
ExprContext::Del => {} ExprContext::Del => {}
}, },
_ => visitor::walk_expr(self, expr), _ => visitor::walk_expr(self, expr),
@ -48,7 +47,7 @@ impl<'a> Visitor<'a> for LoadedNamesVisitor<'a> {
#[derive(Default)] #[derive(Default)]
struct SuspiciousVariablesVisitor<'a> { struct SuspiciousVariablesVisitor<'a> {
names: Vec<(&'a str, &'a Expr, TextRange)>, names: Vec<(&'a str, &'a Expr)>,
safe_functions: Vec<&'a Expr>, safe_functions: Vec<&'a Expr>,
} }
@ -56,9 +55,9 @@ struct SuspiciousVariablesVisitor<'a> {
/// functions, but not bound as arguments). /// functions, but not bound as arguments).
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.node { match stmt {
StmtKind::FunctionDef(ast::StmtFunctionDef { args, body, .. }) Stmt::FunctionDef(ast::StmtFunctionDef { args, body, .. })
| StmtKind::AsyncFunctionDef(ast::StmtAsyncFunctionDef { args, body, .. }) => { | Stmt::AsyncFunctionDef(ast::StmtAsyncFunctionDef { args, 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(body);
@ -76,9 +75,12 @@ impl<'a> Visitor<'a> for SuspiciousVariablesVisitor<'a> {
); );
return; return;
} }
StmtKind::Return(ast::StmtReturn { value: Some(value) }) => { Stmt::Return(ast::StmtReturn {
value: Some(value),
range: _,
}) => {
// Mark `return lambda: x` as safe. // Mark `return lambda: x` as safe.
if matches!(value.node, ExprKind::Lambda(_)) { if value.is_lambda_expr() {
self.safe_functions.push(value); self.safe_functions.push(value);
} }
} }
@ -88,28 +90,30 @@ impl<'a> Visitor<'a> for SuspiciousVariablesVisitor<'a> {
} }
fn visit_expr(&mut self, expr: &'a Expr) { fn visit_expr(&mut self, expr: &'a Expr) {
match &expr.node { match expr {
ExprKind::Call(ast::ExprCall { Expr::Call(ast::ExprCall {
func, func,
args, args,
keywords, keywords,
range: _,
}) => { }) => {
if let ExprKind::Name(ast::ExprName { id, .. }) = &func.node { match func.as_ref() {
Expr::Name(ast::ExprName { id, .. }) => {
let id = id.as_str(); let id = id.as_str();
if id == "filter" || id == "reduce" || id == "map" { if id == "filter" || id == "reduce" || id == "map" {
for arg in args { for arg in args {
if matches!(arg.node, ExprKind::Lambda(_)) { if matches!(arg, Expr::Lambda(_)) {
self.safe_functions.push(arg); self.safe_functions.push(arg);
} }
} }
} }
} }
if let ExprKind::Attribute(ast::ExprAttribute { value, attr, .. }) = &func.node { Expr::Attribute(ast::ExprAttribute { value, attr, .. }) => {
if attr == "reduce" { if attr == "reduce" {
if let ExprKind::Name(ast::ExprName { id, .. }) = &value.node { if let Expr::Name(ast::ExprName { id, .. }) = value.as_ref() {
if id == "functools" { if id == "functools" {
for arg in args { for arg in args {
if matches!(arg.node, ExprKind::Lambda(_)) { if arg.is_lambda_expr() {
self.safe_functions.push(arg); self.safe_functions.push(arg);
} }
} }
@ -117,15 +121,22 @@ impl<'a> Visitor<'a> for SuspiciousVariablesVisitor<'a> {
} }
} }
} }
_ => {}
}
for keyword in keywords { for keyword in keywords {
if keyword.node.arg.as_ref().map_or(false, |arg| arg == "key") if keyword.arg.as_ref().map_or(false, |arg| arg == "key")
&& matches!(keyword.node.value.node, ExprKind::Lambda(_)) && matches!(keyword.value, Expr::Lambda(_))
{ {
self.safe_functions.push(&keyword.node.value); self.safe_functions.push(&keyword.value);
} }
} }
} }
ExprKind::Lambda(ast::ExprLambda { args, body }) => { Expr::Lambda(ast::ExprLambda {
args,
body,
range: _,
}) => {
if !self.safe_functions.contains(&expr) { if !self.safe_functions.contains(&expr) {
// Collect all loaded variable names. // Collect all loaded variable names.
let mut visitor = LoadedNamesVisitor::default(); let mut visitor = LoadedNamesVisitor::default();
@ -160,15 +171,14 @@ struct NamesFromAssignmentsVisitor<'a> {
/// `Visitor` to collect all names used in an assignment expression. /// `Visitor` to collect all names used in an assignment expression.
impl<'a> Visitor<'a> for NamesFromAssignmentsVisitor<'a> { impl<'a> Visitor<'a> for NamesFromAssignmentsVisitor<'a> {
fn visit_expr(&mut self, expr: &'a Expr) { fn visit_expr(&mut self, expr: &'a Expr) {
match &expr.node { match expr {
ExprKind::Name(ast::ExprName { id, .. }) => { Expr::Name(ast::ExprName { id, .. }) => {
self.names.insert(id.as_str()); self.names.insert(id.as_str());
} }
ExprKind::Starred(ast::ExprStarred { value, .. }) => { Expr::Starred(ast::ExprStarred { value, .. }) => {
self.visit_expr(value); self.visit_expr(value);
} }
ExprKind::List(ast::ExprList { elts, .. }) Expr::List(ast::ExprList { elts, .. }) | Expr::Tuple(ast::ExprTuple { elts, .. }) => {
| ExprKind::Tuple(ast::ExprTuple { elts, .. }) => {
for expr in elts { for expr in elts {
self.visit_expr(expr); self.visit_expr(expr);
} }
@ -186,26 +196,23 @@ struct AssignedNamesVisitor<'a> {
/// `Visitor` to collect all used identifiers in a statement. /// `Visitor` to collect all used identifiers in a statement.
impl<'a> Visitor<'a> for AssignedNamesVisitor<'a> { impl<'a> Visitor<'a> for AssignedNamesVisitor<'a> {
fn visit_stmt(&mut self, stmt: &'a Stmt) { fn visit_stmt(&mut self, stmt: &'a Stmt) {
if matches!( if matches!(stmt, Stmt::FunctionDef(_) | Stmt::AsyncFunctionDef(_)) {
&stmt.node,
StmtKind::FunctionDef(_) | StmtKind::AsyncFunctionDef(_)
) {
// Don't recurse. // Don't recurse.
return; return;
} }
match &stmt.node { match stmt {
StmtKind::Assign(ast::StmtAssign { targets, .. }) => { Stmt::Assign(ast::StmtAssign { targets, .. }) => {
let mut visitor = NamesFromAssignmentsVisitor::default(); let mut visitor = NamesFromAssignmentsVisitor::default();
for expr in targets { for expr in targets {
visitor.visit_expr(expr); visitor.visit_expr(expr);
} }
self.names.extend(visitor.names); self.names.extend(visitor.names);
} }
StmtKind::AugAssign(ast::StmtAugAssign { target, .. }) Stmt::AugAssign(ast::StmtAugAssign { target, .. })
| StmtKind::AnnAssign(ast::StmtAnnAssign { target, .. }) | Stmt::AnnAssign(ast::StmtAnnAssign { target, .. })
| StmtKind::For(ast::StmtFor { target, .. }) | Stmt::For(ast::StmtFor { target, .. })
| StmtKind::AsyncFor(ast::StmtAsyncFor { target, .. }) => { | Stmt::AsyncFor(ast::StmtAsyncFor { target, .. }) => {
let mut visitor = NamesFromAssignmentsVisitor::default(); let mut visitor = NamesFromAssignmentsVisitor::default();
visitor.visit_expr(target); visitor.visit_expr(target);
self.names.extend(visitor.names); self.names.extend(visitor.names);
@ -217,7 +224,7 @@ impl<'a> Visitor<'a> for AssignedNamesVisitor<'a> {
} }
fn visit_expr(&mut self, expr: &'a Expr) { fn visit_expr(&mut self, expr: &'a Expr) {
if matches!(&expr.node, ExprKind::Lambda(_)) { if matches!(expr, Expr::Lambda(_)) {
// Don't recurse. // Don't recurse.
return; return;
} }
@ -260,7 +267,7 @@ pub(crate) fn function_uses_loop_variable<'a>(checker: &mut Checker<'a>, node: &
// If a variable was used in a function or lambda body, and assigned in the // If a variable was used in a function or lambda body, and assigned in the
// loop, flag it. // loop, flag it.
for (name, expr, range) in suspicious_variables { for (name, expr) in suspicious_variables {
if reassigned_in_loop.contains(name) { if reassigned_in_loop.contains(name) {
if !checker.flake8_bugbear_seen.contains(&expr) { if !checker.flake8_bugbear_seen.contains(&expr) {
checker.flake8_bugbear_seen.push(expr); checker.flake8_bugbear_seen.push(expr);
@ -268,7 +275,7 @@ pub(crate) fn function_uses_loop_variable<'a>(checker: &mut Checker<'a>, node: &
FunctionUsesLoopVariable { FunctionUsesLoopVariable {
name: name.to_string(), name: name.to_string(),
}, },
range, expr.range(),
)); ));
} }
} }

View file

@ -1,5 +1,5 @@
use ruff_text_size::TextRange; use ruff_text_size::TextRange;
use rustpython_parser::ast::{self, Constant, Expr, ExprContext, ExprKind}; use rustpython_parser::ast::{self, Constant, Expr, ExprContext, Ranged};
use ruff_diagnostics::{AlwaysAutofixableViolation, Diagnostic, Edit, Fix}; use ruff_diagnostics::{AlwaysAutofixableViolation, Diagnostic, Edit, Fix};
use ruff_macros::{derive_message_formats, violation}; use ruff_macros::{derive_message_formats, violation};
@ -26,14 +26,13 @@ impl AlwaysAutofixableViolation for GetAttrWithConstant {
} }
} }
fn attribute(value: &Expr, attr: &str) -> Expr { fn attribute(value: &Expr, attr: &str) -> Expr {
Expr::new(
TextRange::default(),
ast::ExprAttribute { ast::ExprAttribute {
value: Box::new(value.clone()), value: Box::new(value.clone()),
attr: attr.into(), attr: attr.into(),
ctx: ExprContext::Load, ctx: ExprContext::Load,
}, range: TextRange::default(),
) }
.into()
} }
/// B009 /// B009
@ -43,7 +42,7 @@ pub(crate) fn getattr_with_constant(
func: &Expr, func: &Expr,
args: &[Expr], args: &[Expr],
) { ) {
let ExprKind::Name(ast::ExprName { id, .. } )= &func.node else { let Expr::Name(ast::ExprName { id, .. } )= func else {
return; return;
}; };
if id != "getattr" { if id != "getattr" {
@ -52,10 +51,10 @@ pub(crate) fn getattr_with_constant(
let [obj, arg] = args else { let [obj, arg] = args else {
return; return;
}; };
let ExprKind::Constant(ast::ExprConstant { let Expr::Constant(ast::ExprConstant {
value: Constant::Str(value), value: Constant::Str(value),
.. ..
} )= &arg.node else { } )= arg else {
return; return;
}; };
if !is_identifier(value) { if !is_identifier(value) {

View file

@ -1,4 +1,4 @@
use rustpython_parser::ast::{self, Stmt, StmtKind}; use rustpython_parser::ast::{self, Ranged, Stmt};
use ruff_diagnostics::{Diagnostic, Violation}; use ruff_diagnostics::{Diagnostic, Violation};
use ruff_macros::{derive_message_formats, violation}; use ruff_macros::{derive_message_formats, violation};
@ -23,35 +23,31 @@ fn walk_stmt(checker: &mut Checker, body: &[Stmt], f: fn(&Stmt) -> bool) {
if f(stmt) { if f(stmt) {
checker.diagnostics.push(Diagnostic::new( checker.diagnostics.push(Diagnostic::new(
JumpStatementInFinally { JumpStatementInFinally {
name: match &stmt.node { name: match stmt {
StmtKind::Break => "break", Stmt::Break(_) => "break",
StmtKind::Continue => "continue", Stmt::Continue(_) => "continue",
StmtKind::Return(_) => "return", Stmt::Return(_) => "return",
_ => unreachable!( _ => unreachable!("Expected Stmt::Break | Stmt::Continue | Stmt::Return"),
"Expected StmtKind::Break | StmtKind::Continue | StmtKind::Return"
),
} }
.to_owned(), .to_owned(),
}, },
stmt.range(), stmt.range(),
)); ));
} }
match &stmt.node { match stmt {
StmtKind::While(ast::StmtWhile { body, .. }) Stmt::While(ast::StmtWhile { body, .. })
| StmtKind::For(ast::StmtFor { body, .. }) | Stmt::For(ast::StmtFor { body, .. })
| StmtKind::AsyncFor(ast::StmtAsyncFor { body, .. }) => { | Stmt::AsyncFor(ast::StmtAsyncFor { body, .. }) => {
walk_stmt(checker, body, |stmt| { walk_stmt(checker, body, |stmt| matches!(stmt, Stmt::Return(_)));
matches!(stmt.node, StmtKind::Return(_))
});
} }
StmtKind::If(ast::StmtIf { body, .. }) Stmt::If(ast::StmtIf { body, .. })
| StmtKind::Try(ast::StmtTry { body, .. }) | Stmt::Try(ast::StmtTry { body, .. })
| StmtKind::TryStar(ast::StmtTryStar { body, .. }) | Stmt::TryStar(ast::StmtTryStar { body, .. })
| StmtKind::With(ast::StmtWith { body, .. }) | Stmt::With(ast::StmtWith { body, .. })
| StmtKind::AsyncWith(ast::StmtAsyncWith { body, .. }) => { | Stmt::AsyncWith(ast::StmtAsyncWith { body, .. }) => {
walk_stmt(checker, body, f); walk_stmt(checker, body, f);
} }
StmtKind::Match(ast::StmtMatch { cases, .. }) => { Stmt::Match(ast::StmtMatch { cases, .. }) => {
for case in cases { for case in cases {
walk_stmt(checker, &case.body, f); walk_stmt(checker, &case.body, f);
} }
@ -64,9 +60,6 @@ fn walk_stmt(checker: &mut Checker, body: &[Stmt], f: fn(&Stmt) -> bool) {
/// B012 /// B012
pub(crate) fn jump_statement_in_finally(checker: &mut Checker, finalbody: &[Stmt]) { pub(crate) fn jump_statement_in_finally(checker: &mut Checker, finalbody: &[Stmt]) {
walk_stmt(checker, finalbody, |stmt| { walk_stmt(checker, finalbody, |stmt| {
matches!( matches!(stmt, Stmt::Break(_) | Stmt::Continue(_) | Stmt::Return(_))
stmt.node,
StmtKind::Break | StmtKind::Continue | StmtKind::Return(_)
)
}); });
} }

View file

@ -1,5 +1,5 @@
use rustc_hash::FxHashMap; use rustc_hash::FxHashMap;
use rustpython_parser::ast::{self, Expr, ExprKind}; use rustpython_parser::ast::{self, Expr, Ranged};
use ruff_diagnostics::{Diagnostic, Violation}; use ruff_diagnostics::{Diagnostic, Violation};
use ruff_macros::{derive_message_formats, violation}; use ruff_macros::{derive_message_formats, violation};
@ -31,22 +31,26 @@ where
'b: 'a, 'b: 'a,
{ {
fn visit_expr(&mut self, expr: &'b Expr) { fn visit_expr(&mut self, expr: &'b Expr) {
match &expr.node { match expr {
ExprKind::Name(ast::ExprName { id, .. }) => { Expr::Name(ast::ExprName { id, .. }) => {
self.names.insert(id, expr); self.names.insert(id, expr);
} }
ExprKind::ListComp(ast::ExprListComp { generators, .. }) Expr::ListComp(ast::ExprListComp { generators, .. })
| ExprKind::DictComp(ast::ExprDictComp { generators, .. }) | Expr::DictComp(ast::ExprDictComp { generators, .. })
| ExprKind::SetComp(ast::ExprSetComp { generators, .. }) | Expr::SetComp(ast::ExprSetComp { generators, .. })
| ExprKind::GeneratorExp(ast::ExprGeneratorExp { generators, .. }) => { | Expr::GeneratorExp(ast::ExprGeneratorExp { generators, .. }) => {
for comp in generators { for comp in generators {
self.visit_expr(&comp.iter); self.visit_expr(&comp.iter);
} }
} }
ExprKind::Lambda(ast::ExprLambda { args, body }) => { Expr::Lambda(ast::ExprLambda {
args,
body,
range: _,
}) => {
visitor::walk_expr(self, body); visitor::walk_expr(self, body);
for arg in &args.args { for arg in &args.args {
self.names.remove(arg.node.arg.as_str()); self.names.remove(arg.arg.as_str());
} }
} }
_ => visitor::walk_expr(self, expr), _ => visitor::walk_expr(self, expr),

View file

@ -1,4 +1,4 @@
use rustpython_parser::ast::{self, Arguments, Expr, ExprKind}; use rustpython_parser::ast::{self, Arguments, Expr, Ranged};
use ruff_diagnostics::{Diagnostic, Violation}; use ruff_diagnostics::{Diagnostic, Violation};
use ruff_macros::{derive_message_formats, violation}; use ruff_macros::{derive_message_formats, violation};
@ -37,14 +37,14 @@ pub(crate) fn is_mutable_func(checker: &Checker, func: &Expr) -> bool {
} }
fn is_mutable_expr(checker: &Checker, expr: &Expr) -> bool { fn is_mutable_expr(checker: &Checker, expr: &Expr) -> bool {
match &expr.node { match expr {
ExprKind::List(_) Expr::List(_)
| ExprKind::Dict(_) | Expr::Dict(_)
| ExprKind::Set(_) | Expr::Set(_)
| ExprKind::ListComp(_) | Expr::ListComp(_)
| ExprKind::DictComp(_) | Expr::DictComp(_)
| ExprKind::SetComp(_) => true, | Expr::SetComp(_) => true,
ExprKind::Call(ast::ExprCall { func, .. }) => is_mutable_func(checker, func), Expr::Call(ast::ExprCall { func, .. }) => is_mutable_func(checker, func),
_ => false, _ => false,
} }
} }
@ -68,7 +68,6 @@ pub(crate) fn mutable_argument_default(checker: &mut Checker, arguments: &Argume
{ {
if is_mutable_expr(checker, default) if is_mutable_expr(checker, default)
&& !arg && !arg
.node
.annotation .annotation
.as_ref() .as_ref()
.map_or(false, |expr| is_immutable_annotation(&checker.ctx, expr)) .map_or(false, |expr| is_immutable_annotation(&checker.ctx, expr))

View file

@ -1,4 +1,4 @@
use rustpython_parser::ast::{Expr, Keyword}; use rustpython_parser::ast::{Expr, Keyword, Ranged};
use ruff_diagnostics::{Diagnostic, Violation}; use ruff_diagnostics::{Diagnostic, Violation};
use ruff_macros::{derive_message_formats, violation}; use ruff_macros::{derive_message_formats, violation};

View file

@ -1,4 +1,4 @@
use rustpython_parser::ast::{self, ExprKind, Stmt}; use rustpython_parser::ast::{self, Expr, Stmt};
use ruff_diagnostics::{Diagnostic, Violation}; use ruff_diagnostics::{Diagnostic, Violation};
use ruff_macros::{derive_message_formats, violation}; use ruff_macros::{derive_message_formats, violation};
@ -32,8 +32,8 @@ pub(crate) fn raise_without_from_inside_except(checker: &mut Checker, body: &[St
for (range, exc, cause) in raises { for (range, exc, cause) in raises {
if cause.is_none() { if cause.is_none() {
if let Some(exc) = exc { if let Some(exc) = exc {
match &exc.node { match exc {
ExprKind::Name(ast::ExprName { id, .. }) if is_lower(id) => {} Expr::Name(ast::ExprName { id, .. }) if is_lower(id) => {}
_ => { _ => {
checker checker
.diagnostics .diagnostics

View file

@ -1,4 +1,4 @@
use rustpython_parser::ast::{self, Excepthandler, ExcepthandlerKind, ExprKind}; use rustpython_parser::ast::{self, Excepthandler, Expr, Ranged};
use ruff_diagnostics::{AlwaysAutofixableViolation, Diagnostic, Edit, Fix}; use ruff_diagnostics::{AlwaysAutofixableViolation, Diagnostic, Edit, Fix};
use ruff_macros::{derive_message_formats, violation}; use ruff_macros::{derive_message_formats, violation};
@ -34,10 +34,10 @@ pub(crate) fn redundant_tuple_in_exception_handler(
handlers: &[Excepthandler], handlers: &[Excepthandler],
) { ) {
for handler in handlers { for handler in handlers {
let ExcepthandlerKind::ExceptHandler(ast::ExcepthandlerExceptHandler { type_: Some(type_), .. }) = &handler.node else { let Excepthandler::ExceptHandler(ast::ExcepthandlerExceptHandler { type_: Some(type_), .. }) = handler else {
continue; continue;
}; };
let ExprKind::Tuple(ast::ExprTuple { elts, .. }) = &type_.node else { let Expr::Tuple(ast::ExprTuple { elts, .. }) = type_.as_ref() else {
continue; continue;
}; };
let [elt] = &elts[..] else { let [elt] = &elts[..] else {

View file

@ -1,4 +1,4 @@
use rustpython_parser::ast::{self, Comprehension, Expr, ExprKind, Stmt, StmtKind}; use rustpython_parser::ast::{self, Comprehension, Expr, Ranged, Stmt};
use ruff_diagnostics::{Diagnostic, Violation}; use ruff_diagnostics::{Diagnostic, Violation};
use ruff_macros::{derive_message_formats, violation}; use ruff_macros::{derive_message_formats, violation};
@ -84,7 +84,7 @@ impl<'a> GroupNameFinder<'a> {
} }
fn name_matches(&self, expr: &Expr) -> bool { fn name_matches(&self, expr: &Expr) -> bool {
if let ExprKind::Name(ast::ExprName { id, .. }) = &expr.node { if let Expr::Name(ast::ExprName { id, .. }) = expr {
id == self.group_name id == self.group_name
} else { } else {
false false
@ -112,8 +112,8 @@ where
if self.overridden { if self.overridden {
return; return;
} }
match &stmt.node { match stmt {
StmtKind::For(ast::StmtFor { Stmt::For(ast::StmtFor {
target, iter, body, .. target, iter, body, ..
}) => { }) => {
if self.name_matches(target) { if self.name_matches(target) {
@ -138,15 +138,20 @@ where
self.nested = false; self.nested = false;
} }
} }
StmtKind::While(ast::StmtWhile { body, .. }) => { Stmt::While(ast::StmtWhile { body, .. }) => {
self.nested = true; self.nested = true;
visitor::walk_body(self, body); visitor::walk_body(self, body);
self.nested = false; self.nested = false;
} }
StmtKind::If(ast::StmtIf { test, body, orelse }) => { Stmt::If(ast::StmtIf {
test,
body,
orelse,
range: _,
}) => {
// Determine whether we're on an `if` arm (as opposed to an `elif`). // Determine whether we're on an `if` arm (as opposed to an `elif`).
let is_if_arm = !self.parent_ifs.iter().any(|parent| { let is_if_arm = !self.parent_ifs.iter().any(|parent| {
if let StmtKind::If(ast::StmtIf { orelse, .. }) = &parent.node { if let Stmt::If(ast::StmtIf { orelse, .. }) = parent {
orelse.len() == 1 && &orelse[0] == stmt orelse.len() == 1 && &orelse[0] == stmt
} else { } else {
false false
@ -166,12 +171,12 @@ where
let has_else = orelse let has_else = orelse
.first() .first()
.map_or(false, |expr| !matches!(expr.node, StmtKind::If(_))); .map_or(false, |expr| !matches!(expr, Stmt::If(_)));
self.parent_ifs.push(stmt); self.parent_ifs.push(stmt);
if has_else { if has_else {
// There's no `StmtKind::Else`; instead, the `else` contents are directly on // There's no `Stmt::Else`; instead, the `else` contents are directly on
// the `orelse` of the `StmtKind::If` node. We want to add a new counter for // the `orelse` of the `Stmt::If` node. We want to add a new counter for
// the `orelse` branch, but first, we need to visit the `if` body manually. // the `orelse` branch, but first, we need to visit the `if` body manually.
self.visit_expr(test); self.visit_expr(test);
self.visit_body(body); self.visit_body(body);
@ -193,7 +198,11 @@ where
} }
} }
} }
StmtKind::Match(ast::StmtMatch { subject, cases }) => { Stmt::Match(ast::StmtMatch {
subject,
cases,
range: _,
}) => {
self.counter_stack.push(Vec::with_capacity(cases.len())); self.counter_stack.push(Vec::with_capacity(cases.len()));
self.visit_expr(subject); self.visit_expr(subject);
for match_case in cases { for match_case in cases {
@ -207,14 +216,14 @@ where
self.increment_usage_count(max_count); self.increment_usage_count(max_count);
} }
} }
StmtKind::Assign(ast::StmtAssign { targets, value, .. }) => { Stmt::Assign(ast::StmtAssign { targets, value, .. }) => {
if targets.iter().any(|target| self.name_matches(target)) { if targets.iter().any(|target| self.name_matches(target)) {
self.overridden = true; self.overridden = true;
} else { } else {
self.visit_expr(value); self.visit_expr(value);
} }
} }
StmtKind::AnnAssign(ast::StmtAnnAssign { target, value, .. }) => { Stmt::AnnAssign(ast::StmtAnnAssign { target, value, .. }) => {
if self.name_matches(target) { if self.name_matches(target) {
self.overridden = true; self.overridden = true;
} else if let Some(expr) = value { } else if let Some(expr) = value {
@ -241,7 +250,7 @@ where
} }
fn visit_expr(&mut self, expr: &'a Expr) { fn visit_expr(&mut self, expr: &'a Expr) {
if let ExprKind::NamedExpr(ast::ExprNamedExpr { target, .. }) = &expr.node { if let Expr::NamedExpr(ast::ExprNamedExpr { target, .. }) = expr {
if self.name_matches(target) { if self.name_matches(target) {
self.overridden = true; self.overridden = true;
} }
@ -250,9 +259,17 @@ where
return; return;
} }
match &expr.node { match expr {
ExprKind::ListComp(ast::ExprListComp { elt, generators }) Expr::ListComp(ast::ExprListComp {
| ExprKind::SetComp(ast::ExprSetComp { elt, generators }) => { elt,
generators,
range: _,
})
| Expr::SetComp(ast::ExprSetComp {
elt,
generators,
range: _,
}) => {
for comprehension in generators { for comprehension in generators {
self.visit_comprehension(comprehension); self.visit_comprehension(comprehension);
} }
@ -262,10 +279,11 @@ where
self.nested = false; self.nested = false;
} }
} }
ExprKind::DictComp(ast::ExprDictComp { Expr::DictComp(ast::ExprDictComp {
key, key,
value, value,
generators, generators,
range: _,
}) => { }) => {
for comprehension in generators { for comprehension in generators {
self.visit_comprehension(comprehension); self.visit_comprehension(comprehension);
@ -308,10 +326,10 @@ pub(crate) fn reuse_of_groupby_generator(
body: &[Stmt], body: &[Stmt],
iter: &Expr, iter: &Expr,
) { ) {
let ExprKind::Call(ast::ExprCall { func, .. }) = &iter.node else { let Expr::Call(ast::ExprCall { func, .. }) = &iter else {
return; return;
}; };
let ExprKind::Tuple(ast::ExprTuple { elts, .. }) = &target.node else { let Expr::Tuple(ast::ExprTuple { elts, .. }) = target else {
// Ignore any `groupby()` invocation that isn't unpacked // Ignore any `groupby()` invocation that isn't unpacked
return; return;
}; };
@ -319,7 +337,7 @@ pub(crate) fn reuse_of_groupby_generator(
return; return;
} }
// We have an invocation of groupby which is a simple unpacking // We have an invocation of groupby which is a simple unpacking
let ExprKind::Name(ast::ExprName { id: group_name, .. }) = &elts[1].node else { let Expr::Name(ast::ExprName { id: group_name, .. }) = &elts[1] else {
return; return;
}; };
// Check if the function call is `itertools.groupby` // Check if the function call is `itertools.groupby`

View file

@ -1,5 +1,5 @@
use ruff_text_size::TextRange; use ruff_text_size::TextRange;
use rustpython_parser::ast::{self, Constant, Expr, ExprContext, ExprKind, Stmt, StmtKind}; use rustpython_parser::ast::{self, Constant, Expr, ExprContext, Ranged, Stmt};
use ruff_diagnostics::{AlwaysAutofixableViolation, Diagnostic, Edit, Fix}; use ruff_diagnostics::{AlwaysAutofixableViolation, Diagnostic, Edit, Fix};
use ruff_macros::{derive_message_formats, violation}; use ruff_macros::{derive_message_formats, violation};
@ -28,21 +28,17 @@ impl AlwaysAutofixableViolation for SetAttrWithConstant {
} }
fn assignment(obj: &Expr, name: &str, value: &Expr, stylist: &Stylist) -> String { fn assignment(obj: &Expr, name: &str, value: &Expr, stylist: &Stylist) -> String {
let stmt = Stmt::new( let stmt = Stmt::Assign(ast::StmtAssign {
TextRange::default(), targets: vec![Expr::Attribute(ast::ExprAttribute {
StmtKind::Assign(ast::StmtAssign {
targets: vec![Expr::new(
TextRange::default(),
ExprKind::Attribute(ast::ExprAttribute {
value: Box::new(obj.clone()), value: Box::new(obj.clone()),
attr: name.into(), attr: name.into(),
ctx: ExprContext::Store, ctx: ExprContext::Store,
}), range: TextRange::default(),
)], })],
value: Box::new(value.clone()), value: Box::new(value.clone()),
type_comment: None, type_comment: None,
}), range: TextRange::default(),
); });
unparse_stmt(&stmt, stylist) unparse_stmt(&stmt, stylist)
} }
@ -53,7 +49,7 @@ pub(crate) fn setattr_with_constant(
func: &Expr, func: &Expr,
args: &[Expr], args: &[Expr],
) { ) {
let ExprKind::Name(ast::ExprName { id, .. }) = &func.node else { let Expr::Name(ast::ExprName { id, .. }) = func else {
return; return;
}; };
if id != "setattr" { if id != "setattr" {
@ -62,10 +58,10 @@ pub(crate) fn setattr_with_constant(
let [obj, name, value] = args else { let [obj, name, value] = args else {
return; return;
}; };
let ExprKind::Constant(ast::ExprConstant { let Expr::Constant(ast::ExprConstant {
value: Constant::Str(name), value: Constant::Str(name),
.. ..
} )= &name.node else { } )= name else {
return; return;
}; };
if !is_identifier(name) { if !is_identifier(name) {
@ -76,8 +72,12 @@ pub(crate) fn setattr_with_constant(
} }
// We can only replace a `setattr` call (which is an `Expr`) with an assignment // We can only replace a `setattr` call (which is an `Expr`) with an assignment
// (which is a `Stmt`) if the `Expr` is already being used as a `Stmt` // (which is a `Stmt`) if the `Expr` is already being used as a `Stmt`
// (i.e., it's directly within an `StmtKind::Expr`). // (i.e., it's directly within an `Stmt::Expr`).
if let StmtKind::Expr(ast::StmtExpr { value: child }) = &checker.ctx.stmt().node { if let Stmt::Expr(ast::StmtExpr {
value: child,
range: _,
}) = &checker.ctx.stmt()
{
if expr == child.as_ref() { if expr == child.as_ref() {
let mut diagnostic = Diagnostic::new(SetAttrWithConstant, expr.range()); let mut diagnostic = Diagnostic::new(SetAttrWithConstant, expr.range());

View file

@ -7,7 +7,7 @@
//! by the unpacked sequence, and this change of ordering can surprise and //! by the unpacked sequence, and this change of ordering can surprise and
//! mislead readers. //! mislead readers.
use rustpython_parser::ast::{Expr, ExprKind, Keyword}; use rustpython_parser::ast::{Expr, Keyword, Ranged};
use ruff_diagnostics::{Diagnostic, Violation}; use ruff_diagnostics::{Diagnostic, Violation};
use ruff_macros::{derive_message_formats, violation}; use ruff_macros::{derive_message_formats, violation};
@ -34,7 +34,7 @@ pub(crate) fn star_arg_unpacking_after_keyword_arg(
return; return;
}; };
for arg in args { for arg in args {
let ExprKind::Starred (_) = arg.node else { let Expr::Starred (_) = arg else {
continue; continue;
}; };
if arg.start() <= keyword.start() { if arg.start() <= keyword.start() {

View file

@ -1,5 +1,5 @@
use itertools::Itertools; use itertools::Itertools;
use rustpython_parser::ast::{self, Constant, Expr, ExprKind}; use rustpython_parser::ast::{self, Constant, Expr, Ranged};
use ruff_diagnostics::{Diagnostic, Violation}; use ruff_diagnostics::{Diagnostic, Violation};
use ruff_macros::{derive_message_formats, violation}; use ruff_macros::{derive_message_formats, violation};
@ -23,7 +23,7 @@ pub(crate) fn strip_with_multi_characters(
func: &Expr, func: &Expr,
args: &[Expr], args: &[Expr],
) { ) {
let ExprKind::Attribute(ast::ExprAttribute { attr, .. }) = &func.node else { let Expr::Attribute(ast::ExprAttribute { attr, .. }) = func else {
return; return;
}; };
if !matches!(attr.as_str(), "strip" | "lstrip" | "rstrip") { if !matches!(attr.as_str(), "strip" | "lstrip" | "rstrip") {
@ -33,10 +33,10 @@ pub(crate) fn strip_with_multi_characters(
return; return;
} }
let ExprKind::Constant(ast::ExprConstant { let Expr::Constant(ast::ExprConstant {
value: Constant::Str(value), value: Constant::Str(value),
.. ..
} )= &args[0].node else { } )= &args[0] else {
return; return;
}; };

View file

@ -17,7 +17,7 @@
//! n += 1 //! n += 1
//! ``` //! ```
use rustpython_parser::ast::{self, Expr, ExprKind, Unaryop}; use rustpython_parser::ast::{self, Expr, Ranged, Unaryop};
use ruff_diagnostics::{Diagnostic, Violation}; use ruff_diagnostics::{Diagnostic, Violation};
use ruff_macros::{derive_message_formats, violation}; use ruff_macros::{derive_message_formats, violation};
@ -38,13 +38,13 @@ impl Violation for UnaryPrefixIncrement {
pub(crate) fn unary_prefix_increment( pub(crate) fn unary_prefix_increment(
checker: &mut Checker, checker: &mut Checker,
expr: &Expr, expr: &Expr,
op: &Unaryop, op: Unaryop,
operand: &Expr, operand: &Expr,
) { ) {
if !matches!(op, Unaryop::UAdd) { if !matches!(op, Unaryop::UAdd) {
return; return;
} }
let ExprKind::UnaryOp(ast::ExprUnaryOp { op, .. })= &operand.node else { let Expr::UnaryOp(ast::ExprUnaryOp { op, .. })= operand else {
return; return;
}; };
if !matches!(op, Unaryop::UAdd) { if !matches!(op, Unaryop::UAdd) {

View file

@ -1,4 +1,4 @@
use rustpython_parser::ast::{self, Expr, ExprKind, Stmt}; use rustpython_parser::ast::{self, Expr, Ranged, Stmt};
use ruff_diagnostics::{Diagnostic, Violation}; use ruff_diagnostics::{Diagnostic, Violation};
use ruff_macros::{derive_message_formats, violation}; use ruff_macros::{derive_message_formats, violation};
@ -43,16 +43,16 @@ pub(crate) fn unintentional_type_annotation(
if value.is_some() { if value.is_some() {
return; return;
} }
match &target.node { match target {
ExprKind::Subscript(ast::ExprSubscript { value, .. }) => { Expr::Subscript(ast::ExprSubscript { value, .. }) => {
if matches!(&value.node, ExprKind::Name(_)) { if value.is_name_expr() {
checker checker
.diagnostics .diagnostics
.push(Diagnostic::new(UnintentionalTypeAnnotation, stmt.range())); .push(Diagnostic::new(UnintentionalTypeAnnotation, stmt.range()));
} }
} }
ExprKind::Attribute(ast::ExprAttribute { value, .. }) => { Expr::Attribute(ast::ExprAttribute { value, .. }) => {
if let ExprKind::Name(ast::ExprName { id, .. }) = &value.node { if let Expr::Name(ast::ExprName { id, .. }) = value.as_ref() {
if id != "self" { if id != "self" {
checker checker
.diagnostics .diagnostics

View file

@ -1,4 +1,4 @@
use rustpython_parser::ast::{self, Constant, Expr, ExprKind}; use rustpython_parser::ast::{self, Constant, Expr, Ranged};
use ruff_diagnostics::{Diagnostic, Violation}; use ruff_diagnostics::{Diagnostic, Violation};
use ruff_macros::{derive_message_formats, violation}; use ruff_macros::{derive_message_formats, violation};
@ -25,7 +25,7 @@ pub(crate) fn unreliable_callable_check(
func: &Expr, func: &Expr,
args: &[Expr], args: &[Expr],
) { ) {
let ExprKind::Name(ast::ExprName { id, .. }) = &func.node else { let Expr::Name(ast::ExprName { id, .. }) = func else {
return; return;
}; };
if id != "getattr" && id != "hasattr" { if id != "getattr" && id != "hasattr" {
@ -34,10 +34,10 @@ pub(crate) fn unreliable_callable_check(
if args.len() < 2 { if args.len() < 2 {
return; return;
}; };
let ExprKind::Constant(ast::ExprConstant { let Expr::Constant(ast::ExprConstant {
value: Constant::Str(s), value: Constant::Str(s),
.. ..
}) = &args[1].node else }) = &args[1] else
{ {
return; return;
}; };

View file

@ -19,7 +19,7 @@
//! ``` //! ```
use rustc_hash::FxHashMap; use rustc_hash::FxHashMap;
use rustpython_parser::ast::{self, Expr, ExprKind, Stmt}; use rustpython_parser::ast::{self, Expr, Ranged, Stmt};
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use ruff_diagnostics::{AutofixKind, Diagnostic, Edit, Fix, Violation}; use ruff_diagnostics::{AutofixKind, Diagnostic, Edit, Fix, Violation};
@ -74,7 +74,7 @@ impl Violation for UnusedLoopControlVariable {
} }
} }
/// Identify all `ExprKind::Name` nodes in an AST. /// Identify all `Expr::Name` nodes in an AST.
struct NameFinder<'a> { struct NameFinder<'a> {
/// A map from identifier to defining expression. /// A map from identifier to defining expression.
names: FxHashMap<&'a str, &'a Expr>, names: FxHashMap<&'a str, &'a Expr>,
@ -93,7 +93,7 @@ where
'b: 'a, 'b: 'a,
{ {
fn visit_expr(&mut self, expr: &'a Expr) { fn visit_expr(&mut self, expr: &'a Expr) {
if let ExprKind::Name(ast::ExprName { id, .. }) = &expr.node { if let Expr::Name(ast::ExprName { id, .. }) = expr {
self.names.insert(id, expr); self.names.insert(id, expr);
} }
visitor::walk_expr(self, expr); visitor::walk_expr(self, expr);

View file

@ -1,4 +1,4 @@
use rustpython_parser::ast::{Expr, ExprKind}; use rustpython_parser::ast::{Expr, Ranged};
use ruff_diagnostics::{Diagnostic, Violation}; use ruff_diagnostics::{Diagnostic, Violation};
use ruff_macros::{derive_message_formats, violation}; use ruff_macros::{derive_message_formats, violation};
@ -20,7 +20,7 @@ impl Violation for UselessComparison {
/// B015 /// B015
pub(crate) fn useless_comparison(checker: &mut Checker, expr: &Expr) { pub(crate) fn useless_comparison(checker: &mut Checker, expr: &Expr) {
if matches!(expr.node, ExprKind::Compare(_)) { if matches!(expr, Expr::Compare(_)) {
checker checker
.diagnostics .diagnostics
.push(Diagnostic::new(UselessComparison, expr.range())); .push(Diagnostic::new(UselessComparison, expr.range()));

View file

@ -1,4 +1,4 @@
use rustpython_parser::ast::Expr; use rustpython_parser::ast::{Expr, Ranged};
use crate::checkers::ast::Checker; use crate::checkers::ast::Checker;
use ruff_diagnostics::{Diagnostic, Violation}; use ruff_diagnostics::{Diagnostic, Violation};

View file

@ -1,4 +1,4 @@
use rustpython_parser::ast::{self, Constant, Expr, ExprKind}; use rustpython_parser::ast::{self, Constant, Expr, Ranged};
use ruff_diagnostics::{Diagnostic, Violation}; use ruff_diagnostics::{Diagnostic, Violation};
use ruff_macros::{derive_message_formats, violation}; use ruff_macros::{derive_message_formats, violation};
@ -36,15 +36,15 @@ impl Violation for UselessExpression {
/// B018 /// B018
pub(crate) fn useless_expression(checker: &mut Checker, value: &Expr) { pub(crate) fn useless_expression(checker: &mut Checker, value: &Expr) {
// Ignore comparisons, as they're handled by `useless_comparison`. // Ignore comparisons, as they're handled by `useless_comparison`.
if matches!(value.node, ExprKind::Compare(_)) { if matches!(value, Expr::Compare(_)) {
return; return;
} }
// Ignore strings, to avoid false positives with docstrings. // Ignore strings, to avoid false positives with docstrings.
if matches!( if matches!(
value.node, value,
ExprKind::JoinedStr(_) Expr::JoinedStr(_)
| ExprKind::Constant(ast::ExprConstant { | Expr::Constant(ast::ExprConstant {
value: Constant::Str(..) | Constant::Ellipsis, value: Constant::Str(..) | Constant::Ellipsis,
.. ..
}) })
@ -56,7 +56,7 @@ pub(crate) fn useless_expression(checker: &mut Checker, value: &Expr) {
if contains_effect(value, |id| checker.ctx.is_builtin(id)) { if contains_effect(value, |id| checker.ctx.is_builtin(id)) {
// Flag attributes as useless expressions, even if they're attached to calls or other // Flag attributes as useless expressions, even if they're attached to calls or other
// expressions. // expressions.
if matches!(value.node, ExprKind::Attribute(_)) { if matches!(value, Expr::Attribute(_)) {
checker.diagnostics.push(Diagnostic::new( checker.diagnostics.push(Diagnostic::new(
UselessExpression { UselessExpression {
kind: Kind::Attribute, kind: Kind::Attribute,

View file

@ -1,4 +1,4 @@
use rustpython_parser::ast::{self, Expr, ExprKind, Keyword}; use rustpython_parser::ast::{self, Expr, Keyword, Ranged};
use crate::checkers::ast::Checker; use crate::checkers::ast::Checker;
use ruff_diagnostics::{Diagnostic, Violation}; use ruff_diagnostics::{Diagnostic, Violation};
@ -21,16 +21,12 @@ pub(crate) fn zip_without_explicit_strict(
func: &Expr, func: &Expr,
kwargs: &[Keyword], kwargs: &[Keyword],
) { ) {
if let ExprKind::Name(ast::ExprName { id, .. }) = &func.node { if let Expr::Name(ast::ExprName { id, .. }) = func {
if id == "zip" if id == "zip"
&& checker.ctx.is_builtin("zip") && checker.ctx.is_builtin("zip")
&& !kwargs.iter().any(|keyword| { && !kwargs
keyword .iter()
.node .any(|keyword| keyword.arg.as_ref().map_or(false, |name| name == "strict"))
.arg
.as_ref()
.map_or(false, |name| name == "strict")
})
{ {
checker checker
.diagnostics .diagnostics

View file

@ -1,9 +1,8 @@
use rustpython_parser::ast::Attributed;
use ruff_diagnostics::Diagnostic; use ruff_diagnostics::Diagnostic;
use ruff_diagnostics::Violation; use ruff_diagnostics::Violation;
use ruff_macros::{derive_message_formats, violation}; use ruff_macros::{derive_message_formats, violation};
use ruff_python_stdlib::builtins::BUILTINS; use ruff_python_stdlib::builtins::BUILTINS;
use rustpython_parser::ast::Ranged;
use crate::checkers::ast::Checker; use crate::checkers::ast::Checker;
@ -172,11 +171,10 @@ fn shadows_builtin(name: &str, ignorelist: &[String]) -> bool {
} }
/// A001 /// A001
pub(crate) fn builtin_variable_shadowing<T>( pub(crate) fn builtin_variable_shadowing<T>(checker: &mut Checker, name: &str, attributed: &T)
checker: &mut Checker, where
name: &str, T: Ranged,
attributed: &Attributed<T>, {
) {
if shadows_builtin(name, &checker.settings.flake8_builtins.builtins_ignorelist) { if shadows_builtin(name, &checker.settings.flake8_builtins.builtins_ignorelist) {
checker.diagnostics.push(Diagnostic::new( checker.diagnostics.push(Diagnostic::new(
BuiltinVariableShadowing { BuiltinVariableShadowing {
@ -188,11 +186,10 @@ pub(crate) fn builtin_variable_shadowing<T>(
} }
/// A002 /// A002
pub(crate) fn builtin_argument_shadowing<T>( pub(crate) fn builtin_argument_shadowing<T>(checker: &mut Checker, name: &str, attributed: &T)
checker: &mut Checker, where
name: &str, T: Ranged,
attributed: &Attributed<T>, {
) {
if shadows_builtin(name, &checker.settings.flake8_builtins.builtins_ignorelist) { if shadows_builtin(name, &checker.settings.flake8_builtins.builtins_ignorelist) {
checker.diagnostics.push(Diagnostic::new( checker.diagnostics.push(Diagnostic::new(
BuiltinArgumentShadowing { BuiltinArgumentShadowing {
@ -204,11 +201,10 @@ pub(crate) fn builtin_argument_shadowing<T>(
} }
/// A003 /// A003
pub(crate) fn builtin_attribute_shadowing<T>( pub(crate) fn builtin_attribute_shadowing<T>(checker: &mut Checker, name: &str, attributed: &T)
checker: &mut Checker, where
name: &str, T: Ranged,
attributed: &Attributed<T>, {
) {
if shadows_builtin(name, &checker.settings.flake8_builtins.builtins_ignorelist) { if shadows_builtin(name, &checker.settings.flake8_builtins.builtins_ignorelist) {
checker.diagnostics.push(Diagnostic::new( checker.diagnostics.push(Diagnostic::new(
BuiltinAttributeShadowing { BuiltinAttributeShadowing {

View file

@ -7,6 +7,7 @@ use libcst_native::{
ParenthesizedWhitespace, RightCurlyBrace, RightParen, RightSquareBracket, Set, SetComp, ParenthesizedWhitespace, RightCurlyBrace, RightParen, RightSquareBracket, Set, SetComp,
SimpleString, SimpleWhitespace, TrailingWhitespace, Tuple, SimpleString, SimpleWhitespace, TrailingWhitespace, Tuple,
}; };
use rustpython_parser::ast::Ranged;
use ruff_diagnostics::{Edit, Fix}; use ruff_diagnostics::{Edit, Fix};
use ruff_python_ast::source_code::{Locator, Stylist}; use ruff_python_ast::source_code::{Locator, Stylist};
@ -115,11 +116,9 @@ pub(crate) fn fix_unnecessary_generator_set(
// If the expression is embedded in an f-string, surround it with spaces to avoid // If the expression is embedded in an f-string, surround it with spaces to avoid
// syntax errors. // syntax errors.
if let Some(parent_element) = parent { if let Some(rustpython_parser::ast::Expr::FormattedValue(_)) = parent {
if let &rustpython_parser::ast::ExprKind::FormattedValue(_) = &parent_element.node {
content = format!(" {content} "); content = format!(" {content} ");
} }
}
Ok(Edit::range_replacement(content, expr.range())) Ok(Edit::range_replacement(content, expr.range()))
} }
@ -185,11 +184,9 @@ pub(crate) fn fix_unnecessary_generator_dict(
// If the expression is embedded in an f-string, surround it with spaces to avoid // If the expression is embedded in an f-string, surround it with spaces to avoid
// syntax errors. // syntax errors.
if let Some(parent_element) = parent { if let Some(rustpython_parser::ast::Expr::FormattedValue(_)) = parent {
if let &rustpython_parser::ast::ExprKind::FormattedValue(_) = &parent_element.node {
content = format!(" {content} "); content = format!(" {content} ");
} }
}
Ok(Edit::range_replacement(content, expr.range())) Ok(Edit::range_replacement(content, expr.range()))
} }
@ -1100,12 +1097,10 @@ pub(crate) fn fix_unnecessary_map(
// If the expression is embedded in an f-string, surround it with spaces to avoid // If the expression is embedded in an f-string, surround it with spaces to avoid
// syntax errors. // syntax errors.
if kind == "set" || kind == "dict" { if kind == "set" || kind == "dict" {
if let Some(parent_element) = parent { if let Some(rustpython_parser::ast::Expr::FormattedValue(_)) = parent {
if let &rustpython_parser::ast::ExprKind::FormattedValue(_) = &parent_element.node {
content = format!(" {content} "); content = format!(" {content} ");
} }
} }
}
Ok(Edit::range_replacement(content, expr.range())) Ok(Edit::range_replacement(content, expr.range()))
} else { } else {

View file

@ -1,7 +1,7 @@
use rustpython_parser::ast::{self, Expr, ExprKind, Keyword}; use rustpython_parser::ast::{self, Expr, Keyword};
pub(crate) fn expr_name(func: &Expr) -> Option<&str> { pub(crate) fn expr_name(func: &Expr) -> Option<&str> {
if let ExprKind::Name(ast::ExprName { id, .. }) = &func.node { if let Expr::Name(ast::ExprName { id, .. }) = func {
Some(id) Some(id)
} else { } else {
None None
@ -13,7 +13,7 @@ pub(crate) fn exactly_one_argument_with_matching_function<'a>(
func: &Expr, func: &Expr,
args: &'a [Expr], args: &'a [Expr],
keywords: &[Keyword], keywords: &[Keyword],
) -> Option<&'a ExprKind> { ) -> Option<&'a Expr> {
if !keywords.is_empty() { if !keywords.is_empty() {
return None; return None;
} }
@ -23,16 +23,16 @@ pub(crate) fn exactly_one_argument_with_matching_function<'a>(
if expr_name(func)? != name { if expr_name(func)? != name {
return None; return None;
} }
Some(&args[0].node) Some(&args[0])
} }
pub(crate) fn first_argument_with_matching_function<'a>( pub(crate) fn first_argument_with_matching_function<'a>(
name: &str, name: &str,
func: &Expr, func: &Expr,
args: &'a [Expr], args: &'a [Expr],
) -> Option<&'a ExprKind> { ) -> Option<&'a Expr> {
if expr_name(func)? == name { if expr_name(func)? == name {
Some(&args.first()?.node) Some(args.first()?)
} else { } else {
None None
} }

View file

@ -1,4 +1,4 @@
use rustpython_parser::ast::{self, Expr, ExprKind}; use rustpython_parser::ast::{self, Expr, Ranged};
use crate::checkers::ast::Checker; use crate::checkers::ast::Checker;
use crate::registry::AsRule; use crate::registry::AsRule;
@ -65,7 +65,7 @@ pub(crate) fn unnecessary_call_around_sorted(
let Some(arg) = args.first() else { let Some(arg) = args.first() else {
return; return;
}; };
let ExprKind::Call(ast::ExprCall { func, .. }) = &arg.node else { let Expr::Call(ast::ExprCall { func, .. }) = arg else {
return; return;
}; };
let Some(inner) = helpers::expr_name(func) else { let Some(inner) = helpers::expr_name(func) else {

View file

@ -1,4 +1,4 @@
use rustpython_parser::ast::{Expr, Keyword}; use rustpython_parser::ast::{Expr, Keyword, Ranged};
use ruff_diagnostics::{AlwaysAutofixableViolation, Diagnostic}; use ruff_diagnostics::{AlwaysAutofixableViolation, Diagnostic};
use ruff_macros::{derive_message_formats, violation}; use ruff_macros::{derive_message_formats, violation};
@ -70,7 +70,7 @@ pub(crate) fn unnecessary_collection_call(
"dict" "dict"
if keywords.is_empty() if keywords.is_empty()
|| (!settings.allow_dict_calls_with_keyword_arguments || (!settings.allow_dict_calls_with_keyword_arguments
&& keywords.iter().all(|kw| kw.node.arg.is_some())) => && keywords.iter().all(|kw| kw.arg.is_some())) =>
{ {
// `dict()` or `dict(a=1)` (as opposed to `dict(**a)`) // `dict()` or `dict(a=1)` (as opposed to `dict(**a)`)
} }

View file

@ -1,4 +1,4 @@
use rustpython_parser::ast::{self, Comprehension, Expr, ExprKind}; use rustpython_parser::ast::{self, Comprehension, Expr, Ranged};
use ruff_diagnostics::{AlwaysAutofixableViolation, Diagnostic}; use ruff_diagnostics::{AlwaysAutofixableViolation, Diagnostic};
use ruff_macros::{derive_message_formats, violation}; use ruff_macros::{derive_message_formats, violation};
@ -50,10 +50,10 @@ impl AlwaysAutofixableViolation for UnnecessaryComprehension {
/// Add diagnostic for C416 based on the expression node id. /// Add diagnostic for C416 based on the expression node id.
fn add_diagnostic(checker: &mut Checker, expr: &Expr) { fn add_diagnostic(checker: &mut Checker, expr: &Expr) {
let id = match &expr.node { let id = match expr {
ExprKind::ListComp(_) => "list", Expr::ListComp(_) => "list",
ExprKind::SetComp(_) => "set", Expr::SetComp(_) => "set",
ExprKind::DictComp(_) => "dict", Expr::DictComp(_) => "dict",
_ => return, _ => return,
}; };
if !checker.ctx.is_builtin(id) { if !checker.ctx.is_builtin(id) {
@ -95,7 +95,7 @@ pub(crate) fn unnecessary_dict_comprehension(
let Some(value_id) = helpers::expr_name(value) else { let Some(value_id) = helpers::expr_name(value) else {
return; return;
}; };
let ExprKind::Tuple(ast::ExprTuple { elts, .. }) = &generator.target.node else { let Expr::Tuple(ast::ExprTuple { elts, .. }) = &generator.target else {
return; return;
}; };
if elts.len() != 2 { if elts.len() != 2 {

View file

@ -1,4 +1,4 @@
use rustpython_parser::ast::{self, Expr, ExprKind, Keyword}; use rustpython_parser::ast::{self, Expr, Keyword, Ranged};
use ruff_diagnostics::Violation; use ruff_diagnostics::Violation;
use ruff_diagnostics::{AutofixKind, Diagnostic}; use ruff_diagnostics::{AutofixKind, Diagnostic};
@ -66,11 +66,11 @@ pub(crate) fn unnecessary_comprehension_any_all(
if !keywords.is_empty() { if !keywords.is_empty() {
return; return;
} }
let ExprKind::Name(ast::ExprName { id, .. } )= &func.node else { let Expr::Name(ast::ExprName { id, .. } )= func else {
return; return;
}; };
if (matches!(id.as_str(), "all" | "any")) && args.len() == 1 { if (matches!(id.as_str(), "all" | "any")) && args.len() == 1 {
let (ExprKind::ListComp(ast::ExprListComp { elt, .. } )| ExprKind::SetComp(ast::ExprSetComp { elt, .. })) = &args[0].node else { let (Expr::ListComp(ast::ExprListComp { elt, .. } )| Expr::SetComp(ast::ExprSetComp { elt, .. })) = &args[0] else {
return; return;
}; };
if is_async_generator(elt) { if is_async_generator(elt) {
@ -91,5 +91,5 @@ pub(crate) fn unnecessary_comprehension_any_all(
/// Return `true` if the `Expr` contains an `await` expression. /// Return `true` if the `Expr` contains an `await` expression.
fn is_async_generator(expr: &Expr) -> bool { fn is_async_generator(expr: &Expr) -> bool {
any_over_expr(expr, &|expr| matches!(expr.node, ExprKind::Await(_))) any_over_expr(expr, &|expr| matches!(expr, Expr::Await(_)))
} }

View file

@ -1,4 +1,4 @@
use rustpython_parser::ast::{self, Expr, ExprKind}; use rustpython_parser::ast::{self, Expr, Ranged};
use ruff_diagnostics::{AlwaysAutofixableViolation, Diagnostic}; use ruff_diagnostics::{AlwaysAutofixableViolation, Diagnostic};
use ruff_macros::{derive_message_formats, violation}; use ruff_macros::{derive_message_formats, violation};
@ -84,7 +84,7 @@ pub(crate) fn unnecessary_double_cast_or_process(
let Some(arg) = args.first() else { let Some(arg) = args.first() else {
return; return;
}; };
let ExprKind::Call(ast::ExprCall { func, ..} )= &arg.node else { let Expr::Call(ast::ExprCall { func, ..} )= arg else {
return; return;
}; };
let Some(inner) = helpers::expr_name(func) else { let Some(inner) = helpers::expr_name(func) else {

View file

@ -1,4 +1,4 @@
use rustpython_parser::ast::{self, Expr, ExprKind, Keyword}; use rustpython_parser::ast::{self, Expr, Keyword, Ranged};
use ruff_diagnostics::{AlwaysAutofixableViolation, Diagnostic}; use ruff_diagnostics::{AlwaysAutofixableViolation, Diagnostic};
use ruff_macros::{derive_message_formats, violation}; use ruff_macros::{derive_message_formats, violation};
@ -53,9 +53,9 @@ pub(crate) fn unnecessary_generator_dict(
let Some(argument) = helpers::exactly_one_argument_with_matching_function("dict", func, args, keywords) else { let Some(argument) = helpers::exactly_one_argument_with_matching_function("dict", func, args, keywords) else {
return; return;
}; };
if let ExprKind::GeneratorExp(ast::ExprGeneratorExp { elt, .. }) = argument { if let Expr::GeneratorExp(ast::ExprGeneratorExp { elt, .. }) = argument {
match &elt.node { match elt.as_ref() {
ExprKind::Tuple(ast::ExprTuple { elts, .. }) if elts.len() == 2 => { Expr::Tuple(ast::ExprTuple { elts, .. }) if elts.len() == 2 => {
let mut diagnostic = Diagnostic::new(UnnecessaryGeneratorDict, expr.range()); let mut diagnostic = Diagnostic::new(UnnecessaryGeneratorDict, expr.range());
if checker.patch(diagnostic.kind.rule()) { if checker.patch(diagnostic.kind.rule()) {
#[allow(deprecated)] #[allow(deprecated)]

View file

@ -1,4 +1,4 @@
use rustpython_parser::ast::{Expr, ExprKind, Keyword}; use rustpython_parser::ast::{Expr, Keyword, Ranged};
use ruff_diagnostics::{AlwaysAutofixableViolation, Diagnostic}; use ruff_diagnostics::{AlwaysAutofixableViolation, Diagnostic};
use ruff_macros::{derive_message_formats, violation}; use ruff_macros::{derive_message_formats, violation};
@ -55,7 +55,7 @@ pub(crate) fn unnecessary_generator_list(
if !checker.ctx.is_builtin("list") { if !checker.ctx.is_builtin("list") {
return; return;
} }
if let ExprKind::GeneratorExp(_) = argument { if let Expr::GeneratorExp(_) = argument {
let mut diagnostic = Diagnostic::new(UnnecessaryGeneratorList, expr.range()); let mut diagnostic = Diagnostic::new(UnnecessaryGeneratorList, expr.range());
if checker.patch(diagnostic.kind.rule()) { if checker.patch(diagnostic.kind.rule()) {
#[allow(deprecated)] #[allow(deprecated)]

View file

@ -1,4 +1,4 @@
use rustpython_parser::ast::{Expr, ExprKind, Keyword}; use rustpython_parser::ast::{Expr, Keyword, Ranged};
use ruff_diagnostics::{AlwaysAutofixableViolation, Diagnostic}; use ruff_diagnostics::{AlwaysAutofixableViolation, Diagnostic};
use ruff_macros::{derive_message_formats, violation}; use ruff_macros::{derive_message_formats, violation};
@ -56,7 +56,7 @@ pub(crate) fn unnecessary_generator_set(
if !checker.ctx.is_builtin("set") { if !checker.ctx.is_builtin("set") {
return; return;
} }
if let ExprKind::GeneratorExp(_) = argument { if let Expr::GeneratorExp(_) = argument {
let mut diagnostic = Diagnostic::new(UnnecessaryGeneratorSet, expr.range()); let mut diagnostic = Diagnostic::new(UnnecessaryGeneratorSet, expr.range());
if checker.patch(diagnostic.kind.rule()) { if checker.patch(diagnostic.kind.rule()) {
#[allow(deprecated)] #[allow(deprecated)]

View file

@ -1,4 +1,4 @@
use rustpython_parser::ast::Expr; use rustpython_parser::ast::{Expr, Ranged};
use crate::checkers::ast::Checker; use crate::checkers::ast::Checker;
use crate::registry::AsRule; use crate::registry::AsRule;

View file

@ -1,4 +1,4 @@
use rustpython_parser::ast::{self, Expr, ExprKind, Keyword}; use rustpython_parser::ast::{self, Expr, Keyword, Ranged};
use crate::checkers::ast::Checker; use crate::checkers::ast::Checker;
use crate::registry::AsRule; use crate::registry::AsRule;
@ -52,10 +52,10 @@ pub(crate) fn unnecessary_list_comprehension_dict(
if !checker.ctx.is_builtin("dict") { if !checker.ctx.is_builtin("dict") {
return; return;
} }
let ExprKind::ListComp(ast::ExprListComp { elt, .. }) = &argument else { let Expr::ListComp(ast::ExprListComp { elt, .. }) = argument else {
return; return;
}; };
let ExprKind::Tuple(ast::ExprTuple { elts, .. }) = &elt.node else { let Expr::Tuple(ast::ExprTuple { elts, .. }) = elt.as_ref() else {
return; return;
}; };
if elts.len() != 2 { if elts.len() != 2 {

View file

@ -1,4 +1,4 @@
use rustpython_parser::ast::{Expr, Keyword}; use rustpython_parser::ast::{Expr, Keyword, Ranged};
use ruff_diagnostics::{AlwaysAutofixableViolation, Diagnostic}; use ruff_diagnostics::{AlwaysAutofixableViolation, Diagnostic};
use ruff_macros::{derive_message_formats, violation}; use ruff_macros::{derive_message_formats, violation};

View file

@ -1,4 +1,4 @@
use rustpython_parser::ast::{self, Expr, ExprKind, Keyword}; use rustpython_parser::ast::{self, Expr, Keyword, Ranged};
use crate::checkers::ast::Checker; use crate::checkers::ast::Checker;
use crate::registry::AsRule; use crate::registry::AsRule;
@ -60,14 +60,15 @@ pub(crate) fn unnecessary_literal_dict(
return; return;
} }
let (kind, elts) = match argument { let (kind, elts) = match argument {
ExprKind::Tuple(ast::ExprTuple { elts, .. }) => ("tuple", elts), Expr::Tuple(ast::ExprTuple { elts, .. }) => ("tuple", elts),
ExprKind::List(ast::ExprList { elts, .. }) => ("list", elts), Expr::List(ast::ExprList { elts, .. }) => ("list", elts),
_ => return, _ => return,
}; };
// Accept `dict((1, 2), ...))` `dict([(1, 2), ...])`. // Accept `dict((1, 2), ...))` `dict([(1, 2), ...])`.
if !elts.iter().all( if !elts
|elt| matches!(&elt.node, ExprKind::Tuple(ast::ExprTuple { elts, .. } )if elts.len() == 2), .iter()
) { .all(|elt| matches!(&elt, Expr::Tuple(ast::ExprTuple { elts, .. } )if elts.len() == 2))
{
return; return;
} }
let mut diagnostic = Diagnostic::new( let mut diagnostic = Diagnostic::new(

View file

@ -1,4 +1,4 @@
use rustpython_parser::ast::{Expr, ExprKind, Keyword}; use rustpython_parser::ast::{Expr, Keyword, Ranged};
use crate::checkers::ast::Checker; use crate::checkers::ast::Checker;
use crate::registry::AsRule; use crate::registry::AsRule;
@ -61,8 +61,8 @@ pub(crate) fn unnecessary_literal_set(
return; return;
} }
let kind = match argument { let kind = match argument {
ExprKind::List(_) => "list", Expr::List(_) => "list",
ExprKind::Tuple(_) => "tuple", Expr::Tuple(_) => "tuple",
_ => return, _ => return,
}; };
let mut diagnostic = Diagnostic::new( let mut diagnostic = Diagnostic::new(

View file

@ -1,4 +1,4 @@
use rustpython_parser::ast::{Expr, ExprKind, Keyword}; use rustpython_parser::ast::{Expr, Keyword, Ranged};
use std::fmt; use std::fmt;
use ruff_diagnostics::{AlwaysAutofixableViolation, Diagnostic}; use ruff_diagnostics::{AlwaysAutofixableViolation, Diagnostic};
@ -79,8 +79,8 @@ pub(crate) fn unnecessary_literal_within_dict_call(
return; return;
} }
let argument_kind = match argument { let argument_kind = match argument {
ExprKind::DictComp(_) => DictKind::Comprehension, Expr::DictComp(_) => DictKind::Comprehension,
ExprKind::Dict(_) => DictKind::Literal, Expr::Dict(_) => DictKind::Literal,
_ => return, _ => return,
}; };
let mut diagnostic = Diagnostic::new( let mut diagnostic = Diagnostic::new(

View file

@ -1,4 +1,4 @@
use rustpython_parser::ast::{Expr, ExprKind, Keyword}; use rustpython_parser::ast::{Expr, Keyword, Ranged};
use crate::checkers::ast::Checker; use crate::checkers::ast::Checker;
use crate::registry::AsRule; use crate::registry::AsRule;
@ -82,8 +82,8 @@ pub(crate) fn unnecessary_literal_within_list_call(
return; return;
} }
let argument_kind = match argument { let argument_kind = match argument {
ExprKind::Tuple(_) => "tuple", Expr::Tuple(_) => "tuple",
ExprKind::List(_) => "list", Expr::List(_) => "list",
_ => return, _ => return,
}; };
let mut diagnostic = Diagnostic::new( let mut diagnostic = Diagnostic::new(

View file

@ -1,4 +1,4 @@
use rustpython_parser::ast::{Expr, ExprKind, Keyword}; use rustpython_parser::ast::{Expr, Keyword, Ranged};
use crate::checkers::ast::Checker; use crate::checkers::ast::Checker;
use crate::registry::AsRule; use crate::registry::AsRule;
@ -83,8 +83,8 @@ pub(crate) fn unnecessary_literal_within_tuple_call(
return; return;
} }
let argument_kind = match argument { let argument_kind = match argument {
ExprKind::Tuple(_) => "tuple", Expr::Tuple(_) => "tuple",
ExprKind::List(_) => "list", Expr::List(_) => "list",
_ => return, _ => return,
}; };
let mut diagnostic = Diagnostic::new( let mut diagnostic = Diagnostic::new(

View file

@ -1,5 +1,5 @@
use ruff_text_size::TextRange; use ruff_text_size::TextRange;
use rustpython_parser::ast::{self, Expr, ExprKind}; use rustpython_parser::ast::{self, Expr, Ranged};
use ruff_diagnostics::Diagnostic; use ruff_diagnostics::Diagnostic;
use ruff_diagnostics::{AutofixKind, Violation}; use ruff_diagnostics::{AutofixKind, Violation};
@ -93,17 +93,15 @@ pub(crate) fn unnecessary_map(
} }
// Exclude the parent if already matched by other arms // Exclude the parent if already matched by other arms
if let Some(parent) = parent { if let Some(Expr::Call(ast::ExprCall { func: f, .. })) = parent {
if let ExprKind::Call(ast::ExprCall { func: f, .. }) = &parent.node {
if let Some(id_parent) = helpers::expr_name(f) { if let Some(id_parent) = helpers::expr_name(f) {
if id_parent == "dict" || id_parent == "set" || id_parent == "list" { if id_parent == "dict" || id_parent == "set" || id_parent == "list" {
return; return;
} }
} }
}; };
};
if args.len() == 2 && matches!(&args[0].node, ExprKind::Lambda(_)) { if args.len() == 2 && matches!(&args[0], Expr::Lambda(_)) {
let mut diagnostic = create_diagnostic("generator", expr.range()); let mut diagnostic = create_diagnostic("generator", expr.range());
if checker.patch(diagnostic.kind.rule()) { if checker.patch(diagnostic.kind.rule()) {
#[allow(deprecated)] #[allow(deprecated)]
@ -125,15 +123,14 @@ pub(crate) fn unnecessary_map(
return; return;
} }
if let Some(arg) = args.first() { if let Some(Expr::Call(ast::ExprCall { func, args, .. })) = args.first() {
if let ExprKind::Call(ast::ExprCall { func, args, .. }) = &arg.node {
if args.len() != 2 { if args.len() != 2 {
return; return;
} }
let Some(argument) = helpers::first_argument_with_matching_function("map", func, args) else { let Some(argument) = helpers::first_argument_with_matching_function("map", func, args) else {
return; return;
}; };
if let ExprKind::Lambda(_) = argument { if let Expr::Lambda(_) = argument {
let mut diagnostic = create_diagnostic(id, expr.range()); let mut diagnostic = create_diagnostic(id, expr.range());
if checker.patch(diagnostic.kind.rule()) { if checker.patch(diagnostic.kind.rule()) {
#[allow(deprecated)] #[allow(deprecated)]
@ -151,19 +148,18 @@ pub(crate) fn unnecessary_map(
} }
} }
} }
}
"dict" => { "dict" => {
if !checker.ctx.is_builtin(id) { if !checker.ctx.is_builtin(id) {
return; return;
} }
if args.len() == 1 { if args.len() == 1 {
if let ExprKind::Call(ast::ExprCall { func, args, .. }) = &args[0].node { if let Expr::Call(ast::ExprCall { func, args, .. }) = &args[0] {
let Some(argument) = helpers::first_argument_with_matching_function("map", func, args) else { let Some(argument) = helpers::first_argument_with_matching_function("map", func, args) else {
return; return;
}; };
if let ExprKind::Lambda(ast::ExprLambda { body, .. }) = &argument { if let Expr::Lambda(ast::ExprLambda { body, .. }) = argument {
if matches!(&body.node, ExprKind::Tuple(ast::ExprTuple { elts, .. }) | ExprKind::List(ast::ExprList { elts, .. } )if elts.len() == 2) if matches!(body.as_ref(), Expr::Tuple(ast::ExprTuple { elts, .. }) | Expr::List(ast::ExprList { elts, .. } ) if elts.len() == 2)
{ {
let mut diagnostic = create_diagnostic(id, expr.range()); let mut diagnostic = create_diagnostic(id, expr.range());
if checker.patch(diagnostic.kind.rule()) { if checker.patch(diagnostic.kind.rule()) {

View file

@ -1,5 +1,5 @@
use num_bigint::BigInt; use num_bigint::BigInt;
use rustpython_parser::ast::{self, Constant, Expr, ExprKind, Unaryop}; use rustpython_parser::ast::{self, Constant, Expr, Ranged, Unaryop};
use crate::checkers::ast::Checker; use crate::checkers::ast::Checker;
use ruff_diagnostics::{Diagnostic, Violation}; use ruff_diagnostics::{Diagnostic, Violation};
@ -60,10 +60,10 @@ pub(crate) fn unnecessary_subscript_reversal(
if !checker.ctx.is_builtin(id) { if !checker.ctx.is_builtin(id) {
return; return;
} }
let ExprKind::Subscript(ast::ExprSubscript { slice, .. }) = &first_arg.node else { let Expr::Subscript(ast::ExprSubscript { slice, .. }) = first_arg else {
return; return;
}; };
let ExprKind::Slice(ast::ExprSlice { lower, upper, step }) = &slice.node else { let Expr::Slice(ast::ExprSlice { lower, upper, step, range: _ }) = slice.as_ref() else {
return; return;
}; };
if lower.is_some() || upper.is_some() { if lower.is_some() || upper.is_some() {
@ -72,16 +72,17 @@ pub(crate) fn unnecessary_subscript_reversal(
let Some(step) = step.as_ref() else { let Some(step) = step.as_ref() else {
return; return;
}; };
let ExprKind::UnaryOp(ast::ExprUnaryOp { let Expr::UnaryOp(ast::ExprUnaryOp {
op: Unaryop::USub, op: Unaryop::USub,
operand, operand,
}) = &step.node else { range: _,
}) = step.as_ref() else {
return; return;
}; };
let ExprKind::Constant(ast::ExprConstant { let Expr::Constant(ast::ExprConstant {
value: Constant::Int(val), value: Constant::Int(val),
.. ..
}) = &operand.node else { }) = operand.as_ref() else {
return; return;
}; };
if *val != BigInt::from(1) { if *val != BigInt::from(1) {

View file

@ -1,5 +1,5 @@
use ruff_text_size::TextRange; use ruff_text_size::TextRange;
use rustpython_parser::ast::{self, Constant, Expr, ExprKind, Keyword}; use rustpython_parser::ast::{self, Constant, Expr, Keyword};
use ruff_diagnostics::{Diagnostic, Violation}; use ruff_diagnostics::{Diagnostic, Violation};
use ruff_macros::{derive_message_formats, violation}; use ruff_macros::{derive_message_formats, violation};
@ -321,10 +321,11 @@ pub(crate) fn call_datetime_strptime_without_zone(
} }
// Does the `strptime` call contain a format string with a timezone specifier? // Does the `strptime` call contain a format string with a timezone specifier?
if let Some(ExprKind::Constant(ast::ExprConstant { if let Some(Expr::Constant(ast::ExprConstant {
value: Constant::Str(format), value: Constant::Str(format),
kind: None, kind: None,
})) = args.get(1).as_ref().map(|arg| &arg.node) range: _,
})) = args.get(1).as_ref()
{ {
if format.contains("%z") { if format.contains("%z") {
return; return;
@ -339,8 +340,8 @@ pub(crate) fn call_datetime_strptime_without_zone(
return; return;
}; };
if let ExprKind::Call(ast::ExprCall { keywords, .. }) = &grandparent.node { if let Expr::Call(ast::ExprCall { keywords, .. }) = grandparent {
if let ExprKind::Attribute(ast::ExprAttribute { attr, .. }) = &parent.node { if let Expr::Attribute(ast::ExprAttribute { attr, .. }) = parent {
let attr = attr.as_str(); let attr = attr.as_str();
// Ex) `datetime.strptime(...).astimezone()` // Ex) `datetime.strptime(...).astimezone()`
if attr == "astimezone" { if attr == "astimezone" {

View file

@ -1,4 +1,4 @@
use rustpython_parser::ast::{Expr, Stmt}; use rustpython_parser::ast::{Expr, Ranged, Stmt};
use crate::checkers::ast::Checker; use crate::checkers::ast::Checker;
use crate::rules::flake8_debugger::types::DebuggerUsingType; use crate::rules::flake8_debugger::types::DebuggerUsingType;

View file

@ -1,4 +1,4 @@
use rustpython_parser::ast::{self, Constant, Expr, ExprKind, Stmt, StmtKind}; use rustpython_parser::ast::{self, Constant, Expr, Ranged, Stmt};
use ruff_diagnostics::{Diagnostic, Violation}; use ruff_diagnostics::{Diagnostic, Violation};
use ruff_macros::{derive_message_formats, violation}; use ruff_macros::{derive_message_formats, violation};
@ -56,27 +56,27 @@ pub(crate) fn all_with_model_form(
return None; return None;
} }
for element in body.iter() { for element in body.iter() {
let StmtKind::ClassDef(ast::StmtClassDef { name, body, .. }) = &element.node else { let Stmt::ClassDef(ast::StmtClassDef { name, body, .. }) = element else {
continue; continue;
}; };
if name != "Meta" { if name != "Meta" {
continue; continue;
} }
for element in body.iter() { for element in body.iter() {
let StmtKind::Assign(ast::StmtAssign { targets, value, .. }) = &element.node else { let Stmt::Assign(ast::StmtAssign { targets, value, .. }) = element else {
continue; continue;
}; };
for target in targets.iter() { for target in targets.iter() {
let ExprKind::Name(ast::ExprName { id, .. }) = &target.node else { let Expr::Name(ast::ExprName { id, .. }) = target else {
continue; continue;
}; };
if id != "fields" { if id != "fields" {
continue; continue;
} }
let ExprKind::Constant(ast::ExprConstant { value, .. }) = &value.node else { let Expr::Constant(ast::ExprConstant { value, .. }) = value.as_ref() else {
continue; continue;
}; };
match &value { match value {
Constant::Str(s) => { Constant::Str(s) => {
if s == "__all__" { if s == "__all__" {
return Some(Diagnostic::new(DjangoAllWithModelForm, element.range())); return Some(Diagnostic::new(DjangoAllWithModelForm, element.range()));

View file

@ -1,4 +1,4 @@
use rustpython_parser::ast::{self, Expr, ExprKind, Stmt, StmtKind}; use rustpython_parser::ast::{self, Expr, Ranged, Stmt};
use ruff_diagnostics::{Diagnostic, Violation}; use ruff_diagnostics::{Diagnostic, Violation};
use ruff_macros::{derive_message_formats, violation}; use ruff_macros::{derive_message_formats, violation};
@ -54,18 +54,18 @@ pub(crate) fn exclude_with_model_form(
return None; return None;
} }
for element in body.iter() { for element in body.iter() {
let StmtKind::ClassDef(ast::StmtClassDef { name, body, .. }) = &element.node else { let Stmt::ClassDef(ast::StmtClassDef { name, body, .. }) = element else {
continue; continue;
}; };
if name != "Meta" { if name != "Meta" {
continue; continue;
} }
for element in body.iter() { for element in body.iter() {
let StmtKind::Assign(ast::StmtAssign { targets, .. }) = &element.node else { let Stmt::Assign(ast::StmtAssign { targets, .. }) = element else {
continue; continue;
}; };
for target in targets.iter() { for target in targets.iter() {
let ExprKind::Name(ast::ExprName { id, .. }) = &target.node else { let Expr::Name(ast::ExprName { id, .. }) = target else {
continue; continue;
}; };
if id == "exclude" { if id == "exclude" {

View file

@ -1,4 +1,4 @@
use rustpython_parser::ast::{self, Expr, ExprKind, Keyword}; use rustpython_parser::ast::{self, Expr, Keyword, Ranged};
use ruff_diagnostics::{Diagnostic, Violation}; use ruff_diagnostics::{Diagnostic, Violation};
use ruff_macros::{derive_message_formats, violation}; use ruff_macros::{derive_message_formats, violation};
@ -64,17 +64,14 @@ pub(crate) fn locals_in_render_function(
return; return;
} }
&args[2] &args[2]
} else if let Some(keyword) = keywords.iter().find(|keyword| { } else if let Some(keyword) = keywords
keyword .iter()
.node .find(|keyword| keyword.arg.as_ref().map_or(false, |arg| arg == "context"))
.arg {
.as_ref() if !is_locals_call(checker, &keyword.value) {
.map_or(false, |arg| arg == "context")
}) {
if !is_locals_call(checker, &keyword.node.value) {
return; return;
} }
&keyword.node.value &keyword.value
} else { } else {
return; return;
}; };
@ -86,7 +83,7 @@ pub(crate) fn locals_in_render_function(
} }
fn is_locals_call(checker: &Checker, expr: &Expr) -> bool { fn is_locals_call(checker: &Checker, expr: &Expr) -> bool {
let ExprKind::Call(ast::ExprCall { func, .. }) = &expr.node else { let Expr::Call(ast::ExprCall { func, .. }) = expr else {
return false return false
}; };
checker checker

View file

@ -1,4 +1,4 @@
use rustpython_parser::ast::{self, Constant, Expr, ExprKind, Stmt, StmtKind}; use rustpython_parser::ast::{self, Constant, Expr, Ranged, Stmt};
use ruff_diagnostics::{Diagnostic, Violation}; use ruff_diagnostics::{Diagnostic, Violation};
use ruff_macros::{derive_message_formats, violation}; use ruff_macros::{derive_message_formats, violation};
@ -68,8 +68,8 @@ pub(crate) fn model_without_dunder_str(
} }
fn has_dunder_method(body: &[Stmt]) -> bool { fn has_dunder_method(body: &[Stmt]) -> bool {
body.iter().any(|val| match &val.node { body.iter().any(|val| match val {
StmtKind::FunctionDef(ast::StmtFunctionDef { name, .. }) => { Stmt::FunctionDef(ast::StmtFunctionDef { name, .. }) => {
if name == "__str__" { if name == "__str__" {
return true; return true;
} }
@ -94,24 +94,24 @@ fn checker_applies(checker: &Checker, bases: &[Expr], body: &[Stmt]) -> bool {
/// 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(body: &[Stmt]) -> bool { fn is_model_abstract(body: &[Stmt]) -> bool {
for element in body.iter() { for element in body.iter() {
let StmtKind::ClassDef(ast::StmtClassDef {name, body, ..}) = &element.node else { let Stmt::ClassDef(ast::StmtClassDef {name, body, ..}) = element else {
continue continue
}; };
if name != "Meta" { if name != "Meta" {
continue; continue;
} }
for element in body.iter() { for element in body.iter() {
let StmtKind::Assign(ast::StmtAssign {targets, value, ..}) = &element.node else { let Stmt::Assign(ast::StmtAssign {targets, value, ..}) = element else {
continue; continue;
}; };
for target in targets.iter() { for target in targets.iter() {
let ExprKind::Name(ast::ExprName {id , ..}) = &target.node else { let Expr::Name(ast::ExprName {id , ..}) = target else {
continue; continue;
}; };
if id != "abstract" { if id != "abstract" {
continue; continue;
} }
let ExprKind::Constant(ast::ExprConstant{value: Constant::Bool(true), ..}) = &value.node else { let Expr::Constant(ast::ExprConstant{value: Constant::Bool(true), ..}) = value.as_ref() else {
continue; continue;
}; };
return true; return true;

View file

@ -1,4 +1,4 @@
use rustpython_parser::ast::{self, Expr, ExprKind}; use rustpython_parser::ast::{self, Expr, Ranged};
use ruff_diagnostics::{Diagnostic, Violation}; use ruff_diagnostics::{Diagnostic, Violation};
use ruff_macros::{derive_message_formats, violation}; use ruff_macros::{derive_message_formats, violation};
@ -58,8 +58,8 @@ where
let mut diagnostics = vec![]; let mut diagnostics = vec![];
let mut seen_receiver = false; let mut seen_receiver = false;
for (i, decorator) in decorator_list.iter().enumerate() { for (i, decorator) in decorator_list.iter().enumerate() {
let is_receiver = match &decorator.node { let is_receiver = match decorator {
ExprKind::Call(ast::ExprCall { func, .. }) => resolve_call_path(func) Expr::Call(ast::ExprCall { func, .. }) => resolve_call_path(func)
.map_or(false, |call_path| { .map_or(false, |call_path| {
call_path.as_slice() == ["django", "dispatch", "receiver"] call_path.as_slice() == ["django", "dispatch", "receiver"]
}), }),

View file

@ -1,5 +1,5 @@
use rustpython_parser::ast::Constant::Bool; use rustpython_parser::ast::Constant::Bool;
use rustpython_parser::ast::{self, Expr, ExprKind, Stmt, StmtKind}; use rustpython_parser::ast::{self, Expr, Ranged, Stmt};
use ruff_diagnostics::{Diagnostic, Violation}; use ruff_diagnostics::{Diagnostic, Violation};
use ruff_macros::{derive_message_formats, violation}; use ruff_macros::{derive_message_formats, violation};
@ -64,7 +64,7 @@ const NOT_NULL_TRUE_FIELDS: [&str; 6] = [
pub(crate) fn nullable_model_string_field(checker: &Checker, body: &[Stmt]) -> Vec<Diagnostic> { pub(crate) fn nullable_model_string_field(checker: &Checker, body: &[Stmt]) -> Vec<Diagnostic> {
let mut errors = Vec::new(); let mut errors = Vec::new();
for statement in body.iter() { for statement in body.iter() {
let StmtKind::Assign(ast::StmtAssign {value, ..}) = &statement.node else { let Stmt::Assign(ast::StmtAssign {value, ..}) = statement else {
continue continue
}; };
if let Some(field_name) = is_nullable_field(checker, value) { if let Some(field_name) = is_nullable_field(checker, value) {
@ -80,7 +80,7 @@ pub(crate) fn nullable_model_string_field(checker: &Checker, body: &[Stmt]) -> V
} }
fn is_nullable_field<'a>(checker: &'a Checker, value: &'a Expr) -> Option<&'a str> { fn is_nullable_field<'a>(checker: &'a Checker, value: &'a Expr) -> Option<&'a str> {
let ExprKind::Call(ast::ExprCall {func, keywords, ..}) = &value.node else { let Expr::Call(ast::ExprCall {func, keywords, ..}) = value else {
return None; return None;
}; };
@ -96,10 +96,10 @@ fn is_nullable_field<'a>(checker: &'a Checker, value: &'a Expr) -> Option<&'a st
let mut blank_key = false; let mut blank_key = false;
let mut unique_key = false; let mut unique_key = false;
for keyword in keywords.iter() { for keyword in keywords.iter() {
let ExprKind::Constant(ast::ExprConstant {value: Bool(true), ..}) = &keyword.node.value.node else { let Expr::Constant(ast::ExprConstant {value: Bool(true), ..}) = &keyword.value else {
continue continue
}; };
let Some(argument) = &keyword.node.arg else { let Some(argument) = &keyword.arg else {
continue continue
}; };
match argument.as_str() { match argument.as_str() {

View file

@ -1,6 +1,6 @@
use std::fmt; use std::fmt;
use rustpython_parser::ast::{self, Expr, ExprKind, Stmt, StmtKind}; use rustpython_parser::ast::{self, Expr, Ranged, Stmt};
use ruff_diagnostics::{Diagnostic, Violation}; use ruff_diagnostics::{Diagnostic, Violation};
use ruff_macros::{derive_message_formats, violation}; use ruff_macros::{derive_message_formats, violation};
@ -99,10 +99,10 @@ impl fmt::Display for ContentType {
} }
} }
fn get_element_type(checker: &Checker, element: &StmtKind) -> Option<ContentType> { fn get_element_type(checker: &Checker, element: &Stmt) -> Option<ContentType> {
match element { match element {
StmtKind::Assign(ast::StmtAssign { targets, value, .. }) => { Stmt::Assign(ast::StmtAssign { targets, value, .. }) => {
if let ExprKind::Call(ast::ExprCall { func, .. }) = &value.node { if let Expr::Call(ast::ExprCall { func, .. }) = value.as_ref() {
if helpers::is_model_field(&checker.ctx, func) { if helpers::is_model_field(&checker.ctx, func) {
return Some(ContentType::FieldDeclaration); return Some(ContentType::FieldDeclaration);
} }
@ -110,7 +110,7 @@ fn get_element_type(checker: &Checker, element: &StmtKind) -> Option<ContentType
let Some(expr) = targets.first() else { let Some(expr) = targets.first() else {
return None; return None;
}; };
let ExprKind::Name(ast::ExprName { id, .. }) = &expr.node else { let Expr::Name(ast::ExprName { id, .. }) = expr else {
return None; return None;
}; };
if id == "objects" { if id == "objects" {
@ -119,14 +119,14 @@ fn get_element_type(checker: &Checker, element: &StmtKind) -> Option<ContentType
None None
} }
} }
StmtKind::ClassDef(ast::StmtClassDef { name, .. }) => { Stmt::ClassDef(ast::StmtClassDef { name, .. }) => {
if name == "Meta" { if name == "Meta" {
Some(ContentType::MetaClass) Some(ContentType::MetaClass)
} else { } else {
None None
} }
} }
StmtKind::FunctionDef(ast::StmtFunctionDef { name, .. }) => match name.as_str() { Stmt::FunctionDef(ast::StmtFunctionDef { name, .. }) => match name.as_str() {
"__str__" => Some(ContentType::StrMethod), "__str__" => Some(ContentType::StrMethod),
"save" => Some(ContentType::SaveMethod), "save" => Some(ContentType::SaveMethod),
"get_absolute_url" => Some(ContentType::GetAbsoluteUrlMethod), "get_absolute_url" => Some(ContentType::GetAbsoluteUrlMethod),
@ -150,7 +150,7 @@ pub(crate) fn unordered_body_content_in_model(
} }
let mut elements_type_found = Vec::new(); let mut elements_type_found = Vec::new();
for element in body.iter() { for element in body.iter() {
let Some(current_element_type) = get_element_type(checker, &element.node) else { let Some(current_element_type) = get_element_type(checker, element) else {
continue; continue;
}; };
let Some(&element_type) = elements_type_found let Some(&element_type) = elements_type_found

View file

@ -1,8 +1,9 @@
use rustpython_parser::ast::{self, Constant, Expr, ExprContext, ExprKind, Stmt, StmtKind}; use ruff_text_size::TextRange;
use rustpython_parser::ast::{self, Constant, Expr, ExprContext, Ranged, Stmt};
use ruff_diagnostics::{AutofixKind, Diagnostic, Edit, Fix, Violation}; use ruff_diagnostics::{AutofixKind, Diagnostic, Edit, Fix, Violation};
use ruff_macros::{derive_message_formats, violation}; use ruff_macros::{derive_message_formats, violation};
use ruff_python_ast::helpers::{create_expr, create_stmt, unparse_stmt}; use ruff_python_ast::helpers::unparse_stmt;
use ruff_python_ast::source_code::Stylist; use ruff_python_ast::source_code::Stylist;
use ruff_python_ast::whitespace; use ruff_python_ast::whitespace;
@ -183,17 +184,18 @@ impl Violation for DotFormatInException {
/// `raise` statement. The variable name is `msg`. /// `raise` statement. The variable name is `msg`.
/// 2. Replace the exception argument with the variable name. /// 2. Replace the exception argument with the variable name.
fn generate_fix(stylist: &Stylist, stmt: &Stmt, exc_arg: &Expr, indentation: &str) -> Fix { fn generate_fix(stylist: &Stylist, stmt: &Stmt, exc_arg: &Expr, indentation: &str) -> Fix {
let assignment = unparse_stmt( let node = Expr::Name(ast::ExprName {
&create_stmt(StmtKind::Assign(ast::StmtAssign {
targets: vec![create_expr(ExprKind::Name(ast::ExprName {
id: "msg".into(), id: "msg".into(),
ctx: ExprContext::Store, ctx: ExprContext::Store,
}))], range: TextRange::default(),
});
let node1 = Stmt::Assign(ast::StmtAssign {
targets: vec![node],
value: Box::new(exc_arg.clone()), value: Box::new(exc_arg.clone()),
type_comment: None, type_comment: None,
})), range: TextRange::default(),
stylist, });
); let assignment = unparse_stmt(&node1, stylist);
#[allow(deprecated)] #[allow(deprecated)]
Fix::unspecified_edits( Fix::unspecified_edits(
Edit::insertion( Edit::insertion(
@ -214,11 +216,11 @@ fn generate_fix(stylist: &Stylist, stmt: &Stmt, exc_arg: &Expr, indentation: &st
/// EM101, EM102, EM103 /// EM101, EM102, EM103
pub(crate) fn string_in_exception(checker: &mut Checker, stmt: &Stmt, exc: &Expr) { pub(crate) fn string_in_exception(checker: &mut Checker, stmt: &Stmt, exc: &Expr) {
if let ExprKind::Call(ast::ExprCall { args, .. }) = &exc.node { if let Expr::Call(ast::ExprCall { args, .. }) = exc {
if let Some(first) = args.first() { if let Some(first) = args.first() {
match &first.node { match first {
// Check for string literals // Check for string literals
ExprKind::Constant(ast::ExprConstant { Expr::Constant(ast::ExprConstant {
value: Constant::Str(string), value: Constant::Str(string),
.. ..
}) => { }) => {
@ -249,7 +251,7 @@ pub(crate) fn string_in_exception(checker: &mut Checker, stmt: &Stmt, exc: &Expr
} }
} }
// Check for f-strings // Check for f-strings
ExprKind::JoinedStr(_) => { Expr::JoinedStr(_) => {
if checker.settings.rules.enabled(Rule::FStringInException) { if checker.settings.rules.enabled(Rule::FStringInException) {
let indentation = whitespace::indentation(checker.locator, stmt).and_then( let indentation = whitespace::indentation(checker.locator, stmt).and_then(
|indentation| { |indentation| {
@ -275,12 +277,12 @@ pub(crate) fn string_in_exception(checker: &mut Checker, stmt: &Stmt, exc: &Expr
} }
} }
// Check for .format() calls // Check for .format() calls
ExprKind::Call(ast::ExprCall { func, .. }) => { Expr::Call(ast::ExprCall { func, .. }) => {
if checker.settings.rules.enabled(Rule::DotFormatInException) { if checker.settings.rules.enabled(Rule::DotFormatInException) {
if let ExprKind::Attribute(ast::ExprAttribute { value, attr, .. }) = if let Expr::Attribute(ast::ExprAttribute { value, attr, .. }) =
&func.node func.as_ref()
{ {
if attr == "format" && matches!(value.node, ExprKind::Constant(_)) { if attr == "format" && value.is_constant_expr() {
let indentation = whitespace::indentation(checker.locator, stmt) let indentation = whitespace::indentation(checker.locator, stmt)
.and_then(|indentation| { .and_then(|indentation| {
if checker.ctx.find_binding("msg").is_none() { if checker.ctx.find_binding("msg").is_none() {

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