[ty] AST garbage collection (#18482)

## Summary

Garbage collect ASTs once we are done checking a given file. Queries
with a cross-file dependency on the AST will reparse the file on demand.
This reduces ty's peak memory usage by ~20-30%.

The primary change of this PR is adding a `node_index` field to every
AST node, that is assigned by the parser. `ParsedModule` can use this to
create a flat index of AST nodes any time the file is parsed (or
reparsed). This allows `AstNodeRef` to simply index into the current
instance of the `ParsedModule`, instead of storing a pointer directly.

The indices are somewhat hackily (using an atomic integer) assigned by
the `parsed_module` query instead of by the parser directly. Assigning
the indices in source-order in the (recursive) parser turns out to be
difficult, and collecting the nodes during semantic indexing is
impossible as `SemanticIndex` does not hold onto a specific
`ParsedModuleRef`, which the pointers in the flat AST are tied to. This
means that we have to do an extra AST traversal to assign and collect
the nodes into a flat index, but the small performance impact (~3% on
cold runs) seems worth it for the memory savings.

Part of https://github.com/astral-sh/ty/issues/214.
This commit is contained in:
Ibraheem Ahmed 2025-06-13 08:40:11 -04:00 committed by GitHub
parent 76d9009a6e
commit c9dff5c7d5
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
824 changed files with 25243 additions and 804 deletions

7
Cargo.lock generated
View file

@ -132,6 +132,12 @@ version = "1.0.98"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e16d2d3311acee920a9eb8d33b8cbc1787ce4a264e85f964c2404b969bdcd487"
[[package]]
name = "arc-swap"
version = "1.7.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "69f7f8c3906b62b754cd5326047894316021dcfe5a194c8ea52bdd94934a3457"
[[package]]
name = "argfile"
version = "0.2.1"
@ -2612,6 +2618,7 @@ name = "ruff_db"
version = "0.0.0"
dependencies = [
"anstyle",
"arc-swap",
"camino",
"countme",
"dashmap 6.1.0",

View file

@ -51,6 +51,7 @@ aho-corasick = { version = "1.1.3" }
anstream = { version = "0.6.18" }
anstyle = { version = "1.0.10" }
anyhow = { version = "1.0.80" }
arc-swap = { version = "1.7.1" }
assert_fs = { version = "1.1.0" }
argfile = { version = "0.2.0" }
bincode = { version = "2.0.0" }

View file

@ -21,6 +21,7 @@ ruff_source_file = { workspace = true }
ruff_text_size = { workspace = true }
anstyle = { workspace = true }
arc-swap = { workspace = true }
camino = { workspace = true }
countme = { workspace = true }
dashmap = { workspace = true }

View file

@ -1,7 +1,8 @@
use std::fmt::Formatter;
use std::sync::Arc;
use ruff_python_ast::ModModule;
use arc_swap::ArcSwapOption;
use ruff_python_ast::{AnyRootNodeRef, ModModule, NodeIndex};
use ruff_python_parser::{ParseOptions, Parsed, parse_unchecked};
use crate::Db;
@ -23,16 +24,20 @@ use crate::source::source_text;
pub fn parsed_module(db: &dyn Db, file: File) -> ParsedModule {
let _span = tracing::trace_span!("parsed_module", ?file).entered();
let parsed = parsed_module_impl(db, file);
ParsedModule::new(file, parsed)
}
pub fn parsed_module_impl(db: &dyn Db, file: File) -> Parsed<ModModule> {
let source = source_text(db, file);
let ty = file.source_type(db);
let target_version = db.python_version();
let options = ParseOptions::from(ty).with_target_version(target_version);
let parsed = parse_unchecked(&source, options)
parse_unchecked(&source, options)
.try_into_module()
.expect("PySourceType always parses into a module");
ParsedModule::new(parsed)
.expect("PySourceType always parses into a module")
}
/// A wrapper around a parsed module.
@ -41,21 +46,58 @@ pub fn parsed_module(db: &dyn Db, file: File) -> ParsedModule {
/// is represented with the [`ParsedModuleRef`] type.
#[derive(Clone)]
pub struct ParsedModule {
inner: Arc<Parsed<ModModule>>,
file: File,
inner: Arc<ArcSwapOption<indexed::IndexedModule>>,
}
impl ParsedModule {
pub fn new(parsed: Parsed<ModModule>) -> Self {
pub fn new(file: File, parsed: Parsed<ModModule>) -> Self {
Self {
inner: Arc::new(parsed),
file,
inner: Arc::new(ArcSwapOption::new(Some(indexed::IndexedModule::new(
parsed,
)))),
}
}
/// Loads a reference to the parsed module.
///
/// Note that holding on to the reference will prevent garbage collection
/// of the AST. This method will reparse the module if it has been collected.
pub fn load(&self, db: &dyn Db) -> ParsedModuleRef {
let parsed = match self.inner.load_full() {
Some(parsed) => parsed,
None => {
// Re-parse the file.
let parsed = indexed::IndexedModule::new(parsed_module_impl(db, self.file));
tracing::debug!(
"File `{}` was reparsed after being collected in the current Salsa revision",
self.file.path(db)
);
self.inner.store(Some(parsed.clone()));
parsed
}
};
ParsedModuleRef {
module: self.clone(),
indexed: parsed,
}
}
/// Loads a reference to the parsed module.
pub fn load(&self, _db: &dyn Db) -> ParsedModuleRef {
ParsedModuleRef {
module_ref: self.inner.clone(),
}
/// Clear the parsed module, dropping the AST once all references to it are dropped.
pub fn clear(&self) {
self.inner.store(None);
}
/// Returns a pointer for this [`ParsedModule`].
///
/// The pointer uniquely identifies the module within the current Salsa revision,
/// regardless of whether particular [`ParsedModuleRef`] instances are garbage collected.
pub fn as_ptr(&self) -> *const () {
// Note that the outer `Arc` in `inner` is stable across garbage collection, while the inner
// `Arc` within the `ArcSwap` may change.
Arc::as_ptr(&self.inner).cast()
}
}
@ -76,16 +118,19 @@ impl Eq for ParsedModule {}
/// Cheap cloneable wrapper around an instance of a module AST.
#[derive(Clone)]
pub struct ParsedModuleRef {
module_ref: Arc<Parsed<ModModule>>,
module: ParsedModule,
indexed: Arc<indexed::IndexedModule>,
}
impl ParsedModuleRef {
pub fn as_arc(&self) -> &Arc<Parsed<ModModule>> {
&self.module_ref
/// Returns a reference to the [`ParsedModule`] that this instance was loaded from.
pub fn module(&self) -> &ParsedModule {
&self.module
}
pub fn into_arc(self) -> Arc<Parsed<ModModule>> {
self.module_ref
/// Returns a reference to the AST node at the given index.
pub fn get_by_index<'ast>(&'ast self, index: NodeIndex) -> AnyRootNodeRef<'ast> {
self.indexed.get_by_index(index)
}
}
@ -93,7 +138,247 @@ impl std::ops::Deref for ParsedModuleRef {
type Target = Parsed<ModModule>;
fn deref(&self) -> &Self::Target {
&self.module_ref
&self.indexed.parsed
}
}
mod indexed {
use std::sync::Arc;
use ruff_python_ast::visitor::source_order::*;
use ruff_python_ast::*;
use ruff_python_parser::Parsed;
/// A wrapper around the AST that allows access to AST nodes by index.
#[derive(Debug)]
pub struct IndexedModule {
index: Box<[AnyRootNodeRef<'static>]>,
pub parsed: Parsed<ModModule>,
}
impl IndexedModule {
/// Create a new [`IndexedModule`] from the given AST.
#[allow(clippy::unnecessary_cast)]
pub fn new(parsed: Parsed<ModModule>) -> Arc<Self> {
let mut visitor = Visitor {
nodes: Vec::new(),
index: 0,
};
let mut inner = Arc::new(IndexedModule {
parsed,
index: Box::new([]),
});
AnyNodeRef::from(inner.parsed.syntax()).visit_source_order(&mut visitor);
let index: Box<[AnyRootNodeRef<'_>]> = visitor.nodes.into_boxed_slice();
// SAFETY: We cast from `Box<[AnyRootNodeRef<'_>]>` to `Box<[AnyRootNodeRef<'static>]>`,
// faking the 'static lifetime to create the self-referential struct. The node references
// are into the `Arc<Parsed<ModModule>>`, so are valid for as long as the `IndexedModule`
// is alive. We make sure to restore the correct lifetime in `get_by_index`.
//
// Note that we can never move the data within the `Arc` after this point.
Arc::get_mut(&mut inner).unwrap().index =
unsafe { Box::from_raw(Box::into_raw(index) as *mut [AnyRootNodeRef<'static>]) };
inner
}
/// Returns the node at the given index.
pub fn get_by_index<'ast>(&'ast self, index: NodeIndex) -> AnyRootNodeRef<'ast> {
// Note that this method restores the correct lifetime: the nodes are valid for as
// long as the reference to `IndexedModule` is alive.
self.index[index.as_usize()]
}
}
/// A visitor that collects nodes in source order.
pub struct Visitor<'a> {
pub index: u32,
pub nodes: Vec<AnyRootNodeRef<'a>>,
}
impl<'a> Visitor<'a> {
fn visit_node<T>(&mut self, node: &'a T)
where
T: HasNodeIndex + std::fmt::Debug,
AnyRootNodeRef<'a>: From<&'a T>,
{
node.node_index().set(self.index);
self.nodes.push(AnyRootNodeRef::from(node));
self.index += 1;
}
}
impl<'a> SourceOrderVisitor<'a> for Visitor<'a> {
#[inline]
fn visit_mod(&mut self, module: &'a Mod) {
self.visit_node(module);
walk_module(self, module);
}
#[inline]
fn visit_stmt(&mut self, stmt: &'a Stmt) {
self.visit_node(stmt);
walk_stmt(self, stmt);
}
#[inline]
fn visit_annotation(&mut self, expr: &'a Expr) {
self.visit_node(expr);
walk_annotation(self, expr);
}
#[inline]
fn visit_expr(&mut self, expr: &'a Expr) {
self.visit_node(expr);
walk_expr(self, expr);
}
#[inline]
fn visit_decorator(&mut self, decorator: &'a Decorator) {
self.visit_node(decorator);
walk_decorator(self, decorator);
}
#[inline]
fn visit_comprehension(&mut self, comprehension: &'a Comprehension) {
self.visit_node(comprehension);
walk_comprehension(self, comprehension);
}
#[inline]
fn visit_except_handler(&mut self, except_handler: &'a ExceptHandler) {
self.visit_node(except_handler);
walk_except_handler(self, except_handler);
}
#[inline]
fn visit_arguments(&mut self, arguments: &'a Arguments) {
self.visit_node(arguments);
walk_arguments(self, arguments);
}
#[inline]
fn visit_parameters(&mut self, parameters: &'a Parameters) {
self.visit_node(parameters);
walk_parameters(self, parameters);
}
#[inline]
fn visit_parameter(&mut self, arg: &'a Parameter) {
self.visit_node(arg);
walk_parameter(self, arg);
}
fn visit_parameter_with_default(
&mut self,
parameter_with_default: &'a ParameterWithDefault,
) {
self.visit_node(parameter_with_default);
walk_parameter_with_default(self, parameter_with_default);
}
#[inline]
fn visit_keyword(&mut self, keyword: &'a Keyword) {
self.visit_node(keyword);
walk_keyword(self, keyword);
}
#[inline]
fn visit_alias(&mut self, alias: &'a Alias) {
self.visit_node(alias);
walk_alias(self, alias);
}
#[inline]
fn visit_with_item(&mut self, with_item: &'a WithItem) {
self.visit_node(with_item);
walk_with_item(self, with_item);
}
#[inline]
fn visit_type_params(&mut self, type_params: &'a TypeParams) {
self.visit_node(type_params);
walk_type_params(self, type_params);
}
#[inline]
fn visit_type_param(&mut self, type_param: &'a TypeParam) {
self.visit_node(type_param);
walk_type_param(self, type_param);
}
#[inline]
fn visit_match_case(&mut self, match_case: &'a MatchCase) {
self.visit_node(match_case);
walk_match_case(self, match_case);
}
#[inline]
fn visit_pattern(&mut self, pattern: &'a Pattern) {
self.visit_node(pattern);
walk_pattern(self, pattern);
}
#[inline]
fn visit_pattern_arguments(&mut self, pattern_arguments: &'a PatternArguments) {
self.visit_node(pattern_arguments);
walk_pattern_arguments(self, pattern_arguments);
}
#[inline]
fn visit_pattern_keyword(&mut self, pattern_keyword: &'a PatternKeyword) {
self.visit_node(pattern_keyword);
walk_pattern_keyword(self, pattern_keyword);
}
#[inline]
fn visit_elif_else_clause(&mut self, elif_else_clause: &'a ElifElseClause) {
self.visit_node(elif_else_clause);
walk_elif_else_clause(self, elif_else_clause);
}
#[inline]
fn visit_f_string(&mut self, f_string: &'a FString) {
self.visit_node(f_string);
walk_f_string(self, f_string);
}
#[inline]
fn visit_interpolated_string_element(
&mut self,
interpolated_string_element: &'a InterpolatedStringElement,
) {
self.visit_node(interpolated_string_element);
walk_interpolated_string_element(self, interpolated_string_element);
}
#[inline]
fn visit_t_string(&mut self, t_string: &'a TString) {
self.visit_node(t_string);
walk_t_string(self, t_string);
}
#[inline]
fn visit_string_literal(&mut self, string_literal: &'a StringLiteral) {
self.visit_node(string_literal);
walk_string_literal(self, string_literal);
}
#[inline]
fn visit_bytes_literal(&mut self, bytes_literal: &'a BytesLiteral) {
self.visit_node(bytes_literal);
walk_bytes_literal(self, bytes_literal);
}
#[inline]
fn visit_identifier(&mut self, identifier: &'a Identifier) {
self.visit_node(identifier);
walk_identifier(self, identifier);
}
}
}

View file

@ -39,6 +39,7 @@ impl<'ast> SourceOrderVisitor<'ast> for Collector<'_> {
module,
level,
range: _,
node_index: _,
}) => {
let module = module.as_deref();
let level = *level;
@ -78,7 +79,11 @@ impl<'ast> SourceOrderVisitor<'ast> for Collector<'_> {
}
}
}
Stmt::Import(ast::StmtImport { names, range: _ }) => {
Stmt::Import(ast::StmtImport {
names,
range: _,
node_index: _,
}) => {
for alias in names {
if let Some(module_name) = ModuleName::new(alias.name.as_str()) {
self.imports.push(CollectedImport::Import(module_name));
@ -122,7 +127,12 @@ impl<'ast> SourceOrderVisitor<'ast> for Collector<'_> {
fn visit_expr(&mut self, expr: &'ast Expr) {
if self.string_imports {
if let Expr::StringLiteral(ast::ExprStringLiteral { value, range: _ }) = expr {
if let Expr::StringLiteral(ast::ExprStringLiteral {
value,
range: _,
node_index: _,
}) = expr
{
// Determine whether the string literal "looks like" an import statement: contains
// a dot, and consists solely of valid Python identifiers.
let value = value.to_str();

View file

@ -15,6 +15,7 @@ pub(crate) fn except_handler(except_handler: &ExceptHandler, checker: &Checker)
name,
body,
range: _,
node_index: _,
}) => {
if checker.enabled(Rule::BareExcept) {
pycodestyle::rules::bare_except(checker, type_.as_deref(), body, except_handler);

View file

@ -185,12 +185,14 @@ pub(crate) fn expression(expr: &Expr, checker: &Checker) {
elts,
ctx,
range: _,
node_index: _,
parenthesized: _,
})
| Expr::List(ast::ExprList {
elts,
ctx,
range: _,
node_index: _,
}) => {
if ctx.is_store() {
let check_too_many_expressions = checker.enabled(Rule::ExpressionsInStarAssignment);
@ -205,7 +207,12 @@ pub(crate) fn expression(expr: &Expr, checker: &Checker) {
);
}
}
Expr::Name(ast::ExprName { id, ctx, range }) => {
Expr::Name(ast::ExprName {
id,
ctx,
range,
node_index: _,
}) => {
match ctx {
ExprContext::Load => {
if checker.enabled(Rule::TypingTextStrAlias) {
@ -472,8 +479,10 @@ pub(crate) fn expression(expr: &Expr, checker: &Checker) {
args,
keywords,
range: _,
node_index: _,
},
range: _,
node_index: _,
},
) => {
if checker.any_enabled(&[
@ -1261,6 +1270,7 @@ pub(crate) fn expression(expr: &Expr, checker: &Checker) {
op: Operator::Mod,
right,
range: _,
node_index: _,
},
) => {
if let Expr::StringLiteral(format_string @ ast::ExprStringLiteral { value, .. }) =
@ -1426,6 +1436,7 @@ pub(crate) fn expression(expr: &Expr, checker: &Checker) {
op,
operand,
range: _,
node_index: _,
},
) => {
if checker.any_enabled(&[Rule::NotInTest, Rule::NotIsTest]) {
@ -1452,6 +1463,7 @@ pub(crate) fn expression(expr: &Expr, checker: &Checker) {
ops,
comparators,
range: _,
node_index: _,
},
) => {
if checker.any_enabled(&[Rule::NoneComparison, Rule::TrueFalseComparison]) {
@ -1530,7 +1542,13 @@ pub(crate) fn expression(expr: &Expr, checker: &Checker) {
refurb::rules::math_constant(checker, number_literal);
}
}
Expr::StringLiteral(string_like @ ast::ExprStringLiteral { value, range: _ }) => {
Expr::StringLiteral(
string_like @ ast::ExprStringLiteral {
value,
range: _,
node_index: _,
},
) => {
if checker.enabled(Rule::UnicodeKindPrefix) {
for string_part in value {
pyupgrade::rules::unicode_kind_prefix(checker, string_part);
@ -1551,6 +1569,7 @@ pub(crate) fn expression(expr: &Expr, checker: &Checker) {
body,
orelse,
range: _,
node_index: _,
},
) => {
if checker.enabled(Rule::IfElseBlockInsteadOfDictGet) {
@ -1585,6 +1604,7 @@ pub(crate) fn expression(expr: &Expr, checker: &Checker) {
elt,
generators,
range: _,
node_index: _,
},
) => {
if checker.enabled(Rule::UnnecessaryListIndexLookup) {
@ -1615,6 +1635,7 @@ pub(crate) fn expression(expr: &Expr, checker: &Checker) {
elt,
generators,
range: _,
node_index: _,
},
) => {
if checker.enabled(Rule::UnnecessaryListIndexLookup) {
@ -1646,6 +1667,7 @@ pub(crate) fn expression(expr: &Expr, checker: &Checker) {
value,
generators,
range: _,
node_index: _,
},
) => {
if checker.enabled(Rule::UnnecessaryListIndexLookup) {
@ -1684,6 +1706,7 @@ pub(crate) fn expression(expr: &Expr, checker: &Checker) {
generators,
elt: _,
range: _,
node_index: _,
parenthesized: _,
},
) => {

View file

@ -18,7 +18,11 @@ use ruff_python_ast::PythonVersion;
/// Run lint rules over a [`Stmt`] syntax node.
pub(crate) fn statement(stmt: &Stmt, checker: &mut Checker) {
match stmt {
Stmt::Global(ast::StmtGlobal { names, range: _ }) => {
Stmt::Global(ast::StmtGlobal {
names,
range: _,
node_index: _,
}) => {
if checker.enabled(Rule::GlobalAtModuleLevel) {
pylint::rules::global_at_module_level(checker, stmt);
}
@ -28,7 +32,13 @@ pub(crate) fn statement(stmt: &Stmt, checker: &mut Checker) {
}
}
}
Stmt::Nonlocal(nonlocal @ ast::StmtNonlocal { names, range: _ }) => {
Stmt::Nonlocal(
nonlocal @ ast::StmtNonlocal {
names,
range: _,
node_index: _,
},
) => {
if checker.enabled(Rule::AmbiguousVariableName) {
for name in names {
pycodestyle::rules::ambiguous_variable_name(checker, name, name.range());
@ -80,6 +90,7 @@ pub(crate) fn statement(stmt: &Stmt, checker: &mut Checker) {
body,
type_params: _,
range: _,
node_index: _,
},
) => {
if checker.enabled(Rule::DjangoNonLeadingReceiverDecorator) {
@ -381,6 +392,7 @@ pub(crate) fn statement(stmt: &Stmt, checker: &mut Checker) {
decorator_list,
body,
range: _,
node_index: _,
},
) => {
if checker.enabled(Rule::NoClassmethodDecorator) {
@ -542,7 +554,11 @@ pub(crate) fn statement(stmt: &Stmt, checker: &mut Checker) {
ruff::rules::implicit_class_var_in_dataclass(checker, class_def);
}
}
Stmt::Import(ast::StmtImport { names, range: _ }) => {
Stmt::Import(ast::StmtImport {
names,
range: _,
node_index: _,
}) => {
if checker.enabled(Rule::MultipleImportsOnOneLine) {
pycodestyle::rules::multiple_imports_on_one_line(checker, stmt, names);
}
@ -699,6 +715,7 @@ pub(crate) fn statement(stmt: &Stmt, checker: &mut Checker) {
module,
level,
range: _,
node_index: _,
},
) => {
let level = *level;
@ -1142,6 +1159,7 @@ pub(crate) fn statement(stmt: &Stmt, checker: &mut Checker) {
test,
msg,
range: _,
node_index: _,
},
) => {
if !checker.semantic.in_type_checking_block() {
@ -1243,6 +1261,7 @@ pub(crate) fn statement(stmt: &Stmt, checker: &mut Checker) {
orelse,
is_async,
range: _,
node_index: _,
},
) => {
if checker.enabled(Rule::TooManyNestedBlocks) {
@ -1606,7 +1625,13 @@ pub(crate) fn statement(stmt: &Stmt, checker: &mut Checker) {
flake8_pyi::rules::t_suffixed_type_alias(checker, name);
}
}
Stmt::Delete(delete @ ast::StmtDelete { targets, range: _ }) => {
Stmt::Delete(
delete @ ast::StmtDelete {
targets,
range: _,
node_index: _,
},
) => {
if checker.enabled(Rule::GlobalStatement) {
for target in targets {
if let Expr::Name(ast::ExprName { id, .. }) = target {
@ -1618,7 +1643,13 @@ pub(crate) fn statement(stmt: &Stmt, checker: &mut Checker) {
refurb::rules::delete_full_slice(checker, delete);
}
}
Stmt::Expr(expr @ ast::StmtExpr { value, range: _ }) => {
Stmt::Expr(
expr @ ast::StmtExpr {
value,
range: _,
node_index: _,
},
) => {
if checker.enabled(Rule::UselessComparison) {
flake8_bugbear::rules::useless_comparison(checker, value);
}
@ -1645,6 +1676,7 @@ pub(crate) fn statement(stmt: &Stmt, checker: &mut Checker) {
subject: _,
cases,
range: _,
node_index: _,
}) => {
if checker.enabled(Rule::NanComparison) {
pylint::rules::nan_comparison_match(checker, cases);

View file

@ -836,10 +836,15 @@ impl<'a> Visitor<'a> for Checker<'a> {
op: _,
value: _,
range: _,
node_index: _,
}) => {
self.handle_node_load(target);
}
Stmt::Import(ast::StmtImport { names, range: _ }) => {
Stmt::Import(ast::StmtImport {
names,
range: _,
node_index: _,
}) => {
if self.semantic.at_top_level() {
self.importer.visit_import(stmt);
}
@ -893,6 +898,7 @@ impl<'a> Visitor<'a> for Checker<'a> {
module,
level,
range: _,
node_index: _,
}) => {
if self.semantic.at_top_level() {
self.importer.visit_import(stmt);
@ -954,7 +960,11 @@ impl<'a> Visitor<'a> for Checker<'a> {
}
}
}
Stmt::Global(ast::StmtGlobal { names, range: _ }) => {
Stmt::Global(ast::StmtGlobal {
names,
range: _,
node_index: _,
}) => {
if !self.semantic.scope_id.is_global() {
for name in names {
let binding_id = self.semantic.global_scope().get(name);
@ -976,7 +986,11 @@ impl<'a> Visitor<'a> for Checker<'a> {
}
}
}
Stmt::Nonlocal(ast::StmtNonlocal { names, range: _ }) => {
Stmt::Nonlocal(ast::StmtNonlocal {
names,
range: _,
node_index: _,
}) => {
if !self.semantic.scope_id.is_global() {
for name in names {
if let Some((scope_id, binding_id)) = self.semantic.nonlocal(name) {
@ -1186,6 +1200,7 @@ impl<'a> Visitor<'a> for Checker<'a> {
}
Stmt::TypeAlias(ast::StmtTypeAlias {
range: _,
node_index: _,
name,
type_params,
value,
@ -1280,6 +1295,7 @@ impl<'a> Visitor<'a> for Checker<'a> {
test,
msg,
range: _,
node_index: _,
}) => {
let snapshot = self.semantic.flags;
self.semantic.flags |= SemanticModelFlags::ASSERT_STATEMENT;
@ -1294,6 +1310,7 @@ impl<'a> Visitor<'a> for Checker<'a> {
body,
is_async: _,
range: _,
node_index: _,
}) => {
for item in items {
self.visit_with_item(item);
@ -1307,6 +1324,7 @@ impl<'a> Visitor<'a> for Checker<'a> {
body,
orelse,
range: _,
node_index: _,
}) => {
self.visit_boolean_test(test);
self.visit_body(body);
@ -1318,6 +1336,7 @@ impl<'a> Visitor<'a> for Checker<'a> {
body,
elif_else_clauses,
range: _,
node_index: _,
},
) => {
self.visit_boolean_test(test);
@ -1437,15 +1456,27 @@ impl<'a> Visitor<'a> for Checker<'a> {
func,
arguments: _,
range: _,
node_index: _,
}) => {
if let Expr::Name(ast::ExprName { id, ctx, range: _ }) = func.as_ref() {
if let Expr::Name(ast::ExprName {
id,
ctx,
range: _,
node_index: _,
}) = func.as_ref()
{
if id == "locals" && ctx.is_load() {
let scope = self.semantic.current_scope_mut();
scope.set_uses_locals();
}
}
}
Expr::Name(ast::ExprName { id, ctx, range: _ }) => match ctx {
Expr::Name(ast::ExprName {
id,
ctx,
range: _,
node_index: _,
}) => match ctx {
ExprContext::Load => self.handle_node_load(expr),
ExprContext::Store => self.handle_node_store(id, expr),
ExprContext::Del => self.handle_node_delete(expr),
@ -1460,6 +1491,7 @@ impl<'a> Visitor<'a> for Checker<'a> {
elt,
generators,
range: _,
node_index: _,
}) => {
self.visit_generators(GeneratorKind::ListComprehension, generators);
self.visit_expr(elt);
@ -1468,6 +1500,7 @@ impl<'a> Visitor<'a> for Checker<'a> {
elt,
generators,
range: _,
node_index: _,
}) => {
self.visit_generators(GeneratorKind::SetComprehension, generators);
self.visit_expr(elt);
@ -1476,6 +1509,7 @@ impl<'a> Visitor<'a> for Checker<'a> {
elt,
generators,
range: _,
node_index: _,
parenthesized: _,
}) => {
self.visit_generators(GeneratorKind::Generator, generators);
@ -1486,6 +1520,7 @@ impl<'a> Visitor<'a> for Checker<'a> {
value,
generators,
range: _,
node_index: _,
}) => {
self.visit_generators(GeneratorKind::DictComprehension, generators);
self.visit_expr(key);
@ -1496,6 +1531,7 @@ impl<'a> Visitor<'a> for Checker<'a> {
parameters,
body: _,
range: _,
node_index: _,
},
) => {
// Visit the default arguments, but avoid the body, which will be deferred.
@ -1517,6 +1553,7 @@ impl<'a> Visitor<'a> for Checker<'a> {
body,
orelse,
range: _,
node_index: _,
}) => {
self.visit_boolean_test(test);
self.visit_expr(body);
@ -1526,6 +1563,7 @@ impl<'a> Visitor<'a> for Checker<'a> {
op: UnaryOp::Not,
operand,
range: _,
node_index: _,
}) => {
self.visit_boolean_test(operand);
}
@ -1533,6 +1571,7 @@ impl<'a> Visitor<'a> for Checker<'a> {
func,
arguments,
range: _,
node_index: _,
}) => {
self.visit_expr(func);
@ -1647,6 +1686,7 @@ impl<'a> Visitor<'a> for Checker<'a> {
arg,
value,
range: _,
node_index: _,
} = keyword;
if let Some(id) = arg {
if matches!(&**id, "bound" | "default") {
@ -1738,7 +1778,12 @@ impl<'a> Visitor<'a> for Checker<'a> {
self.visit_non_type_definition(arg);
}
for arg in args {
if let Expr::Dict(ast::ExprDict { items, range: _ }) = arg {
if let Expr::Dict(ast::ExprDict {
items,
range: _,
node_index: _,
}) = arg
{
for ast::DictItem { key, value } in items {
if let Some(key) = key {
self.visit_non_type_definition(key);
@ -1776,6 +1821,7 @@ impl<'a> Visitor<'a> for Checker<'a> {
value,
arg,
range: _,
node_index: _,
} = keyword;
if arg.as_ref().is_some_and(|arg| arg == "type") {
self.visit_type_definition(value);
@ -1804,6 +1850,7 @@ impl<'a> Visitor<'a> for Checker<'a> {
slice,
ctx,
range: _,
node_index: _,
}) => {
// Only allow annotations in `ExprContext::Load`. If we have, e.g.,
// `obj["foo"]["bar"]`, we need to avoid treating the `obj["foo"]`
@ -1843,6 +1890,7 @@ impl<'a> Visitor<'a> for Checker<'a> {
elts,
ctx,
range: _,
node_index: _,
parenthesized: _,
}) = slice.as_ref()
{
@ -1867,7 +1915,12 @@ impl<'a> Visitor<'a> for Checker<'a> {
}
}
Some(typing::SubscriptKind::TypedDict) => {
if let Expr::Dict(ast::ExprDict { items, range: _ }) = slice.as_ref() {
if let Expr::Dict(ast::ExprDict {
items,
range: _,
node_index: _,
}) = slice.as_ref()
{
for item in items {
if let Some(key) = &item.key {
self.visit_non_type_definition(key);
@ -1906,6 +1959,7 @@ impl<'a> Visitor<'a> for Checker<'a> {
target,
value,
range: _,
node_index: _,
}) => {
self.visit_expr(value);
@ -1955,6 +2009,7 @@ impl<'a> Visitor<'a> for Checker<'a> {
name,
body: _,
range: _,
node_index: _,
}) => {
if let Some(name) = name {
// Store the existing binding, if any.
@ -2029,6 +2084,7 @@ impl<'a> Visitor<'a> for Checker<'a> {
| Pattern::MatchStar(ast::PatternMatchStar {
name: Some(name),
range: _,
node_index: _,
})
| Pattern::MatchMapping(ast::PatternMatchMapping {
rest: Some(name), ..
@ -2088,6 +2144,7 @@ impl<'a> Visitor<'a> for Checker<'a> {
default,
name: _,
range: _,
node_index: _,
}) => {
if let Some(expr) = bound {
self.visit
@ -2104,6 +2161,7 @@ impl<'a> Visitor<'a> for Checker<'a> {
default,
name: _,
range: _,
node_index: _,
}) => {
if let Some(expr) = default {
self.visit
@ -2115,6 +2173,7 @@ impl<'a> Visitor<'a> for Checker<'a> {
default,
name: _,
range: _,
node_index: _,
}) => {
if let Some(expr) = default {
self.visit
@ -2833,6 +2892,7 @@ impl<'a> Checker<'a> {
parameters,
body,
range: _,
node_index: _,
})) = self.semantic.current_expression()
else {
unreachable!("Expected Expr::Lambda");

View file

@ -73,6 +73,7 @@ impl StatementVisitor<'_> for StringLinesVisitor<'_> {
if let Stmt::Expr(ast::StmtExpr {
value: expr,
range: _,
node_index: _,
}) = stmt
{
if expr.is_string_literal_expr() {

View file

@ -7,7 +7,12 @@ use ruff_python_semantic::{Definition, DefinitionId, Definitions, Member, Member
pub(crate) fn docstring_from(suite: &[Stmt]) -> Option<&ast::ExprStringLiteral> {
let stmt = suite.first()?;
// Require the docstring to be a standalone expression.
let Stmt::Expr(ast::StmtExpr { value, range: _ }) = stmt else {
let Stmt::Expr(ast::StmtExpr {
value,
range: _,
node_index: _,
}) = stmt
else {
return None;
};
// Only match strings.

View file

@ -453,6 +453,7 @@ impl<'a> Importer<'a> {
names,
level,
range: _,
node_index: _,
}) = stmt
{
if *level == 0

View file

@ -104,7 +104,12 @@ pub(crate) fn airflow_3_removal_expr(checker: &Checker, expr: &Expr) {
check_name(checker, expr, *range);
check_class_attribute(checker, attribute_expr);
}
Expr::Name(ExprName { id, ctx, range }) => {
Expr::Name(ExprName {
id,
ctx,
range,
node_index: _,
}) => {
check_name(checker, expr, *range);
if matches!(ctx, ExprContext::Store) {
if let ScopeKind::Class(class_def) = checker.semantic().current_scope().kind {
@ -375,8 +380,11 @@ fn check_context_key_usage_in_call(checker: &Checker, call_expr: &ExprCall) {
}
for removed_key in REMOVED_CONTEXT_KEYS {
let Some(Expr::StringLiteral(ExprStringLiteral { value, range })) =
call_expr.arguments.find_positional(0)
let Some(Expr::StringLiteral(ExprStringLiteral {
value,
range,
node_index: _,
})) = call_expr.arguments.find_positional(0)
else {
continue;
};

View file

@ -102,6 +102,7 @@ pub(crate) fn airflow_3_0_suggested_update_expr(checker: &Checker, expr: &Expr)
id: _,
ctx: _,
range,
node_index: _,
}) => {
check_name(checker, expr, *range);
}

View file

@ -176,6 +176,7 @@ pub(crate) fn subscript(checker: &Checker, value: &Expr, slice: &Expr) {
upper: Some(upper),
step: None,
range: _,
node_index: _,
}) => {
if let Expr::NumberLiteral(ast::ExprNumberLiteral {
value: ast::Number::Int(i),

View file

@ -137,6 +137,7 @@ impl AutoPythonType {
let expr = Expr::Name(ast::ExprName {
id: Name::from(binding),
range: TextRange::default(),
node_index: ruff_python_ast::AtomicNodeIndex::dummy(),
ctx: ExprContext::Load,
});
Some((expr, vec![no_return_edit]))
@ -203,6 +204,7 @@ fn type_expr(python_type: PythonType) -> Option<Expr> {
Expr::Name(ast::ExprName {
id: name.into(),
range: TextRange::default(),
node_index: ruff_python_ast::AtomicNodeIndex::dummy(),
ctx: ExprContext::Load,
})
}

View file

@ -562,7 +562,11 @@ fn is_stub_function(function_def: &ast::StmtFunctionDef, checker: &Checker) -> b
fn is_empty_body(function_def: &ast::StmtFunctionDef) -> bool {
function_def.body.iter().all(|stmt| match stmt {
Stmt::Pass(_) => true,
Stmt::Expr(ast::StmtExpr { value, range: _ }) => {
Stmt::Expr(ast::StmtExpr {
value,
range: _,
node_index: _,
}) => {
matches!(
value.as_ref(),
Expr::StringLiteral(_) | Expr::EllipsisLiteral(_)
@ -606,6 +610,7 @@ pub(crate) fn definition(
let ast::StmtFunctionDef {
range: _,
node_index: _,
is_async: _,
decorator_list,
name,

View file

@ -166,6 +166,7 @@ fn parse_mask(expr: &Expr, semantic: &SemanticModel) -> Result<Option<u16>> {
op,
right,
range: _,
node_index: _,
}) => {
let Some(left_value) = parse_mask(left, semantic)? else {
return Ok(None);

View file

@ -131,7 +131,11 @@ fn is_abc_class(bases: &[Expr], keywords: &[Keyword], semantic: &SemanticModel)
fn is_empty_body(body: &[Stmt]) -> bool {
body.iter().all(|stmt| match stmt {
Stmt::Pass(_) => true,
Stmt::Expr(ast::StmtExpr { value, range: _ }) => {
Stmt::Expr(ast::StmtExpr {
value,
range: _,
node_index: _,
}) => {
matches!(
value.as_ref(),
Expr::StringLiteral(_) | Expr::EllipsisLiteral(_)

View file

@ -52,11 +52,13 @@ impl AlwaysFixableViolation for AssertFalse {
fn assertion_error(msg: Option<&Expr>) -> Stmt {
Stmt::Raise(ast::StmtRaise {
range: TextRange::default(),
node_index: ruff_python_ast::AtomicNodeIndex::dummy(),
exc: Some(Box::new(Expr::Call(ast::ExprCall {
func: Box::new(Expr::Name(ast::ExprName {
id: "AssertionError".into(),
ctx: ExprContext::Load,
range: TextRange::default(),
node_index: ruff_python_ast::AtomicNodeIndex::dummy(),
})),
arguments: Arguments {
args: if let Some(msg) = msg {
@ -66,8 +68,10 @@ fn assertion_error(msg: Option<&Expr>) -> Stmt {
},
keywords: Box::from([]),
range: TextRange::default(),
node_index: ruff_python_ast::AtomicNodeIndex::dummy(),
},
range: TextRange::default(),
node_index: ruff_python_ast::AtomicNodeIndex::dummy(),
}))),
cause: None,
})

View file

@ -63,6 +63,7 @@ pub(crate) fn assert_raises_exception(checker: &Checker, items: &[WithItem]) {
func,
arguments,
range: _,
node_index: _,
}) = &item.context_expr
else {
continue;

View file

@ -113,6 +113,7 @@ fn type_pattern(elts: Vec<&Expr>) -> Expr {
elts: elts.into_iter().cloned().collect(),
ctx: ExprContext::Load,
range: TextRange::default(),
node_index: ruff_python_ast::AtomicNodeIndex::dummy(),
parenthesized: true,
}
.into()

View file

@ -45,7 +45,12 @@ pub(crate) fn f_string_docstring(checker: &Checker, body: &[Stmt]) {
let Some(stmt) = body.first() else {
return;
};
let Stmt::Expr(ast::StmtExpr { value, range: _ }) = stmt else {
let Stmt::Expr(ast::StmtExpr {
value,
range: _,
node_index: _,
}) = stmt
else {
return;
};
if !value.is_f_string_expr() {

View file

@ -111,6 +111,7 @@ impl<'a> Visitor<'a> for SuspiciousVariablesVisitor<'a> {
Stmt::Return(ast::StmtReturn {
value: Some(value),
range: _,
node_index: _,
}) => {
// Mark `return lambda: x` as safe.
if value.is_lambda_expr() {
@ -128,6 +129,7 @@ impl<'a> Visitor<'a> for SuspiciousVariablesVisitor<'a> {
func,
arguments,
range: _,
node_index: _,
}) => {
match func.as_ref() {
Expr::Name(ast::ExprName { id, .. }) => {
@ -167,6 +169,7 @@ impl<'a> Visitor<'a> for SuspiciousVariablesVisitor<'a> {
parameters,
body,
range: _,
node_index: _,
}) => {
if !self.safe_functions.contains(&expr) {
// Collect all loaded variable names.

View file

@ -60,6 +60,7 @@ pub(crate) fn loop_iterator_mutation(checker: &Checker, stmt_for: &StmtFor) {
orelse: _,
is_async: _,
range: _,
node_index: _,
} = stmt_for;
let (index, target, iter) = match iter.as_ref() {
@ -170,6 +171,7 @@ impl<'a> LoopMutationsVisitor<'a> {
for target in targets {
if let Expr::Subscript(ExprSubscript {
range: _,
node_index: _,
value,
slice: _,
ctx: _,
@ -188,6 +190,7 @@ impl<'a> LoopMutationsVisitor<'a> {
for target in targets {
if let Expr::Subscript(ExprSubscript {
range: _,
node_index: _,
value,
slice,
ctx: _,
@ -217,6 +220,7 @@ impl<'a> LoopMutationsVisitor<'a> {
fn handle_call(&mut self, func: &Expr) {
if let Expr::Attribute(ExprAttribute {
range,
node_index: _,
value,
attr,
ctx: _,
@ -237,7 +241,11 @@ impl<'a> Visitor<'a> for LoopMutationsVisitor<'a> {
fn visit_stmt(&mut self, stmt: &'a Stmt) {
match stmt {
// Ex) `del items[0]`
Stmt::Delete(StmtDelete { range, targets }) => {
Stmt::Delete(StmtDelete {
range,
targets,
node_index: _,
}) => {
self.handle_delete(*range, targets);
visitor::walk_stmt(self, stmt);
}

View file

@ -97,6 +97,7 @@ impl<'a> Visitor<'a> for NameFinder<'a> {
parameters,
body,
range: _,
node_index: _,
}) => {
visitor::walk_expr(self, body);

View file

@ -126,6 +126,7 @@ impl StatementVisitor<'_> for ReturnInGeneratorVisitor {
Stmt::Return(ast::StmtReturn {
value: Some(_),
range,
node_index: _,
}) => {
self.return_ = Some(*range);
}

View file

@ -153,6 +153,7 @@ impl<'a> Visitor<'a> for GroupNameFinder<'a> {
body,
elif_else_clauses,
range: _,
node_index: _,
}) => {
// base if plus branches
let mut if_stack = Vec::with_capacity(1 + elif_else_clauses.len());
@ -179,6 +180,7 @@ impl<'a> Visitor<'a> for GroupNameFinder<'a> {
subject,
cases,
range: _,
node_index: _,
}) => {
self.counter_stack.push(Vec::with_capacity(cases.len()));
self.visit_expr(subject);
@ -210,7 +212,11 @@ impl<'a> Visitor<'a> for GroupNameFinder<'a> {
Stmt::Continue(_) | Stmt::Break(_) => {
self.reset_usage_count();
}
Stmt::Return(ast::StmtReturn { value, range: _ }) => {
Stmt::Return(ast::StmtReturn {
value,
range: _,
node_index: _,
}) => {
if let Some(expr) = value {
self.visit_expr(expr);
}
@ -250,11 +256,13 @@ impl<'a> Visitor<'a> for GroupNameFinder<'a> {
elt,
generators,
range: _,
node_index: _,
})
| Expr::SetComp(ast::ExprSetComp {
elt,
generators,
range: _,
node_index: _,
}) => {
for comprehension in generators {
self.visit_comprehension(comprehension);
@ -270,6 +278,7 @@ impl<'a> Visitor<'a> for GroupNameFinder<'a> {
value,
generators,
range: _,
node_index: _,
}) => {
for comprehension in generators {
self.visit_comprehension(comprehension);

View file

@ -53,9 +53,11 @@ fn assignment(obj: &Expr, name: &str, value: &Expr, generator: Generator) -> Str
attr: Identifier::new(name.to_string(), TextRange::default()),
ctx: ExprContext::Store,
range: TextRange::default(),
node_index: ruff_python_ast::AtomicNodeIndex::dummy(),
})],
value: Box::new(value.clone()),
range: TextRange::default(),
node_index: ruff_python_ast::AtomicNodeIndex::dummy(),
});
generator.stmt(&stmt)
}
@ -92,6 +94,7 @@ pub(crate) fn setattr_with_constant(checker: &Checker, expr: &Expr, func: &Expr,
if let Stmt::Expr(ast::StmtExpr {
value: child,
range: _,
node_index: _,
}) = checker.semantic().current_statement()
{
if expr == child.as_ref() {

View file

@ -176,14 +176,17 @@ fn fix_unnecessary_dict_comprehension(value: &Expr, generator: &Comprehension) -
},
keywords: Box::from([]),
range: TextRange::default(),
node_index: ruff_python_ast::AtomicNodeIndex::dummy(),
};
Expr::Call(ExprCall {
func: Box::new(Expr::Name(ExprName {
id: "dict.fromkeys".into(),
ctx: ExprContext::Load,
range: TextRange::default(),
node_index: ruff_python_ast::AtomicNodeIndex::dummy(),
})),
arguments: args,
range: TextRange::default(),
node_index: ruff_python_ast::AtomicNodeIndex::dummy(),
})
}

View file

@ -48,6 +48,7 @@ pub(crate) fn unnecessary_list_call(checker: &Checker, expr: &Expr, call: &ExprC
func,
arguments,
range: _,
node_index: _,
} = call;
if !arguments.keywords.is_empty() {
@ -60,6 +61,7 @@ pub(crate) fn unnecessary_list_call(checker: &Checker, expr: &Expr, call: &ExprC
let Arguments {
range: _,
node_index: _,
args,
keywords: _,
} = arguments;

View file

@ -52,6 +52,7 @@ pub(crate) fn unnecessary_subscript_reversal(checker: &Checker, call: &ast::Expr
upper,
step,
range: _,
node_index: _,
}) = slice.as_ref()
else {
return;
@ -66,6 +67,7 @@ pub(crate) fn unnecessary_subscript_reversal(checker: &Checker, call: &ast::Expr
op: UnaryOp::USub,
operand,
range: _,
node_index: _,
}) = step.as_ref()
else {
return;

View file

@ -72,6 +72,7 @@ pub(crate) fn multiple_starts_ends_with(checker: &Checker, expr: &Expr) {
op: BoolOp::Or,
values,
range: _,
node_index: _,
}) = expr
else {
return;
@ -86,8 +87,10 @@ pub(crate) fn multiple_starts_ends_with(checker: &Checker, expr: &Expr) {
args,
keywords,
range: _,
node_index: _,
},
range: _,
node_index: _,
}) = &call
else {
continue;
@ -145,8 +148,10 @@ pub(crate) fn multiple_starts_ends_with(checker: &Checker, expr: &Expr) {
args,
keywords: _,
range: _,
node_index: _,
},
range: _,
node_index: _,
}) = expr
else {
unreachable!(
@ -173,18 +178,21 @@ pub(crate) fn multiple_starts_ends_with(checker: &Checker, expr: &Expr) {
.collect(),
ctx: ExprContext::Load,
range: TextRange::default(),
node_index: ruff_python_ast::AtomicNodeIndex::dummy(),
parenthesized: true,
});
let node1 = Expr::Name(ast::ExprName {
id: arg_name.into(),
ctx: ExprContext::Load,
range: TextRange::default(),
node_index: ruff_python_ast::AtomicNodeIndex::dummy(),
});
let node2 = Expr::Attribute(ast::ExprAttribute {
value: Box::new(node1),
attr: Identifier::new(attr_name.to_string(), TextRange::default()),
ctx: ExprContext::Load,
range: TextRange::default(),
node_index: ruff_python_ast::AtomicNodeIndex::dummy(),
});
let node3 = Expr::Call(ast::ExprCall {
func: Box::new(node2),
@ -192,8 +200,10 @@ pub(crate) fn multiple_starts_ends_with(checker: &Checker, expr: &Expr) {
args: Box::from([node]),
keywords: Box::from([]),
range: TextRange::default(),
node_index: ruff_python_ast::AtomicNodeIndex::dummy(),
},
range: TextRange::default(),
node_index: ruff_python_ast::AtomicNodeIndex::dummy(),
});
let call = node3;
@ -213,6 +223,7 @@ pub(crate) fn multiple_starts_ends_with(checker: &Checker, expr: &Expr) {
})
.collect(),
range: TextRange::default(),
node_index: ruff_python_ast::AtomicNodeIndex::dummy(),
});
let bool_op = node;
diagnostic.set_fix(Fix::unsafe_edit(Edit::range_replacement(

View file

@ -63,6 +63,7 @@ pub(crate) fn reimplemented_container_builtin(checker: &Checker, expr: &ExprLamb
parameters,
body,
range: _,
node_index: _,
} = expr;
if parameters.is_some() {

View file

@ -88,12 +88,14 @@ pub(crate) fn duplicate_literal_member<'a>(checker: &Checker, expr: &'a Expr) {
Expr::Tuple(ast::ExprTuple {
elts: unique_nodes.into_iter().cloned().collect(),
range: TextRange::default(),
node_index: ruff_python_ast::AtomicNodeIndex::dummy(),
ctx: ExprContext::Load,
parenthesized: false,
})
}),
value: subscript.value.clone(),
range: TextRange::default(),
node_index: ruff_python_ast::AtomicNodeIndex::dummy(),
ctx: ExprContext::Load,
});
let fix = Fix::applicable_edit(

View file

@ -165,6 +165,7 @@ fn generate_pep604_fix(
op: Operator::BitOr,
right: Box::new(right.clone()),
range: TextRange::default(),
node_index: ruff_python_ast::AtomicNodeIndex::dummy(),
}))
} else {
Some(right.clone())

View file

@ -347,6 +347,7 @@ fn check_positional_args_for_overloaded_method(
// If any overloads have any variadic arguments, don't do any checking
let Parameters {
range: _,
node_index: _,
posonlyargs,
args,
vararg: None,

View file

@ -133,14 +133,17 @@ fn generate_union_fix(
// Construct the expression as `Subscript[typing.Union, Tuple[expr, [expr, ...]]]`
let new_expr = Expr::Subscript(ExprSubscript {
range: TextRange::default(),
node_index: ruff_python_ast::AtomicNodeIndex::dummy(),
value: Box::new(Expr::Name(ExprName {
id: Name::new(binding),
ctx: ExprContext::Store,
range: TextRange::default(),
node_index: ruff_python_ast::AtomicNodeIndex::dummy(),
})),
slice: Box::new(Expr::Tuple(ExprTuple {
elts: nodes.into_iter().cloned().collect(),
range: TextRange::default(),
node_index: ruff_python_ast::AtomicNodeIndex::dummy(),
ctx: ExprContext::Load,
parenthesized: false,
})),

View file

@ -59,7 +59,12 @@ pub(crate) fn non_empty_stub_body(checker: &Checker, body: &[Stmt]) {
}
// Ignore `...` (the desired case).
if let Stmt::Expr(ast::StmtExpr { value, range: _ }) = stmt {
if let Stmt::Expr(ast::StmtExpr {
value,
range: _,
node_index: _,
}) = stmt
{
if value.is_ellipsis_literal_expr() {
return;
}

View file

@ -205,11 +205,13 @@ fn create_fix(
let new_literal_expr = Expr::Subscript(ast::ExprSubscript {
value: Box::new(literal_subscript.clone()),
range: TextRange::default(),
node_index: ruff_python_ast::AtomicNodeIndex::dummy(),
ctx: ExprContext::Load,
slice: Box::new(if literal_elements.len() > 1 {
Expr::Tuple(ast::ExprTuple {
elts: literal_elements.into_iter().cloned().collect(),
range: TextRange::default(),
node_index: ruff_python_ast::AtomicNodeIndex::dummy(),
ctx: ExprContext::Load,
parenthesized: true,
})
@ -233,6 +235,7 @@ fn create_fix(
UnionKind::BitOr => {
let none_expr = Expr::NoneLiteral(ExprNoneLiteral {
range: TextRange::default(),
node_index: ruff_python_ast::AtomicNodeIndex::dummy(),
});
let union_expr = pep_604_union(&[new_literal_expr, none_expr]);
let content = checker.generator().expr(&union_expr);

View file

@ -252,6 +252,7 @@ fn generate_pep604_fix(
op: Operator::BitOr,
right: Box::new(right.clone()),
range: TextRange::default(),
node_index: ruff_python_ast::AtomicNodeIndex::dummy(),
}))
} else {
Some(right.clone())

View file

@ -300,7 +300,11 @@ fn is_valid_default_value_with_annotation(
}
Expr::List(ast::ExprList { elts, .. })
| Expr::Tuple(ast::ExprTuple { elts, .. })
| Expr::Set(ast::ExprSet { elts, range: _ }) => {
| Expr::Set(ast::ExprSet {
elts,
range: _,
node_index: _,
}) => {
return allow_container
&& elts.len() <= 10
&& elts
@ -320,6 +324,7 @@ fn is_valid_default_value_with_annotation(
op: UnaryOp::USub,
operand,
range: _,
node_index: _,
}) => {
match operand.as_ref() {
// Ex) `-1`, `-3.14`, `2j`
@ -342,6 +347,7 @@ fn is_valid_default_value_with_annotation(
op: Operator::Add | Operator::Sub,
right,
range: _,
node_index: _,
}) => {
// Ex) `1 + 2j`, `1 - 2j`, `-1 - 2j`, `-1 + 2j`
if let Expr::NumberLiteral(ast::ExprNumberLiteral {
@ -360,6 +366,7 @@ fn is_valid_default_value_with_annotation(
op: UnaryOp::USub,
operand,
range: _,
node_index: _,
}) = left.as_ref()
{
// Ex) `-1 + 2j`, `-1 - 2j`
@ -398,6 +405,7 @@ fn is_valid_pep_604_union(annotation: &Expr) -> bool {
op: Operator::BitOr,
right,
range: _,
node_index: _,
}) => is_valid_pep_604_union_member(left) && is_valid_pep_604_union_member(right),
Expr::Name(_) | Expr::Subscript(_) | Expr::Attribute(_) | Expr::NoneLiteral(_) => true,
_ => false,
@ -410,6 +418,7 @@ fn is_valid_pep_604_union(annotation: &Expr) -> bool {
op: Operator::BitOr,
right,
range: _,
node_index: _,
}) = annotation
else {
return false;

View file

@ -140,10 +140,12 @@ pub(crate) fn unnecessary_literal_union<'a>(checker: &Checker, expr: &'a Expr) {
slice: Box::new(Expr::Tuple(ast::ExprTuple {
elts: literal_exprs.into_iter().cloned().collect(),
range: TextRange::default(),
node_index: ruff_python_ast::AtomicNodeIndex::dummy(),
ctx: ExprContext::Load,
parenthesized: true,
})),
range: TextRange::default(),
node_index: ruff_python_ast::AtomicNodeIndex::dummy(),
ctx: ExprContext::Load,
});
@ -162,10 +164,12 @@ pub(crate) fn unnecessary_literal_union<'a>(checker: &Checker, expr: &'a Expr) {
slice: Box::new(Expr::Tuple(ast::ExprTuple {
elts,
range: TextRange::default(),
node_index: ruff_python_ast::AtomicNodeIndex::dummy(),
ctx: ExprContext::Load,
parenthesized: true,
})),
range: TextRange::default(),
node_index: ruff_python_ast::AtomicNodeIndex::dummy(),
ctx: ExprContext::Load,
}))
} else {

View file

@ -134,10 +134,12 @@ pub(crate) fn unnecessary_type_union<'a>(checker: &Checker, union: &'a Expr) {
id: Name::new_static("type"),
ctx: ExprContext::Load,
range: TextRange::default(),
node_index: ruff_python_ast::AtomicNodeIndex::dummy(),
})),
slice: Box::new(pep_604_union(&elts)),
ctx: ExprContext::Load,
range: TextRange::default(),
node_index: ruff_python_ast::AtomicNodeIndex::dummy(),
});
if other_exprs.is_empty() {
@ -157,6 +159,7 @@ pub(crate) fn unnecessary_type_union<'a>(checker: &Checker, union: &'a Expr) {
id: Name::new_static("type"),
ctx: ExprContext::Load,
range: TextRange::default(),
node_index: ruff_python_ast::AtomicNodeIndex::dummy(),
})),
slice: Box::new(Expr::Subscript(ast::ExprSubscript {
value: subscript.value.clone(),
@ -168,18 +171,22 @@ pub(crate) fn unnecessary_type_union<'a>(checker: &Checker, union: &'a Expr) {
id: type_member,
ctx: ExprContext::Load,
range: TextRange::default(),
node_index: ruff_python_ast::AtomicNodeIndex::dummy(),
})
})
.collect(),
ctx: ExprContext::Load,
range: TextRange::default(),
node_index: ruff_python_ast::AtomicNodeIndex::dummy(),
parenthesized: true,
})),
ctx: ExprContext::Load,
range: TextRange::default(),
node_index: ruff_python_ast::AtomicNodeIndex::dummy(),
})),
ctx: ExprContext::Load,
range: TextRange::default(),
node_index: ruff_python_ast::AtomicNodeIndex::dummy(),
});
if other_exprs.is_empty() {
@ -195,10 +202,12 @@ pub(crate) fn unnecessary_type_union<'a>(checker: &Checker, union: &'a Expr) {
elts: exprs.into_iter().cloned().collect(),
ctx: ExprContext::Load,
range: TextRange::default(),
node_index: ruff_python_ast::AtomicNodeIndex::dummy(),
parenthesized: true,
})),
ctx: ExprContext::Load,
range: TextRange::default(),
node_index: ruff_python_ast::AtomicNodeIndex::dummy(),
});
checker.generator().expr(&union)

View file

@ -619,11 +619,13 @@ fn is_composite_condition(test: &Expr) -> CompositionKind {
op: UnaryOp::Not,
operand,
range: _,
node_index: _,
}) => {
if let Expr::BoolOp(ast::ExprBoolOp {
op: BoolOp::Or,
values,
range: _,
node_index: _,
}) = operand.as_ref()
{
// Only split cases without mixed `and` and `or`.

View file

@ -622,7 +622,11 @@ struct SkipFunctionsVisitor<'a> {
impl<'a> Visitor<'a> for SkipFunctionsVisitor<'a> {
fn visit_stmt(&mut self, stmt: &'a Stmt) {
match stmt {
Stmt::Return(ast::StmtReturn { value, range: _ }) => {
Stmt::Return(ast::StmtReturn {
value,
range: _,
node_index: _,
}) => {
if value.is_some() {
self.has_return_with_value = true;
}
@ -637,7 +641,11 @@ impl<'a> Visitor<'a> for SkipFunctionsVisitor<'a> {
Expr::YieldFrom(_) => {
self.has_yield_from = true;
}
Expr::Yield(ast::ExprYield { value, range: _ }) => {
Expr::Yield(ast::ExprYield {
value,
range: _,
node_index: _,
}) => {
self.yield_statements.push(expr);
if value.is_some() {
self.has_return_with_value = true;
@ -686,6 +694,7 @@ fn check_fixture_decorator(checker: &Checker, func_name: &str, decorator: &Decor
func,
arguments,
range: _,
node_index: _,
}) => {
if checker.enabled(Rule::PytestFixtureIncorrectParenthesesStyle) {
if !checker.settings.flake8_pytest_style.fixture_parentheses

View file

@ -146,8 +146,10 @@ fn check_mark_parentheses(checker: &Checker, decorator: &Decorator, marker: &str
args,
keywords,
range: _,
node_index: _,
},
range: _,
node_index: _,
}) => {
if !checker.settings.flake8_pytest_style.mark_parentheses
&& args.is_empty()

View file

@ -301,6 +301,7 @@ fn elts_to_csv(elts: &[Expr], generator: Generator, flags: StringLiteralFlags) -
})
.into_boxed_str(),
range: TextRange::default(),
node_index: ruff_python_ast::AtomicNodeIndex::dummy(),
flags,
});
Some(generator.expr(&node))
@ -363,12 +364,14 @@ fn check_names(checker: &Checker, call: &ExprCall, expr: &Expr, argvalues: &Expr
Expr::from(ast::StringLiteral {
value: Box::from(*name),
range: TextRange::default(),
node_index: ruff_python_ast::AtomicNodeIndex::dummy(),
flags: checker.default_string_flags(),
})
})
.collect(),
ctx: ExprContext::Load,
range: TextRange::default(),
node_index: ruff_python_ast::AtomicNodeIndex::dummy(),
parenthesized: true,
});
diagnostic.set_fix(Fix::unsafe_edit(Edit::range_replacement(
@ -398,12 +401,14 @@ fn check_names(checker: &Checker, call: &ExprCall, expr: &Expr, argvalues: &Expr
Expr::from(ast::StringLiteral {
value: Box::from(*name),
range: TextRange::default(),
node_index: ruff_python_ast::AtomicNodeIndex::dummy(),
flags: checker.default_string_flags(),
})
})
.collect(),
ctx: ExprContext::Load,
range: TextRange::default(),
node_index: ruff_python_ast::AtomicNodeIndex::dummy(),
});
diagnostic.set_fix(Fix::unsafe_edit(Edit::range_replacement(
checker.generator().expr(&node),
@ -432,6 +437,7 @@ fn check_names(checker: &Checker, call: &ExprCall, expr: &Expr, argvalues: &Expr
elts: elts.clone(),
ctx: ExprContext::Load,
range: TextRange::default(),
node_index: ruff_python_ast::AtomicNodeIndex::dummy(),
});
diagnostic.set_fix(Fix::unsafe_edit(Edit::range_replacement(
checker.generator().expr(&node),
@ -476,6 +482,7 @@ fn check_names(checker: &Checker, call: &ExprCall, expr: &Expr, argvalues: &Expr
elts: elts.clone(),
ctx: ExprContext::Load,
range: TextRange::default(),
node_index: ruff_python_ast::AtomicNodeIndex::dummy(),
parenthesized: true,
});
diagnostic.set_fix(Fix::unsafe_edit(Edit::range_replacement(

View file

@ -84,6 +84,7 @@ fn check_patch_call(checker: &Checker, call: &ast::ExprCall, index: usize) {
parameters,
body,
range: _,
node_index: _,
}) = call
.arguments
.find_argument_value("new", index)

View file

@ -166,6 +166,7 @@ fn assert(expr: &Expr, msg: Option<&Expr>) -> Stmt {
test: Box::new(expr.clone()),
msg: msg.map(|msg| Box::new(msg.clone())),
range: TextRange::default(),
node_index: ruff_python_ast::AtomicNodeIndex::dummy(),
})
}
@ -175,6 +176,7 @@ fn compare(left: &Expr, cmp_op: CmpOp, right: &Expr) -> Expr {
ops: Box::from([cmp_op]),
comparators: Box::from([right.clone()]),
range: TextRange::default(),
node_index: ruff_python_ast::AtomicNodeIndex::dummy(),
})
}
@ -294,6 +296,7 @@ impl UnittestAssert {
op: UnaryOp::Not,
operand: Box::new(expr.clone()),
range: TextRange::default(),
node_index: ruff_python_ast::AtomicNodeIndex::dummy(),
}),
msg,
)
@ -367,6 +370,7 @@ impl UnittestAssert {
};
let node = Expr::NoneLiteral(ast::ExprNoneLiteral {
range: TextRange::default(),
node_index: ruff_python_ast::AtomicNodeIndex::dummy(),
});
let expr = compare(expr, cmp_op, &node);
Ok(assert(&expr, msg))
@ -383,6 +387,7 @@ impl UnittestAssert {
id: Name::new_static("isinstance"),
ctx: ExprContext::Load,
range: TextRange::default(),
node_index: ruff_python_ast::AtomicNodeIndex::dummy(),
};
let node1 = ast::ExprCall {
func: Box::new(node.into()),
@ -390,8 +395,10 @@ impl UnittestAssert {
args: Box::from([(**obj).clone(), (**cls).clone()]),
keywords: Box::from([]),
range: TextRange::default(),
node_index: ruff_python_ast::AtomicNodeIndex::dummy(),
},
range: TextRange::default(),
node_index: ruff_python_ast::AtomicNodeIndex::dummy(),
};
let isinstance = node1.into();
if matches!(self, UnittestAssert::IsInstance) {
@ -401,6 +408,7 @@ impl UnittestAssert {
op: UnaryOp::Not,
operand: Box::new(isinstance),
range: TextRange::default(),
node_index: ruff_python_ast::AtomicNodeIndex::dummy(),
};
let expr = node.into();
Ok(assert(&expr, msg))
@ -421,12 +429,14 @@ impl UnittestAssert {
id: Name::new_static("re"),
ctx: ExprContext::Load,
range: TextRange::default(),
node_index: ruff_python_ast::AtomicNodeIndex::dummy(),
};
let node1 = ast::ExprAttribute {
value: Box::new(node.into()),
attr: Identifier::new("search".to_string(), TextRange::default()),
ctx: ExprContext::Load,
range: TextRange::default(),
node_index: ruff_python_ast::AtomicNodeIndex::dummy(),
};
let node2 = ast::ExprCall {
func: Box::new(node1.into()),
@ -434,8 +444,10 @@ impl UnittestAssert {
args: Box::from([(**regex).clone(), (**text).clone()]),
keywords: Box::from([]),
range: TextRange::default(),
node_index: ruff_python_ast::AtomicNodeIndex::dummy(),
},
range: TextRange::default(),
node_index: ruff_python_ast::AtomicNodeIndex::dummy(),
};
let re_search = node2.into();
if matches!(self, UnittestAssert::Regex | UnittestAssert::RegexpMatches) {
@ -445,6 +457,7 @@ impl UnittestAssert {
op: UnaryOp::Not,
operand: Box::new(re_search),
range: TextRange::default(),
node_index: ruff_python_ast::AtomicNodeIndex::dummy(),
};
Ok(assert(&node.into(), msg))
}

View file

@ -67,6 +67,7 @@ pub(crate) fn unnecessary_escaped_quote(checker: &Checker, string_like: StringLi
ast::StringLikePart::FString(ast::FString {
elements,
range,
node_index: _,
flags,
}) => {
check_interpolated_string(checker, AnyStringFlags::from(*flags), *range, elements);
@ -74,6 +75,7 @@ pub(crate) fn unnecessary_escaped_quote(checker: &Checker, string_like: StringLi
ast::StringLikePart::TString(ast::TString {
elements,
range,
node_index: _,
flags,
}) => {
check_interpolated_string(checker, AnyStringFlags::from(*flags), *range, elements);

View file

@ -58,6 +58,7 @@ pub(crate) fn unnecessary_paren_on_raise_exception(checker: &Checker, expr: &Exp
func,
arguments,
range: _,
node_index: _,
}) = expr
else {
return;

View file

@ -95,8 +95,16 @@ impl<'a> Visitor<'a> for ReturnVisitor<'_, 'a> {
// But don't recurse into the body.
return;
}
Stmt::Global(ast::StmtGlobal { names, range: _ })
| Stmt::Nonlocal(ast::StmtNonlocal { names, range: _ }) => {
Stmt::Global(ast::StmtGlobal {
names,
range: _,
node_index: _,
})
| Stmt::Nonlocal(ast::StmtNonlocal {
names,
range: _,
node_index: _,
}) => {
self.stack
.non_locals
.extend(names.iter().map(Identifier::as_str));

View file

@ -307,8 +307,10 @@ fn isinstance_target<'a>(call: &'a Expr, semantic: &'a SemanticModel) -> Option<
args,
keywords,
range: _,
node_index: _,
},
range: _,
node_index: _,
} = call.as_call_expr()?;
if args.len() != 2 {
return None;
@ -330,6 +332,7 @@ pub(crate) fn duplicate_isinstance_call(checker: &Checker, expr: &Expr) {
op: BoolOp::Or,
values,
range: _,
node_index: _,
}) = expr
else {
return;
@ -418,6 +421,7 @@ pub(crate) fn duplicate_isinstance_call(checker: &Checker, expr: &Expr) {
.collect(),
ctx: ExprContext::Load,
range: TextRange::default(),
node_index: ruff_python_ast::AtomicNodeIndex::dummy(),
parenthesized: true,
};
let isinstance_call = ast::ExprCall {
@ -426,6 +430,7 @@ pub(crate) fn duplicate_isinstance_call(checker: &Checker, expr: &Expr) {
id: Name::new_static("isinstance"),
ctx: ExprContext::Load,
range: TextRange::default(),
node_index: ruff_python_ast::AtomicNodeIndex::dummy(),
}
.into(),
),
@ -433,8 +438,10 @@ pub(crate) fn duplicate_isinstance_call(checker: &Checker, expr: &Expr) {
args: Box::from([target.clone(), tuple.into()]),
keywords: Box::from([]),
range: TextRange::default(),
node_index: ruff_python_ast::AtomicNodeIndex::dummy(),
},
range: TextRange::default(),
node_index: ruff_python_ast::AtomicNodeIndex::dummy(),
}
.into();
@ -451,6 +458,7 @@ pub(crate) fn duplicate_isinstance_call(checker: &Checker, expr: &Expr) {
.chain(after)
.collect(),
range: TextRange::default(),
node_index: ruff_python_ast::AtomicNodeIndex::dummy(),
}
.into();
let fixed_source = checker.generator().expr(&bool_op);
@ -472,6 +480,7 @@ fn match_eq_target(expr: &Expr) -> Option<(&Name, &Expr)> {
ops,
comparators,
range: _,
node_index: _,
}) = expr
else {
return None;
@ -497,6 +506,7 @@ pub(crate) fn compare_with_tuple(checker: &Checker, expr: &Expr) {
op: BoolOp::Or,
values,
range: _,
node_index: _,
}) = expr
else {
return;
@ -542,18 +552,21 @@ pub(crate) fn compare_with_tuple(checker: &Checker, expr: &Expr) {
elts: comparators.into_iter().cloned().collect(),
ctx: ExprContext::Load,
range: TextRange::default(),
node_index: ruff_python_ast::AtomicNodeIndex::dummy(),
parenthesized: true,
};
let node1 = ast::ExprName {
id: id.clone(),
ctx: ExprContext::Load,
range: TextRange::default(),
node_index: ruff_python_ast::AtomicNodeIndex::dummy(),
};
let node2 = ast::ExprCompare {
left: Box::new(node1.into()),
ops: Box::from([CmpOp::In]),
comparators: Box::from([node.into()]),
range: TextRange::default(),
node_index: ruff_python_ast::AtomicNodeIndex::dummy(),
};
let in_expr = node2.into();
let mut diagnostic = checker.report_diagnostic(
@ -576,6 +589,7 @@ pub(crate) fn compare_with_tuple(checker: &Checker, expr: &Expr) {
op: BoolOp::Or,
values: iter::once(in_expr).chain(unmatched).collect(),
range: TextRange::default(),
node_index: ruff_python_ast::AtomicNodeIndex::dummy(),
};
node.into()
};
@ -592,6 +606,7 @@ pub(crate) fn expr_and_not_expr(checker: &Checker, expr: &Expr) {
op: BoolOp::And,
values,
range: _,
node_index: _,
}) = expr
else {
return;
@ -608,6 +623,7 @@ pub(crate) fn expr_and_not_expr(checker: &Checker, expr: &Expr) {
op: UnaryOp::Not,
operand,
range: _,
node_index: _,
}) = expr
{
negated_expr.push(operand);
@ -648,6 +664,7 @@ pub(crate) fn expr_or_not_expr(checker: &Checker, expr: &Expr) {
op: BoolOp::Or,
values,
range: _,
node_index: _,
}) = expr
else {
return;
@ -664,6 +681,7 @@ pub(crate) fn expr_or_not_expr(checker: &Checker, expr: &Expr) {
op: UnaryOp::Not,
operand,
range: _,
node_index: _,
}) = expr
{
negated_expr.push(operand);
@ -733,6 +751,7 @@ fn is_short_circuit(
op,
values,
range: _,
node_index: _,
}) = expr
else {
return None;

View file

@ -232,6 +232,7 @@ fn check_os_environ_subscript(checker: &Checker, expr: &Expr) {
}
}),
range: TextRange::default(),
node_index: ruff_python_ast::AtomicNodeIndex::dummy(),
};
let new_env_var = node.into();
diagnostic.set_fix(Fix::unsafe_edit(Edit::range_replacement(
@ -246,6 +247,7 @@ pub(crate) fn dict_get_with_none_default(checker: &Checker, expr: &Expr) {
func,
arguments: Arguments { args, keywords, .. },
range: _,
node_index: _,
}) = expr
else {
return;

View file

@ -188,6 +188,7 @@ pub(crate) fn if_expr_with_true_false(
id: Name::new_static("bool"),
ctx: ExprContext::Load,
range: TextRange::default(),
node_index: ruff_python_ast::AtomicNodeIndex::dummy(),
}
.into(),
),
@ -195,8 +196,10 @@ pub(crate) fn if_expr_with_true_false(
args: Box::from([test.clone()]),
keywords: Box::from([]),
range: TextRange::default(),
node_index: ruff_python_ast::AtomicNodeIndex::dummy(),
},
range: TextRange::default(),
node_index: ruff_python_ast::AtomicNodeIndex::dummy(),
}
.into(),
),
@ -224,6 +227,7 @@ pub(crate) fn if_expr_with_false_true(
op: UnaryOp::Not,
operand: Box::new(test.clone()),
range: TextRange::default(),
node_index: ruff_python_ast::AtomicNodeIndex::dummy(),
}
.into(),
),
@ -243,6 +247,7 @@ pub(crate) fn twisted_arms_in_ifexpr(
op,
operand,
range: _,
node_index: _,
}) = &test
else {
return;
@ -277,6 +282,7 @@ pub(crate) fn twisted_arms_in_ifexpr(
body: Box::new(node1),
orelse: Box::new(node),
range: TextRange::default(),
node_index: ruff_python_ast::AtomicNodeIndex::dummy(),
};
diagnostic.set_fix(Fix::unsafe_edit(Edit::range_replacement(
checker.generator().expr(&node3.into()),

View file

@ -153,6 +153,7 @@ pub(crate) fn negation_with_equal_op(checker: &Checker, expr: &Expr, op: UnaryOp
ops,
comparators,
range: _,
node_index: _,
}) = operand
else {
return;
@ -185,6 +186,7 @@ pub(crate) fn negation_with_equal_op(checker: &Checker, expr: &Expr, op: UnaryOp
ops: Box::from([CmpOp::NotEq]),
comparators: comparators.clone(),
range: TextRange::default(),
node_index: ruff_python_ast::AtomicNodeIndex::dummy(),
};
diagnostic.set_fix(Fix::unsafe_edit(Edit::range_replacement(
checker.generator().expr(&node.into()),
@ -207,6 +209,7 @@ pub(crate) fn negation_with_not_equal_op(
ops,
comparators,
range: _,
node_index: _,
}) = operand
else {
return;
@ -239,6 +242,7 @@ pub(crate) fn negation_with_not_equal_op(
ops: Box::from([CmpOp::Eq]),
comparators: comparators.clone(),
range: TextRange::default(),
node_index: ruff_python_ast::AtomicNodeIndex::dummy(),
};
diagnostic.set_fix(Fix::unsafe_edit(Edit::range_replacement(
checker.generator().expr(&node.into()),
@ -255,6 +259,7 @@ pub(crate) fn double_negation(checker: &Checker, expr: &Expr, op: UnaryOp, opera
op: operand_op,
operand,
range: _,
node_index: _,
}) = operand
else {
return;
@ -279,6 +284,7 @@ pub(crate) fn double_negation(checker: &Checker, expr: &Expr, op: UnaryOp, opera
id: Name::new_static("bool"),
ctx: ExprContext::Load,
range: TextRange::default(),
node_index: ruff_python_ast::AtomicNodeIndex::dummy(),
};
let node1 = ast::ExprCall {
func: Box::new(node.into()),
@ -286,8 +292,10 @@ pub(crate) fn double_negation(checker: &Checker, expr: &Expr, op: UnaryOp, opera
args: Box::from([*operand.clone()]),
keywords: Box::from([]),
range: TextRange::default(),
node_index: ruff_python_ast::AtomicNodeIndex::dummy(),
},
range: TextRange::default(),
node_index: ruff_python_ast::AtomicNodeIndex::dummy(),
};
diagnostic.set_fix(Fix::safe_edit(Edit::range_replacement(
checker.generator().expr(&node1.into()),

View file

@ -123,6 +123,7 @@ pub(crate) fn if_else_block_instead_of_dict_get(checker: &Checker, stmt_if: &ast
ops,
comparators: test_dict,
range: _,
node_index: _,
}) = &**test
else {
return;
@ -187,6 +188,7 @@ pub(crate) fn if_else_block_instead_of_dict_get(checker: &Checker, stmt_if: &ast
attr: Identifier::new("get".to_string(), TextRange::default()),
ctx: ExprContext::Load,
range: TextRange::default(),
node_index: ruff_python_ast::AtomicNodeIndex::dummy(),
};
let node3 = ast::ExprCall {
func: Box::new(node2.into()),
@ -194,14 +196,17 @@ pub(crate) fn if_else_block_instead_of_dict_get(checker: &Checker, stmt_if: &ast
args: Box::from([node1, node]),
keywords: Box::from([]),
range: TextRange::default(),
node_index: ruff_python_ast::AtomicNodeIndex::dummy(),
},
range: TextRange::default(),
node_index: ruff_python_ast::AtomicNodeIndex::dummy(),
};
let node4 = expected_var.clone();
let node5 = ast::StmtAssign {
targets: vec![node4],
value: Box::new(node3.into()),
range: TextRange::default(),
node_index: ruff_python_ast::AtomicNodeIndex::dummy(),
};
let contents = checker.generator().stmt(&node5.into());
@ -246,6 +251,7 @@ pub(crate) fn if_exp_instead_of_dict_get(
ops,
comparators: test_dict,
range: _,
node_index: _,
}) = test
else {
return;
@ -291,6 +297,7 @@ pub(crate) fn if_exp_instead_of_dict_get(
attr: Identifier::new("get".to_string(), TextRange::default()),
ctx: ExprContext::Load,
range: TextRange::default(),
node_index: ruff_python_ast::AtomicNodeIndex::dummy(),
};
let fixed_node = ast::ExprCall {
func: Box::new(dict_get_node.into()),
@ -298,8 +305,10 @@ pub(crate) fn if_exp_instead_of_dict_get(
args: Box::from([dict_key_node, default_value_node]),
keywords: Box::from([]),
range: TextRange::default(),
node_index: ruff_python_ast::AtomicNodeIndex::dummy(),
},
range: TextRange::default(),
node_index: ruff_python_ast::AtomicNodeIndex::dummy(),
};
let contents = checker.generator().expr(&fixed_node.into());

View file

@ -57,6 +57,7 @@ pub(crate) fn if_else_block_instead_of_dict_lookup(checker: &Checker, stmt_if: &
ops,
comparators,
range: _,
node_index: _,
}) = test.as_ref()
else {
return;
@ -73,7 +74,14 @@ pub(crate) fn if_else_block_instead_of_dict_lookup(checker: &Checker, stmt_if: &
let Some(literal_expr) = expr.as_literal_expr() else {
return;
};
let [Stmt::Return(ast::StmtReturn { value, range: _ })] = body.as_slice() else {
let [
Stmt::Return(ast::StmtReturn {
value,
range: _,
node_index: _,
}),
] = body.as_slice()
else {
return;
};
@ -99,7 +107,14 @@ pub(crate) fn if_else_block_instead_of_dict_lookup(checker: &Checker, stmt_if: &
for clause in elif_else_clauses {
let ElifElseClause { test, body, .. } = clause;
let [Stmt::Return(ast::StmtReturn { value, range: _ })] = body.as_slice() else {
let [
Stmt::Return(ast::StmtReturn {
value,
range: _,
node_index: _,
}),
] = body.as_slice()
else {
return;
};
@ -107,7 +122,14 @@ pub(crate) fn if_else_block_instead_of_dict_lookup(checker: &Checker, stmt_if: &
// `else`
None => {
// The else must also be a single effect-free return statement
let [Stmt::Return(ast::StmtReturn { value, range: _ })] = body.as_slice() else {
let [
Stmt::Return(ast::StmtReturn {
value,
range: _,
node_index: _,
}),
] = body.as_slice()
else {
return;
};
if value.as_ref().is_some_and(|value| {
@ -122,6 +144,7 @@ pub(crate) fn if_else_block_instead_of_dict_lookup(checker: &Checker, stmt_if: &
ops,
comparators,
range: _,
node_index: _,
})) => {
let Expr::Name(ast::ExprName { id, .. }) = left.as_ref() else {
return;

View file

@ -96,6 +96,7 @@ pub(crate) fn if_else_block_instead_of_if_exp(checker: &Checker, stmt_if: &ast::
body,
elif_else_clauses,
range: _,
node_index: _,
} = stmt_if;
// `test: None` to only match an `else` clause
@ -268,11 +269,13 @@ fn assignment_ternary(
body: Box::new(body_value.clone()),
orelse: Box::new(orelse_value.clone()),
range: TextRange::default(),
node_index: ruff_python_ast::AtomicNodeIndex::dummy(),
};
let node1 = ast::StmtAssign {
targets: vec![target_var.clone()],
value: Box::new(node.into()),
range: TextRange::default(),
node_index: ruff_python_ast::AtomicNodeIndex::dummy(),
};
node1.into()
}
@ -282,11 +285,13 @@ fn assignment_binary_and(target_var: &Expr, left_value: &Expr, right_value: &Exp
op: BoolOp::And,
values: vec![left_value.clone(), right_value.clone()],
range: TextRange::default(),
node_index: ruff_python_ast::AtomicNodeIndex::dummy(),
};
let node1 = ast::StmtAssign {
targets: vec![target_var.clone()],
value: Box::new(node.into()),
range: TextRange::default(),
node_index: ruff_python_ast::AtomicNodeIndex::dummy(),
};
node1.into()
}
@ -294,10 +299,12 @@ fn assignment_binary_and(target_var: &Expr, left_value: &Expr, right_value: &Exp
fn assignment_binary_or(target_var: &Expr, left_value: &Expr, right_value: &Expr) -> Stmt {
(ast::StmtAssign {
range: TextRange::default(),
node_index: ruff_python_ast::AtomicNodeIndex::dummy(),
targets: vec![target_var.clone()],
value: Box::new(
(ast::ExprBoolOp {
range: TextRange::default(),
node_index: ruff_python_ast::AtomicNodeIndex::dummy(),
op: BoolOp::Or,
values: vec![left_value.clone(), right_value.clone()],
})

View file

@ -60,6 +60,7 @@ fn key_in_dict(checker: &Checker, left: &Expr, right: &Expr, operator: CmpOp, pa
func,
arguments: Arguments { args, keywords, .. },
range: _,
node_index: _,
}) = &right
else {
return;

View file

@ -132,11 +132,13 @@ pub(crate) fn needless_bool(checker: &Checker, stmt: &Stmt) {
body: elif_body,
test: Some(elif_test),
range: elif_range,
node_index: _,
},
ElifElseClause {
body: else_body,
test: None,
range: else_range,
node_index: _,
},
] => (
elif_test,
@ -254,6 +256,7 @@ pub(crate) fn needless_bool(checker: &Checker, stmt: &Stmt) {
left: left.clone(),
comparators: Box::new([right.clone()]),
range: TextRange::default(),
node_index: ruff_python_ast::AtomicNodeIndex::dummy(),
}))
}
@ -261,6 +264,7 @@ pub(crate) fn needless_bool(checker: &Checker, stmt: &Stmt) {
op: ast::UnaryOp::Not,
operand: Box::new(if_test.clone()),
range: TextRange::default(),
node_index: ruff_python_ast::AtomicNodeIndex::dummy(),
})),
}
} else if if_test.is_compare_expr() {
@ -273,6 +277,7 @@ pub(crate) fn needless_bool(checker: &Checker, stmt: &Stmt) {
id: Name::new_static("bool"),
ctx: ExprContext::Load,
range: TextRange::default(),
node_index: ruff_python_ast::AtomicNodeIndex::dummy(),
};
let call_node = ast::ExprCall {
func: Box::new(func_node.into()),
@ -280,8 +285,10 @@ pub(crate) fn needless_bool(checker: &Checker, stmt: &Stmt) {
args: Box::from([if_test.clone()]),
keywords: Box::from([]),
range: TextRange::default(),
node_index: ruff_python_ast::AtomicNodeIndex::dummy(),
},
range: TextRange::default(),
node_index: ruff_python_ast::AtomicNodeIndex::dummy(),
};
Some(Expr::Call(call_node))
} else {
@ -294,6 +301,7 @@ pub(crate) fn needless_bool(checker: &Checker, stmt: &Stmt) {
Stmt::Return(ast::StmtReturn {
value: Some(Box::new(expr.clone())),
range: TextRange::default(),
node_index: ruff_python_ast::AtomicNodeIndex::dummy(),
})
});
@ -336,7 +344,12 @@ fn is_one_line_return_bool(stmts: &[Stmt]) -> Option<Bool> {
let [stmt] = stmts else {
return None;
};
let Stmt::Return(ast::StmtReturn { value, range: _ }) = stmt else {
let Stmt::Return(ast::StmtReturn {
value,
range: _,
node_index: _,
}) = stmt
else {
return None;
};
let Some(Expr::BooleanLiteral(ast::ExprBooleanLiteral { value, .. })) = value.as_deref() else {

View file

@ -49,7 +49,12 @@ fn match_async_exit_stack(semantic: &SemanticModel) -> bool {
let Some(expr) = semantic.current_expression_grandparent() else {
return false;
};
let Expr::Await(ast::ExprAwait { value, range: _ }) = expr else {
let Expr::Await(ast::ExprAwait {
value,
range: _,
node_index: _,
}) = expr
else {
return false;
};
let Expr::Call(ast::ExprCall { func, .. }) = value.as_ref() else {

View file

@ -133,6 +133,7 @@ pub(crate) fn convert_for_loop_to_any_all(checker: &Checker, stmt: &Stmt) {
op: UnaryOp::Not,
operand,
range: _,
node_index: _,
}) = &loop_.test
{
*operand.clone()
@ -141,6 +142,7 @@ pub(crate) fn convert_for_loop_to_any_all(checker: &Checker, stmt: &Stmt) {
ops,
comparators,
range: _,
node_index: _,
}) = &loop_.test
{
if let ([op], [comparator]) = (&**ops, &**comparators) {
@ -161,6 +163,7 @@ pub(crate) fn convert_for_loop_to_any_all(checker: &Checker, stmt: &Stmt) {
ops: Box::from([op]),
comparators: Box::from([comparator.clone()]),
range: TextRange::default(),
node_index: ruff_python_ast::AtomicNodeIndex::dummy(),
};
node.into()
} else {
@ -168,6 +171,7 @@ pub(crate) fn convert_for_loop_to_any_all(checker: &Checker, stmt: &Stmt) {
op: UnaryOp::Not,
operand: Box::new(loop_.test.clone()),
range: TextRange::default(),
node_index: ruff_python_ast::AtomicNodeIndex::dummy(),
};
node.into()
}
@ -176,6 +180,7 @@ pub(crate) fn convert_for_loop_to_any_all(checker: &Checker, stmt: &Stmt) {
op: UnaryOp::Not,
operand: Box::new(loop_.test.clone()),
range: TextRange::default(),
node_index: ruff_python_ast::AtomicNodeIndex::dummy(),
};
node.into()
}
@ -276,6 +281,7 @@ fn match_loop(stmt: &Stmt) -> Option<Loop> {
test: nested_test,
elif_else_clauses: nested_elif_else_clauses,
range: _,
node_index: _,
}),
] = body.as_slice()
else {
@ -288,6 +294,7 @@ fn match_loop(stmt: &Stmt) -> Option<Loop> {
Stmt::Return(ast::StmtReturn {
value: Some(value),
range: _,
node_index: _,
}),
] = nested_body.as_slice()
else {
@ -325,6 +332,7 @@ fn match_else_return(stmt: &Stmt) -> Option<Terminal> {
Stmt::Return(ast::StmtReturn {
value: Some(next_value),
range: _,
node_index: _,
}),
] = orelse.as_slice()
else {
@ -368,6 +376,7 @@ fn match_sibling_return<'a>(stmt: &'a Stmt, sibling: &'a Stmt) -> Option<Termina
let Stmt::Return(ast::StmtReturn {
value: Some(next_value),
range: _,
node_index: _,
}) = &sibling
else {
return None;
@ -395,14 +404,17 @@ fn return_stmt(id: Name, test: &Expr, target: &Expr, iter: &Expr, generator: Gen
ifs: vec![],
is_async: false,
range: TextRange::default(),
node_index: ruff_python_ast::AtomicNodeIndex::dummy(),
}],
range: TextRange::default(),
node_index: ruff_python_ast::AtomicNodeIndex::dummy(),
parenthesized: false,
};
let node1 = ast::ExprName {
id,
ctx: ExprContext::Load,
range: TextRange::default(),
node_index: ruff_python_ast::AtomicNodeIndex::dummy(),
};
let node2 = ast::ExprCall {
func: Box::new(node1.into()),
@ -410,12 +422,15 @@ fn return_stmt(id: Name, test: &Expr, target: &Expr, iter: &Expr, generator: Gen
args: Box::from([node.into()]),
keywords: Box::from([]),
range: TextRange::default(),
node_index: ruff_python_ast::AtomicNodeIndex::dummy(),
},
range: TextRange::default(),
node_index: ruff_python_ast::AtomicNodeIndex::dummy(),
};
let node3 = ast::StmtReturn {
value: Some(Box::new(node2.into())),
range: TextRange::default(),
node_index: ruff_python_ast::AtomicNodeIndex::dummy(),
};
generator.stmt(&node3.into())
}

View file

@ -124,6 +124,7 @@ fn construct_replacement(elts: &[&str], flags: StringLiteralFlags) -> Expr {
Expr::from(StringLiteral {
value: Box::from(*elt),
range: TextRange::default(),
node_index: ruff_python_ast::AtomicNodeIndex::dummy(),
// intentionally omit the triple quote flag, if set, to avoid strange
// replacements like
//
@ -140,6 +141,7 @@ fn construct_replacement(elts: &[&str], flags: StringLiteralFlags) -> Expr {
.collect(),
ctx: ExprContext::Load,
range: TextRange::default(),
node_index: ruff_python_ast::AtomicNodeIndex::dummy(),
})
}

View file

@ -65,7 +65,13 @@ impl Violation for SuppressibleException {
fn is_empty(body: &[Stmt]) -> bool {
match body {
[Stmt::Pass(_)] => true,
[Stmt::Expr(ast::StmtExpr { value, range: _ })] => value.is_ellipsis_literal_expr(),
[
Stmt::Expr(ast::StmtExpr {
value,
range: _,
node_index: _,
}),
] => value.is_ellipsis_literal_expr(),
_ => false,
}
}

View file

@ -116,6 +116,7 @@ impl From<&Expr> for ConstantLikelihood {
op: UnaryOp::UAdd | UnaryOp::USub | UnaryOp::Invert,
operand,
range: _,
node_index: _,
}) => ConstantLikelihood::from(&**operand),
_ => ConstantLikelihood::Unlikely,
}

View file

@ -101,6 +101,7 @@ fn fix_banned_relative_import(
names: names.clone(),
level: 0,
range: TextRange::default(),
node_index: ruff_python_ast::AtomicNodeIndex::dummy(),
};
let content = generator.stmt(&node.into());
Some(Fix::unsafe_edit(Edit::range_replacement(

View file

@ -367,6 +367,7 @@ impl<'a> QuoteAnnotator<'a> {
let annotation = subgenerator.expr(&expr_without_forward_references);
generator.expr(&Expr::from(ast::StringLiteral {
range: TextRange::default(),
node_index: ruff_python_ast::AtomicNodeIndex::dummy(),
value: annotation.into_boxed_str(),
flags: self.flags,
}))

View file

@ -9,6 +9,7 @@ fn to_interpolated_string_interpolation_element(inner: &Expr) -> ast::Interpolat
conversion: ConversionFlag::None,
format_spec: None,
range: TextRange::default(),
node_index: ruff_python_ast::AtomicNodeIndex::dummy(),
})
}
@ -17,6 +18,7 @@ pub(super) fn to_interpolated_string_literal_element(s: &str) -> ast::Interpolat
ast::InterpolatedStringElement::Literal(ast::InterpolatedStringLiteralElement {
value: Box::from(s),
range: TextRange::default(),
node_index: ruff_python_ast::AtomicNodeIndex::dummy(),
})
}
@ -31,8 +33,10 @@ fn is_simple_call(expr: &Expr) -> bool {
args,
keywords,
range: _,
node_index: _,
},
range: _,
node_index: _,
}) => args.is_empty() && keywords.is_empty() && is_simple_callee(func),
_ => false,
}
@ -53,12 +57,17 @@ pub(super) fn to_interpolated_string_element(
expr: &Expr,
) -> Option<ast::InterpolatedStringElement> {
match expr {
Expr::StringLiteral(ast::ExprStringLiteral { value, range }) => Some(
ast::InterpolatedStringElement::Literal(ast::InterpolatedStringLiteralElement {
Expr::StringLiteral(ast::ExprStringLiteral {
value,
range,
node_index,
}) => Some(ast::InterpolatedStringElement::Literal(
ast::InterpolatedStringLiteralElement {
value: value.to_string().into_boxed_str(),
range: *range,
}),
),
node_index: node_index.clone(),
},
)),
// These should be pretty safe to wrap in a formatted value.
Expr::NumberLiteral(_) | Expr::BooleanLiteral(_) | Expr::Name(_) | Expr::Attribute(_) => {
Some(to_interpolated_string_interpolation_element(expr))

View file

@ -91,6 +91,7 @@ fn build_fstring(joiner: &str, joinees: &[Expr], flags: FStringFlags) -> Option<
.into_boxed_str(),
flags: flags?,
range: TextRange::default(),
node_index: ruff_python_ast::AtomicNodeIndex::dummy(),
};
return Some(node.into());
}
@ -113,6 +114,7 @@ fn build_fstring(joiner: &str, joinees: &[Expr], flags: FStringFlags) -> Option<
let node = ast::FString {
elements: f_string_elements.into(),
range: TextRange::default(),
node_index: ruff_python_ast::AtomicNodeIndex::dummy(),
flags,
};
Some(node.into())

View file

@ -23,7 +23,11 @@ pub(crate) fn annotate_imports<'a>(
.iter()
.map(|import| {
match import {
Stmt::Import(ast::StmtImport { names, range }) => {
Stmt::Import(ast::StmtImport {
names,
range,
node_index: _,
}) => {
// Find comments above.
let mut atop = vec![];
while let Some(comment) =
@ -59,6 +63,7 @@ pub(crate) fn annotate_imports<'a>(
names,
level,
range: _,
node_index: _,
}) => {
// Find comments above.
let mut atop = vec![];

View file

@ -58,7 +58,12 @@ impl AlwaysFixableViolation for MissingRequiredImport {
fn includes_import(stmt: &Stmt, target: &NameImport) -> bool {
match target {
NameImport::Import(target) => {
let Stmt::Import(ast::StmtImport { names, range: _ }) = &stmt else {
let Stmt::Import(ast::StmtImport {
names,
range: _,
node_index: _,
}) = &stmt
else {
return false;
};
names.iter().any(|alias| {
@ -72,6 +77,7 @@ fn includes_import(stmt: &Stmt, target: &NameImport) -> bool {
names,
level,
range: _,
node_index: _,
}) = &stmt
else {
return false;

View file

@ -87,6 +87,7 @@ pub(crate) fn nunique_constant_series_check(
Expr::NumberLiteral(ast::ExprNumberLiteral {
value: ast::Number::Int(Int::ONE),
range: _,
node_index: _,
})
) {
return;

View file

@ -121,6 +121,7 @@ pub(crate) fn manual_dict_comprehension(checker: &Checker, for_stmt: &ast::StmtF
targets,
value,
range,
node_index: _,
}) = stmt
else {
return;

View file

@ -148,8 +148,10 @@ pub(crate) fn manual_list_comprehension(checker: &Checker, for_stmt: &ast::StmtF
args,
keywords,
range: _,
node_index: _,
},
range,
node_index: _,
}) = value.as_ref()
else {
return;

View file

@ -69,8 +69,10 @@ pub(crate) fn manual_list_copy(checker: &Checker, for_stmt: &ast::StmtFor) {
args,
keywords,
range: _,
node_index: _,
},
range,
node_index: _,
}) = value.as_ref()
else {
return;

View file

@ -58,8 +58,10 @@ pub(crate) fn unnecessary_list_cast(checker: &Checker, iter: &Expr, body: &[Stmt
args,
keywords: _,
range: _,
node_index: _,
},
range: list_range,
node_index: _,
}) = iter
else {
return;

View file

@ -187,6 +187,7 @@ fn function(
ExprEllipsisLiteral::default(),
))),
range: TextRange::default(),
node_index: ruff_python_ast::AtomicNodeIndex::dummy(),
});
let parameters = lambda.parameters.as_deref().cloned().unwrap_or_default();
if let Some(annotation) = annotation {
@ -234,6 +235,7 @@ fn function(
returns: Some(Box::new(return_type)),
type_params: None,
range: TextRange::default(),
node_index: ruff_python_ast::AtomicNodeIndex::dummy(),
});
let generated = checker.generator().stmt(&func);
@ -249,6 +251,7 @@ fn function(
returns: None,
type_params: None,
range: TextRange::default(),
node_index: ruff_python_ast::AtomicNodeIndex::dummy(),
});
let generated = checker.generator().stmt(&function);

View file

@ -75,6 +75,7 @@ fn split_imports(
.map(|alias| {
let Alias {
range: _,
node_index: _,
name,
asname,
} = alias;
@ -99,6 +100,7 @@ fn split_imports(
.map(|alias| {
let Alias {
range: _,
node_index: _,
name,
asname,
} = alias;

View file

@ -88,6 +88,7 @@ pub(crate) fn not_tests(checker: &Checker, unary_op: &ast::ExprUnaryOp) {
ops,
comparators,
range: _,
node_index: _,
}) = unary_op.operand.as_ref()
else {
return;

View file

@ -673,6 +673,7 @@ impl<'a> Visitor<'a> for BodyVisitor<'a> {
}
Stmt::Return(ast::StmtReturn {
range,
node_index: _,
value: Some(value),
}) => {
self.returns.push(ReturnEntry {
@ -684,7 +685,11 @@ impl<'a> Visitor<'a> for BodyVisitor<'a> {
},
});
}
Stmt::Return(ast::StmtReturn { range, value: None }) => {
Stmt::Return(ast::StmtReturn {
range,
node_index: _,
value: None,
}) => {
self.returns.push(ReturnEntry {
range: *range,
kind: ReturnEntryKind::ImplicitNone,
@ -701,6 +706,7 @@ impl<'a> Visitor<'a> for BodyVisitor<'a> {
match expr {
Expr::Yield(ast::ExprYield {
range,
node_index: _,
value: Some(value),
}) => {
self.yields.push(YieldEntry {
@ -708,7 +714,11 @@ impl<'a> Visitor<'a> for BodyVisitor<'a> {
is_none_yield: value.is_none_literal_expr(),
});
}
Expr::Yield(ast::ExprYield { range, value: None }) => {
Expr::Yield(ast::ExprYield {
range,
node_index: _,
value: None,
}) => {
self.yields.push(YieldEntry {
range: *range,
is_none_yield: true,

View file

@ -92,6 +92,7 @@ pub(crate) fn call(checker: &Checker, string: &str, range: TextRange) {
pub(crate) fn percent(checker: &Checker, expr: &Expr, format_string: &ExprStringLiteral) {
for StringLiteral {
value: _,
node_index: _,
range,
flags,
} in &format_string.value

View file

@ -219,6 +219,7 @@ pub(crate) fn bad_string_format_type(
let mut format_strings = vec![];
for StringLiteral {
value: _,
node_index: _,
range,
flags,
} in &format_string.value
@ -236,7 +237,11 @@ pub(crate) fn bad_string_format_type(
// Parse the parameters.
let is_valid = match &*bin_op.right {
Expr::Tuple(ast::ExprTuple { elts, .. }) => is_valid_tuple(&format_strings, elts),
Expr::Dict(ast::ExprDict { items, range: _ }) => is_valid_dict(&format_strings, items),
Expr::Dict(ast::ExprDict {
items,
range: _,
node_index: _,
}) => is_valid_dict(&format_strings, items),
_ => is_valid_constant(&format_strings, &bin_op.right),
};
if !is_valid {

View file

@ -87,6 +87,7 @@ pub(crate) fn if_stmt_min_max(checker: &Checker, stmt_if: &ast::StmtIf) {
body,
elif_else_clauses,
range: _,
node_index: _,
} = stmt_if;
if !elif_else_clauses.is_empty() {

View file

@ -72,9 +72,11 @@ pub(crate) fn manual_from_import(checker: &Checker, stmt: &Stmt, alias: &Alias,
name: asname.clone(),
asname: None,
range: TextRange::default(),
node_index: ruff_python_ast::AtomicNodeIndex::dummy(),
}],
level: 0,
range: TextRange::default(),
node_index: ruff_python_ast::AtomicNodeIndex::dummy(),
};
diagnostic.set_fix(Fix::safe_edit(Edit::range_replacement(
checker.generator().stmt(&node.into()),

View file

@ -112,8 +112,10 @@ fn collect_nested_args(min_max: MinMax, args: &[Expr], semantic: &SemanticModel)
args,
keywords,
range: _,
node_index: _,
},
range: _,
node_index: _,
}) = arg
{
if MinMax::try_from_call(func, keywords, semantic) == Some(min_max) {
@ -123,6 +125,7 @@ fn collect_nested_args(min_max: MinMax, args: &[Expr], semantic: &SemanticModel)
value: Box::new(arg.clone()),
ctx: ast::ExprContext::Load,
range: TextRange::default(),
node_index: ruff_python_ast::AtomicNodeIndex::dummy(),
});
new_args.push(new_arg);
continue;
@ -181,8 +184,10 @@ pub(crate) fn nested_min_max(
args: collect_nested_args(min_max, args, checker.semantic()).into_boxed_slice(),
keywords: Box::from(keywords),
range: TextRange::default(),
node_index: ruff_python_ast::AtomicNodeIndex::dummy(),
},
range: TextRange::default(),
node_index: ruff_python_ast::AtomicNodeIndex::dummy(),
});
diagnostic.set_fix(Fix::unsafe_edit(Edit::range_replacement(
checker.generator().expr(&flattened_expr),

View file

@ -280,11 +280,13 @@ fn assignment_targets_from_expr<'a>(
ctx: ExprContext::Store,
value,
range: _,
node_index: _,
}) => Box::new(iter::once(value.as_ref())),
Expr::Name(ast::ExprName {
ctx: ExprContext::Store,
id,
range: _,
node_index: _,
}) => {
// Ignore dummy variables.
if dummy_variable_rgx.is_match(id) {
@ -297,6 +299,7 @@ fn assignment_targets_from_expr<'a>(
ctx: ExprContext::Store,
elts,
range: _,
node_index: _,
}) => Box::new(
elts.iter()
.flat_map(|elt| assignment_targets_from_expr(elt, dummy_variable_rgx)),
@ -305,6 +308,7 @@ fn assignment_targets_from_expr<'a>(
ctx: ExprContext::Store,
elts,
range: _,
node_index: _,
parenthesized: _,
}) => Box::new(
elts.iter()

View file

@ -167,7 +167,11 @@ fn slots_attributes(expr: &Expr) -> impl Iterator<Item = Slot> {
Expr::Tuple(ast::ExprTuple { elts, .. })
| Expr::List(ast::ExprList { elts, .. })
| Expr::Set(ast::ExprSet { elts, .. }) => Some(elts.iter().filter_map(|elt| match elt {
Expr::StringLiteral(ast::ExprStringLiteral { value, range }) => Some(Slot {
Expr::StringLiteral(ast::ExprStringLiteral {
value,
range,
node_index: _,
}) => Some(Slot {
name: value.to_str(),
range: *range,
}),
@ -183,12 +187,14 @@ fn slots_attributes(expr: &Expr) -> impl Iterator<Item = Slot> {
.unwrap()
.iter_keys()
.filter_map(|key| match key {
Some(Expr::StringLiteral(ast::ExprStringLiteral { value, range })) => {
Some(Slot {
name: value.to_str(),
range: *range,
})
}
Some(Expr::StringLiteral(ast::ExprStringLiteral {
value,
range,
node_index: _,
})) => Some(Slot {
name: value.to_str(),
range: *range,
}),
_ => None,
}),
),

View file

@ -5,7 +5,7 @@ use ast::ExprContext;
use ruff_macros::{ViolationMetadata, derive_message_formats};
use ruff_python_ast::comparable::ComparableExpr;
use ruff_python_ast::helpers::{any_over_expr, contains_effect};
use ruff_python_ast::{self as ast, BoolOp, CmpOp, Expr};
use ruff_python_ast::{self as ast, AtomicNodeIndex, BoolOp, CmpOp, Expr};
use ruff_python_semantic::SemanticModel;
use ruff_text_size::{Ranged, TextRange};
@ -164,11 +164,13 @@ pub(crate) fn repeated_equality_comparison(checker: &Checker, bool_op: &ast::Exp
Expr::Set(ast::ExprSet {
elts: comparators.iter().copied().cloned().collect(),
range: TextRange::default(),
node_index: AtomicNodeIndex::dummy(),
})
} else {
Expr::Tuple(ast::ExprTuple {
elts: comparators.iter().copied().cloned().collect(),
range: TextRange::default(),
node_index: AtomicNodeIndex::dummy(),
ctx: ExprContext::Load,
parenthesized: true,
})
@ -186,10 +188,12 @@ pub(crate) fn repeated_equality_comparison(checker: &Checker, bool_op: &ast::Exp
},
comparators: Box::from([comparator]),
range: bool_op.range(),
node_index: AtomicNodeIndex::dummy(),
})))
.chain(after)
.collect(),
range: bool_op.range(),
node_index: AtomicNodeIndex::dummy(),
})),
bool_op.range(),
)));

View file

@ -46,7 +46,12 @@ impl Violation for ReturnInInit {
/// PLE0101
pub(crate) fn return_in_init(checker: &Checker, stmt: &Stmt) {
if let Stmt::Return(ast::StmtReturn { value, range: _ }) = stmt {
if let Stmt::Return(ast::StmtReturn {
value,
range: _,
node_index: _,
}) = stmt
{
if let Some(expr) = value {
if expr.is_none_literal_expr() {
// Explicit `return None`.

View file

@ -63,6 +63,7 @@ pub(crate) fn unnecessary_lambda(checker: &Checker, lambda: &ExprLambda) {
parameters,
body,
range: _,
node_index: _,
} = lambda;
// The lambda should consist of a single function call.

View file

@ -173,6 +173,7 @@ fn generate_keyword_fix(checker: &Checker, call: &ast::ExprCall) -> Fix {
value: Box::from("utf-8"),
flags: checker.default_string_flags(),
range: TextRange::default(),
node_index: ruff_python_ast::AtomicNodeIndex::dummy(),
}))
),
&call.arguments,

View file

@ -61,7 +61,12 @@ pub(crate) fn useless_return(
};
// Verify that the last statement is a return statement.
let Stmt::Return(ast::StmtReturn { value, range: _ }) = &last_stmt else {
let Stmt::Return(ast::StmtReturn {
value,
range: _,
node_index: _,
}) = &last_stmt
else {
return;
};
@ -72,7 +77,12 @@ pub(crate) fn useless_return(
// Skip functions that consist of a docstring and a return statement.
if body.len() == 2 {
if let Stmt::Expr(ast::StmtExpr { value, range: _ }) = &body[0] {
if let Stmt::Expr(ast::StmtExpr {
value,
range: _,
node_index: _,
}) = &body[0]
{
if value.is_string_literal_expr() {
return;
}

View file

@ -87,6 +87,7 @@ pub(crate) fn convert_named_tuple_functional_to_class(
// Ex) `NamedTuple("MyType")`
([_typename], []) => vec![Stmt::Pass(ast::StmtPass {
range: TextRange::default(),
node_index: ruff_python_ast::AtomicNodeIndex::dummy(),
})],
// Ex) `NamedTuple("MyType", [("a", int), ("b", str)])`
([_typename, fields], []) => {
@ -145,6 +146,7 @@ fn match_named_tuple_assign<'a>(
func,
arguments: Arguments { args, keywords, .. },
range: _,
node_index: _,
}) = value
else {
return None;
@ -163,6 +165,7 @@ fn create_field_assignment_stmt(field: Name, annotation: &Expr) -> Stmt {
id: field,
ctx: ExprContext::Load,
range: TextRange::default(),
node_index: ruff_python_ast::AtomicNodeIndex::dummy(),
}
.into(),
),
@ -170,6 +173,7 @@ fn create_field_assignment_stmt(field: Name, annotation: &Expr) -> Stmt {
value: None,
simple: true,
range: TextRange::default(),
node_index: ruff_python_ast::AtomicNodeIndex::dummy(),
}
.into()
}
@ -180,6 +184,7 @@ fn create_fields_from_fields_arg(fields: &Expr) -> Option<Vec<Stmt>> {
if fields.is_empty() {
let node = Stmt::Pass(ast::StmtPass {
range: TextRange::default(),
node_index: ruff_python_ast::AtomicNodeIndex::dummy(),
});
Some(vec![node])
} else {
@ -231,11 +236,13 @@ fn create_class_def_stmt(typename: &str, body: Vec<Stmt>, base_class: &Expr) ->
args: Box::from([base_class.clone()]),
keywords: Box::from([]),
range: TextRange::default(),
node_index: ruff_python_ast::AtomicNodeIndex::dummy(),
})),
body,
type_params: None,
decorator_list: vec![],
range: TextRange::default(),
node_index: ruff_python_ast::AtomicNodeIndex::dummy(),
}
.into()
}

View file

@ -131,6 +131,7 @@ fn match_typed_dict_assign<'a>(
func,
arguments,
range: _,
node_index: _,
}) = value
else {
return None;
@ -149,6 +150,7 @@ fn create_field_assignment_stmt(field: &str, annotation: &Expr) -> Stmt {
id: field.into(),
ctx: ExprContext::Load,
range: TextRange::default(),
node_index: ruff_python_ast::AtomicNodeIndex::dummy(),
}
.into(),
),
@ -156,6 +158,7 @@ fn create_field_assignment_stmt(field: &str, annotation: &Expr) -> Stmt {
value: None,
simple: true,
range: TextRange::default(),
node_index: ruff_python_ast::AtomicNodeIndex::dummy(),
}
.into()
}
@ -176,11 +179,13 @@ fn create_class_def_stmt(
None => Box::from([]),
},
range: TextRange::default(),
node_index: ruff_python_ast::AtomicNodeIndex::dummy(),
})),
body,
type_params: None,
decorator_list: vec![],
range: TextRange::default(),
node_index: ruff_python_ast::AtomicNodeIndex::dummy(),
}
.into()
}
@ -189,6 +194,7 @@ fn fields_from_dict_literal(items: &[ast::DictItem]) -> Option<Vec<Stmt>> {
if items.is_empty() {
let node = Stmt::Pass(ast::StmtPass {
range: TextRange::default(),
node_index: ruff_python_ast::AtomicNodeIndex::dummy(),
});
Some(vec![node])
} else {
@ -222,6 +228,7 @@ fn fields_from_dict_call(func: &Expr, keywords: &[Keyword]) -> Option<Vec<Stmt>>
if keywords.is_empty() {
let node = Stmt::Pass(ast::StmtPass {
range: TextRange::default(),
node_index: ruff_python_ast::AtomicNodeIndex::dummy(),
});
Some(vec![node])
} else {
@ -234,6 +241,7 @@ fn fields_from_keywords(keywords: &[Keyword]) -> Option<Vec<Stmt>> {
if keywords.is_empty() {
let node = Stmt::Pass(ast::StmtPass {
range: TextRange::default(),
node_index: ruff_python_ast::AtomicNodeIndex::dummy(),
});
return Some(vec![node]);
}
@ -256,13 +264,16 @@ fn match_fields_and_total(arguments: &Arguments) -> Option<(Vec<Stmt>, Option<&K
([_typename, fields], [..]) => {
let total = arguments.find_keyword("total");
match fields {
Expr::Dict(ast::ExprDict { items, range: _ }) => {
Some((fields_from_dict_literal(items)?, total))
}
Expr::Dict(ast::ExprDict {
items,
range: _,
node_index: _,
}) => Some((fields_from_dict_literal(items)?, total)),
Expr::Call(ast::ExprCall {
func,
arguments: Arguments { keywords, .. },
range: _,
node_index: _,
}) => Some((fields_from_dict_call(func, keywords)?, total)),
_ => None,
}
@ -271,6 +282,7 @@ fn match_fields_and_total(arguments: &Arguments) -> Option<(Vec<Stmt>, Option<&K
([_typename], []) => {
let node = Stmt::Pass(ast::StmtPass {
range: TextRange::default(),
node_index: ruff_python_ast::AtomicNodeIndex::dummy(),
});
Some((vec![node], None))
}

View file

@ -53,7 +53,11 @@ where
/// UP023
pub(crate) fn deprecated_c_element_tree(checker: &Checker, stmt: &Stmt) {
match stmt {
Stmt::Import(ast::StmtImport { names, range: _ }) => {
Stmt::Import(ast::StmtImport {
names,
range: _,
node_index: _,
}) => {
// Ex) `import xml.etree.cElementTree as ET`
for name in names {
if &name.name == "xml.etree.cElementTree" && name.asname.is_some() {
@ -66,6 +70,7 @@ pub(crate) fn deprecated_c_element_tree(checker: &Checker, stmt: &Stmt) {
names,
level,
range: _,
node_index: _,
}) => {
if *level > 0 {
// Ex) `import .xml.etree.cElementTree as ET`

View file

@ -274,7 +274,11 @@ pub(crate) fn deprecated_mock_attribute(checker: &Checker, attribute: &ast::Expr
/// UP026
pub(crate) fn deprecated_mock_import(checker: &Checker, stmt: &Stmt) {
match stmt {
Stmt::Import(ast::StmtImport { names, range: _ }) => {
Stmt::Import(ast::StmtImport {
names,
range: _,
node_index: _,
}) => {
// Find all `mock` imports.
if names
.iter()

View file

@ -85,6 +85,7 @@ impl<'a> FormatSummaryValues<'a> {
arg,
value,
range: _,
node_index: _,
} = keyword;
let key = arg.as_ref()?;
if contains_quotes(locator.slice(value)) || locator.contains_line_break(value.range()) {

View file

@ -64,8 +64,10 @@ pub(crate) fn lru_cache_with_maxsize_none(checker: &Checker, decorator_list: &[D
args,
keywords,
range: _,
node_index: _,
},
range: _,
node_index: _,
}) = &decorator.expression
else {
continue;
@ -85,6 +87,7 @@ pub(crate) fn lru_cache_with_maxsize_none(checker: &Checker, decorator_list: &[D
arg,
value,
range: _,
node_index: _,
} = &keywords[0];
if arg.as_ref().is_some_and(|arg| arg == "maxsize") && value.is_none_literal_expr() {
let mut diagnostic = checker.report_diagnostic(

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