Migrate ruff_linter, only ~660 errors remaining

This commit is contained in:
Micha Reiser 2024-08-01 16:42:19 +02:00
parent ab9b01b0c4
commit 56edbef069
No known key found for this signature in database
132 changed files with 1015 additions and 666 deletions

2
Cargo.lock generated
View File

@ -2301,6 +2301,7 @@ dependencies = [
"pyproject-toml",
"quick-junit",
"regex",
"ruff_allocator",
"ruff_cache",
"ruff_diagnostics",
"ruff_macros",
@ -2400,6 +2401,7 @@ name = "ruff_python_codegen"
version = "0.0.0"
dependencies = [
"once_cell",
"ruff_allocator",
"ruff_python_ast",
"ruff_python_literal",
"ruff_python_parser",

View File

@ -21,11 +21,11 @@ impl<'allocator, T> Box<'allocator, T> {
}
}
impl<'allocator, T> CloneIn<'allocator> for Box<'allocator, T>
impl<'ast, T> CloneIn<'ast> for Box<'ast, T>
where
T: CloneIn<'allocator>,
T: CloneIn<'ast>,
{
fn clone_in(&self, allocator: &'allocator Allocator) -> Self {
fn clone_in(&self, allocator: &'ast Allocator) -> Self {
Self(bumpalo::boxed::Box::new_in(
self.0.as_ref().clone_in(allocator),
allocator,
@ -122,16 +122,16 @@ impl<'a, I: ExactSizeIterator + ?Sized> ExactSizeIterator for Box<'a, I> {
impl<'a, I: FusedIterator + ?Sized> FusedIterator for Box<'a, I> {}
pub trait CloneIn<'allocator> {
pub trait CloneIn<'a> {
#[must_use]
fn clone_in(&self, allocator: &'allocator Allocator) -> Self;
fn clone_in(&self, allocator: &'a Allocator) -> Self;
}
impl<T> CloneIn<'_> for T
where
T: Clone,
{
fn clone_in(&self, _: &Allocator) -> Self {
fn clone_in(&self, _: &'_ Allocator) -> Self {
self.clone()
}
}

View File

@ -13,6 +13,7 @@ license = { workspace = true }
[lib]
[dependencies]
ruff_allocator = { workspace = true }
ruff_cache = { workspace = true }
ruff_diagnostics = { workspace = true, features = ["serde"] }
ruff_notebook = { workspace = true }

View File

@ -6,13 +6,13 @@ use ruff_python_semantic::{ScopeId, Snapshot};
/// visited.
#[derive(Debug, Default)]
pub(crate) struct Visit<'a> {
pub(crate) string_type_definitions: Vec<(&'a ExprStringLiteral, Snapshot)>,
pub(crate) future_type_definitions: Vec<(&'a Expr, Snapshot)>,
pub(crate) type_param_definitions: Vec<(&'a Expr, Snapshot)>,
pub(crate) string_type_definitions: Vec<(&'a ExprStringLiteral<'a>, Snapshot)>,
pub(crate) future_type_definitions: Vec<(&'a Expr<'a>, Snapshot)>,
pub(crate) type_param_definitions: Vec<(&'a Expr<'a>, Snapshot)>,
pub(crate) functions: Vec<Snapshot>,
pub(crate) lambdas: Vec<Snapshot>,
/// N.B. This field should always be empty unless it's a stub file
pub(crate) class_bases: Vec<(&'a Expr, Snapshot)>,
pub(crate) class_bases: Vec<(&'a Expr<'a>, Snapshot)>,
}
impl Visit<'_> {

View File

@ -30,7 +30,7 @@ use std::path::Path;
use itertools::Itertools;
use log::debug;
use ruff_allocator::Allocator;
use ruff_diagnostics::{Diagnostic, IsolationLevel};
use ruff_notebook::{CellOffsets, NotebookIndex};
use ruff_python_ast::helpers::{collect_import_from_member, is_docstring_stmt, to_module_path};
@ -175,10 +175,12 @@ impl ExpectedDocstringKind {
}
pub(crate) struct Checker<'a> {
allocator: &'a Allocator,
/// The [`Parsed`] output for the source code.
parsed: &'a Parsed<ModModule>,
parsed: &'a Parsed<ModModule<'a>>,
/// The [`Parsed`] output for the type annotation the checker is currently in.
parsed_type_annotation: Option<&'a Parsed<ModExpression>>,
parsed_type_annotation: Option<&'a Parsed<ModExpression<'a>>>,
/// The [`Path`] to the file under analysis.
path: &'a Path,
/// The [`Path`] to the package containing the current file.
@ -208,7 +210,7 @@ pub(crate) struct Checker<'a> {
/// The [`Indexer`] for the current file, which contains the offsets of all comments and more.
indexer: &'a Indexer,
/// The [`Importer`] for the current file, which enables importing of other modules.
importer: Importer<'a>,
importer: Importer<'a, 'a>,
/// The [`SemanticModel`], built up over the course of the AST traversal.
semantic: SemanticModel<'a>,
/// A set of deferred nodes to be visited after the current traversal (e.g., function bodies).
@ -228,7 +230,8 @@ pub(crate) struct Checker<'a> {
impl<'a> Checker<'a> {
#[allow(clippy::too_many_arguments)]
pub(crate) fn new(
parsed: &'a Parsed<ModModule>,
allocator: &'a Allocator,
parsed: &'a Parsed<ModModule<'a>>,
settings: &'a LinterSettings,
noqa_line_for: &'a NoqaMapping,
noqa: flags::Noqa,
@ -243,6 +246,7 @@ impl<'a> Checker<'a> {
notebook_index: Option<&'a NotebookIndex>,
) -> Checker<'a> {
Checker {
allocator,
parsed,
parsed_type_annotation: None,
settings,
@ -294,6 +298,10 @@ impl<'a> Checker<'a> {
)
}
pub(crate) fn allocator(&self) -> &'a Allocator {
self.allocator
}
/// Returns the appropriate quoting for f-string by reversing the one used outside of
/// the f-string.
///
@ -358,7 +366,7 @@ impl<'a> Checker<'a> {
}
/// The [`Importer`] for the current file, which enables importing of other modules.
pub(crate) const fn importer(&self) -> &Importer<'a> {
pub(crate) const fn importer(&self) -> &Importer<'a, 'a> {
&self.importer
}
@ -407,8 +415,8 @@ impl<'a> Checker<'a> {
}
}
impl<'a> Visitor<'a> for Checker<'a> {
fn visit_stmt(&mut self, stmt: &'a Stmt) {
impl<'a> Visitor<'a, 'a> for Checker<'a> {
fn visit_stmt(&mut self, stmt: &'a Stmt<'a>) {
// Step 0: Pre-processing
self.semantic.push_node(stmt);
@ -997,14 +1005,14 @@ impl<'a> Visitor<'a> for Checker<'a> {
self.last_stmt_end = stmt.end();
}
fn visit_annotation(&mut self, expr: &'a Expr) {
fn visit_annotation(&mut self, expr: &'a Expr<'a>) {
let flags_snapshot = self.semantic.flags;
self.semantic.flags |= SemanticModelFlags::TYPING_ONLY_ANNOTATION;
self.visit_type_definition(expr);
self.semantic.flags = flags_snapshot;
}
fn visit_expr(&mut self, expr: &'a Expr) {
fn visit_expr(&mut self, expr: &'a Expr<'a>) {
// Step 0: Pre-processing
if self.source_type.is_stub()
&& self.semantic.in_class_base()
@ -1064,7 +1072,7 @@ impl<'a> Visitor<'a> for Checker<'a> {
range: _,
}) => {
if let Expr::Name(ast::ExprName { id, ctx, range: _ }) = func.as_ref() {
if id == "locals" && ctx.is_load() {
if *id == "locals" && ctx.is_load() {
let scope = self.semantic.current_scope_mut();
scope.set_uses_locals();
}
@ -1497,7 +1505,7 @@ impl<'a> Visitor<'a> for Checker<'a> {
self.semantic.pop_node();
}
fn visit_except_handler(&mut self, except_handler: &'a ExceptHandler) {
fn visit_except_handler(&mut self, except_handler: &'a ExceptHandler<'a>) {
let flags_snapshot = self.semantic.flags;
self.semantic.flags |= SemanticModelFlags::EXCEPTION_HANDLER;
@ -1559,7 +1567,7 @@ impl<'a> Visitor<'a> for Checker<'a> {
analyze::parameters(parameters, self);
}
fn visit_parameter(&mut self, parameter: &'a Parameter) {
fn visit_parameter(&mut self, parameter: &'a Parameter<'a>) {
// Step 1: Binding.
// Bind, but intentionally avoid walking the annotation, as we handle it
// upstream.
@ -1574,7 +1582,7 @@ impl<'a> Visitor<'a> for Checker<'a> {
analyze::parameter(parameter, self);
}
fn visit_pattern(&mut self, pattern: &'a Pattern) {
fn visit_pattern(&mut self, pattern: &'a Pattern<'a>) {
// Step 1: Binding
if let Pattern::MatchAs(ast::PatternMatchAs {
name: Some(name), ..
@ -1599,7 +1607,7 @@ impl<'a> Visitor<'a> for Checker<'a> {
walk_pattern(self, pattern);
}
fn visit_body(&mut self, body: &'a [Stmt]) {
fn visit_body(&mut self, body: &'a [Stmt<'a>]) {
// Step 4: Analysis
analyze::suite(body, self);
@ -1609,7 +1617,7 @@ impl<'a> Visitor<'a> for Checker<'a> {
}
}
fn visit_match_case(&mut self, match_case: &'a MatchCase) {
fn visit_match_case(&mut self, match_case: &'a MatchCase<'a>) {
self.visit_pattern(&match_case.pattern);
if let Some(expr) = &match_case.guard {
self.visit_boolean_test(expr);
@ -1620,7 +1628,7 @@ impl<'a> Visitor<'a> for Checker<'a> {
self.semantic.pop_branch();
}
fn visit_type_param(&mut self, type_param: &'a ast::TypeParam) {
fn visit_type_param(&mut self, type_param: &'a ast::TypeParam<'a>) {
// Step 1: Binding
match type_param {
ast::TypeParam::TypeVar(ast::TypeParamTypeVar { name, range, .. })
@ -1678,7 +1686,7 @@ impl<'a> Visitor<'a> for Checker<'a> {
}
}
fn visit_f_string_element(&mut self, f_string_element: &'a FStringElement) {
fn visit_f_string_element(&mut self, f_string_element: &'a FStringElement<'a>) {
let snapshot = self.semantic.flags;
if f_string_element.is_expression() {
self.semantic.flags |= SemanticModelFlags::F_STRING_REPLACEMENT_FIELD;
@ -1690,13 +1698,13 @@ impl<'a> Visitor<'a> for Checker<'a> {
impl<'a> Checker<'a> {
/// Visit a [`Module`]. Returns `true` if the module contains a module-level docstring.
fn visit_module(&mut self, python_ast: &'a Suite) {
fn visit_module(&mut self, python_ast: &'a Suite<'a>) {
analyze::module(python_ast, self);
}
/// Visit a list of [`Comprehension`] nodes, assumed to be the comprehensions that compose a
/// generator expression, like a list or set comprehension.
fn visit_generators(&mut self, generators: &'a [Comprehension]) {
fn visit_generators(&mut self, generators: &'a [Comprehension<'a>]) {
let mut iterator = generators.iter();
let Some(generator) = iterator.next() else {
@ -1762,7 +1770,7 @@ impl<'a> Checker<'a> {
}
/// Visit an body of [`Stmt`] nodes within a type-checking block.
fn visit_type_checking_block(&mut self, body: &'a [Stmt]) {
fn visit_type_checking_block(&mut self, body: &'a [Stmt<'a>]) {
let snapshot = self.semantic.flags;
self.semantic.flags |= SemanticModelFlags::TYPE_CHECKING_BLOCK;
self.visit_body(body);
@ -1770,7 +1778,7 @@ impl<'a> Checker<'a> {
}
/// Visit an [`Expr`], and treat it as a runtime-evaluated type annotation.
fn visit_runtime_evaluated_annotation(&mut self, expr: &'a Expr) {
fn visit_runtime_evaluated_annotation(&mut self, expr: &'a Expr<'a>) {
let snapshot = self.semantic.flags;
self.semantic.flags |= SemanticModelFlags::RUNTIME_EVALUATED_ANNOTATION;
self.visit_type_definition(expr);
@ -1778,7 +1786,7 @@ impl<'a> Checker<'a> {
}
/// Visit an [`Expr`], and treat it as a runtime-required type annotation.
fn visit_runtime_required_annotation(&mut self, expr: &'a Expr) {
fn visit_runtime_required_annotation(&mut self, expr: &'a Expr<'a>) {
let snapshot = self.semantic.flags;
self.semantic.flags |= SemanticModelFlags::RUNTIME_REQUIRED_ANNOTATION;
self.visit_type_definition(expr);
@ -1786,7 +1794,7 @@ impl<'a> Checker<'a> {
}
/// Visit an [`Expr`], and treat it as a type definition.
fn visit_type_definition(&mut self, expr: &'a Expr) {
fn visit_type_definition(&mut self, expr: &'a Expr<'a>) {
let snapshot = self.semantic.flags;
self.semantic.flags |= SemanticModelFlags::TYPE_DEFINITION;
self.visit_expr(expr);
@ -1794,7 +1802,7 @@ impl<'a> Checker<'a> {
}
/// Visit an [`Expr`], and treat it as _not_ a type definition.
fn visit_non_type_definition(&mut self, expr: &'a Expr) {
fn visit_non_type_definition(&mut self, expr: &'a Expr<'a>) {
let snapshot = self.semantic.flags;
self.semantic.flags -= SemanticModelFlags::TYPE_DEFINITION;
self.visit_expr(expr);
@ -1804,7 +1812,7 @@ impl<'a> Checker<'a> {
/// Visit an [`Expr`], and treat it as a boolean test. This is useful for detecting whether an
/// expressions return value is significant, or whether the calling context only relies on
/// its truthiness.
fn visit_boolean_test(&mut self, expr: &'a Expr) {
fn visit_boolean_test(&mut self, expr: &'a Expr<'a>) {
let snapshot = self.semantic.flags;
self.semantic.flags |= SemanticModelFlags::BOOLEAN_TEST;
self.visit_expr(expr);
@ -1812,7 +1820,7 @@ impl<'a> Checker<'a> {
}
/// Visit an [`ElifElseClause`]
fn visit_elif_else_clause(&mut self, clause: &'a ElifElseClause) {
fn visit_elif_else_clause(&mut self, clause: &'a ElifElseClause<'a>) {
if let Some(test) = &clause.test {
self.visit_boolean_test(test);
}
@ -1927,7 +1935,7 @@ impl<'a> Checker<'a> {
}
}
fn handle_node_load(&mut self, expr: &Expr) {
fn handle_node_load(&mut self, expr: &Expr<'a>) {
let Expr::Name(expr) = expr else {
return;
};
@ -1948,21 +1956,21 @@ impl<'a> Checker<'a> {
&& match parent {
Stmt::Assign(ast::StmtAssign { targets, .. }) => {
if let Some(Expr::Name(ast::ExprName { id, .. })) = targets.first() {
id == "__all__"
*id == "__all__"
} else {
false
}
}
Stmt::AugAssign(ast::StmtAugAssign { target, .. }) => {
if let Expr::Name(ast::ExprName { id, .. }) = target.as_ref() {
id == "__all__"
*id == "__all__"
} else {
false
}
}
Stmt::AnnAssign(ast::StmtAnnAssign { target, .. }) => {
if let Expr::Name(ast::ExprName { id, .. }) = target.as_ref() {
id == "__all__"
*id == "__all__"
} else {
false
}
@ -2036,7 +2044,7 @@ impl<'a> Checker<'a> {
self.add_binding(id, expr.range(), BindingKind::Assignment, flags);
}
fn handle_node_delete(&mut self, expr: &'a Expr) {
fn handle_node_delete(&mut self, expr: &'a Expr<'a>) {
let Expr::Name(ast::ExprName { id, .. }) = expr else {
return;
};
@ -2172,7 +2180,7 @@ impl<'a> Checker<'a> {
let type_definitions = std::mem::take(&mut self.visit.string_type_definitions);
for (string_expr, snapshot) in type_definitions {
if let Ok((parsed_annotation, kind)) =
parse_type_annotation(string_expr, self.locator.contents())
parse_type_annotation(string_expr, self.locator.contents(), self.allocator)
{
let parsed_annotation = allocator.alloc(parsed_annotation);
self.parsed_type_annotation = Some(parsed_annotation);
@ -2354,8 +2362,9 @@ impl<'a> Checker<'a> {
}
#[allow(clippy::too_many_arguments)]
pub(crate) fn check_ast(
parsed: &Parsed<ModModule>,
pub(crate) fn check_ast<'a>(
allocator: &'a Allocator,
parsed: &Parsed<ModModule<'a>>,
locator: &Locator,
stylist: &Stylist,
indexer: &Indexer,
@ -2389,6 +2398,7 @@ pub(crate) fn check_ast(
};
let mut checker = Checker::new(
allocator,
parsed,
settings,
noqa_line_for,

View File

@ -89,6 +89,7 @@ pub(crate) fn check_physical_lines(
#[cfg(test)]
mod tests {
use ruff_allocator::Allocator;
use ruff_python_codegen::Stylist;
use ruff_python_index::Indexer;
use ruff_python_parser::parse_module;
@ -105,7 +106,8 @@ mod tests {
fn e501_non_ascii_char() {
let line = "'\u{4e9c}' * 2"; // 7 in UTF-32, 9 in UTF-8.
let locator = Locator::new(line);
let parsed = parse_module(line).unwrap();
let allocator = Allocator::new();
let parsed = parse_module(line, &allocator).unwrap();
let indexer = Indexer::from_tokens(parsed.tokens(), &locator);
let stylist = Stylist::from_tokens(parsed.tokens(), &locator);

View File

@ -373,6 +373,7 @@ impl TodoDirectiveKind {
#[cfg(test)]
mod tests {
use ruff_allocator::Allocator;
use ruff_python_parser::parse_module;
use ruff_python_trivia::CommentRanges;
use ruff_text_size::{TextLen, TextRange, TextSize};
@ -388,7 +389,8 @@ mod tests {
use super::IsortDirectives;
fn noqa_mappings(contents: &str) -> NoqaMapping {
let parsed = parse_module(contents).unwrap();
let allocator = Allocator::new();
let parsed = parse_module(contents, &allocator).unwrap();
let locator = Locator::new(contents);
let indexer = Indexer::from_tokens(parsed.tokens(), &locator);
@ -563,7 +565,8 @@ assert foo, \
}
fn isort_directives(contents: &str) -> IsortDirectives {
let parsed = parse_module(contents).unwrap();
let allocator = Allocator::new();
let parsed = parse_module(contents, &allocator).unwrap();
let locator = Locator::new(contents);
let comment_ranges = CommentRanges::from(parsed.tokens());
extract_isort_directives(&locator, &comment_ranges)

View File

@ -4,7 +4,9 @@ use ruff_python_ast::{self as ast, Stmt};
use ruff_python_semantic::{Definition, DefinitionId, Definitions, Member, MemberKind};
/// Extract a docstring from a function or class body.
pub(crate) fn docstring_from(suite: &[Stmt]) -> Option<&ast::ExprStringLiteral> {
pub(crate) fn docstring_from<'a, 'ast>(
suite: &'a [Stmt<'ast>],
) -> Option<&'a ast::ExprStringLiteral<'ast>> {
let stmt = suite.first()?;
// Require the docstring to be a standalone expression.
let Stmt::Expr(ast::StmtExpr { value, range: _ }) = stmt else {
@ -26,8 +28,8 @@ pub(crate) fn extract_docstring<'a>(
#[derive(Copy, Clone)]
pub(crate) enum ExtractionTarget<'a> {
Class(&'a ast::StmtClassDef),
Function(&'a ast::StmtFunctionDef),
Class(&'a ast::StmtClassDef<'a>),
Function(&'a ast::StmtFunctionDef<'a>),
}
/// Extract a `Definition` from the AST node defined by a `Stmt`.

View File

@ -15,7 +15,7 @@ pub(crate) mod styles;
pub(crate) struct Docstring<'a> {
pub(crate) definition: &'a Definition<'a>,
/// The literal AST node representing the docstring.
pub(crate) expr: &'a ExprStringLiteral,
pub(crate) expr: &'a ExprStringLiteral<'a>,
/// The content of the docstring, including the leading and trailing quotes.
pub(crate) contents: &'a str,
/// The range of the docstring body (without the quotes). The range is relative to [`Self::contents`].

View File

@ -234,7 +234,7 @@ impl<'a> SectionContexts<'a> {
self.contexts.len()
}
pub(crate) fn iter(&self) -> SectionContextsIter {
pub(crate) fn iter(&self) -> SectionContextsIter<'_, 'a> {
SectionContextsIter {
docstring_body: self.docstring.body(),
inner: self.contexts.iter(),
@ -242,9 +242,9 @@ impl<'a> SectionContexts<'a> {
}
}
impl<'a> IntoIterator for &'a SectionContexts<'a> {
impl<'sections, 'a> IntoIterator for &'sections SectionContexts<'a> {
type Item = SectionContext<'a>;
type IntoIter = SectionContextsIter<'a>;
type IntoIter = SectionContextsIter<'sections, 'a>;
fn into_iter(self) -> Self::IntoIter {
self.iter()
@ -257,12 +257,12 @@ impl Debug for SectionContexts<'_> {
}
}
pub(crate) struct SectionContextsIter<'a> {
pub(crate) struct SectionContextsIter<'sections, 'a> {
docstring_body: DocstringBody<'a>,
inner: std::slice::Iter<'a, SectionContextData>,
inner: std::slice::Iter<'sections, SectionContextData>,
}
impl<'a> Iterator for SectionContextsIter<'a> {
impl<'a> Iterator for SectionContextsIter<'_, 'a> {
type Item = SectionContext<'a>;
fn next(&mut self) -> Option<Self::Item> {
@ -279,7 +279,7 @@ impl<'a> Iterator for SectionContextsIter<'a> {
}
}
impl<'a> DoubleEndedIterator for SectionContextsIter<'a> {
impl DoubleEndedIterator for SectionContextsIter<'_, '_> {
fn next_back(&mut self) -> Option<Self::Item> {
let back = self.inner.next_back()?;
Some(SectionContext {
@ -289,8 +289,8 @@ impl<'a> DoubleEndedIterator for SectionContextsIter<'a> {
}
}
impl FusedIterator for SectionContextsIter<'_> {}
impl ExactSizeIterator for SectionContextsIter<'_> {}
impl FusedIterator for SectionContextsIter<'_, '_> {}
impl ExactSizeIterator for SectionContextsIter<'_, '_> {}
#[derive(Debug)]
struct SectionContextData {

View File

@ -26,9 +26,9 @@ use crate::importer::insertion::Insertion;
mod insertion;
pub(crate) struct Importer<'a> {
pub(crate) struct Importer<'a, 'ast> {
/// The Python AST to which we are adding imports.
python_ast: &'a [Stmt],
python_ast: &'a [Stmt<'ast>],
/// The tokens representing the Python AST.
tokens: &'a Tokens,
/// The [`Locator`] for the Python AST.
@ -36,14 +36,14 @@ pub(crate) struct Importer<'a> {
/// The [`Stylist`] for the Python AST.
stylist: &'a Stylist<'a>,
/// The list of visited, top-level runtime imports in the Python AST.
runtime_imports: Vec<&'a Stmt>,
runtime_imports: Vec<&'a Stmt<'ast>>,
/// The list of visited, top-level `if TYPE_CHECKING:` blocks in the Python AST.
type_checking_blocks: Vec<&'a Stmt>,
type_checking_blocks: Vec<&'a Stmt<'ast>>,
}
impl<'a> Importer<'a> {
impl<'a, 'ast> Importer<'a, 'ast> {
pub(crate) fn new(
parsed: &'a Parsed<ModModule>,
parsed: &'a Parsed<ModModule<'ast>>,
locator: &'a Locator<'a>,
stylist: &'a Stylist<'a>,
) -> Self {
@ -58,12 +58,12 @@ impl<'a> Importer<'a> {
}
/// Visit a top-level import statement.
pub(crate) fn visit_import(&mut self, import: &'a Stmt) {
pub(crate) fn visit_import(&mut self, import: &'a Stmt<'ast>) {
self.runtime_imports.push(import);
}
/// Visit a top-level type-checking block.
pub(crate) fn visit_type_checking_block(&mut self, type_checking_block: &'a Stmt) {
pub(crate) fn visit_type_checking_block(&mut self, type_checking_block: &'a Stmt<'ast>) {
self.type_checking_blocks.push(type_checking_block);
}
@ -468,7 +468,7 @@ impl<'a> Importer<'a> {
}
/// Return the import statement that precedes the given position, if any.
fn preceding_import(&self, at: TextSize) -> Option<&'a Stmt> {
fn preceding_import(&self, at: TextSize) -> Option<&'a Stmt<'a>> {
self.runtime_imports
.partition_point(|stmt| stmt.start() < at)
.checked_sub(1)
@ -476,7 +476,7 @@ impl<'a> Importer<'a> {
}
/// Return the `TYPE_CHECKING` block that precedes the given position, if any.
fn preceding_type_checking_block(&self, at: TextSize) -> Option<&'a Stmt> {
fn preceding_type_checking_block(&self, at: TextSize) -> Option<&'a Stmt<'a>> {
let block = self.type_checking_blocks.first()?;
if block.start() <= at {
Some(block)
@ -562,7 +562,7 @@ impl<'a> ImportRequest<'a> {
/// An existing list of module or member imports, located within an import statement.
pub(crate) struct ImportedMembers<'a> {
/// The import statement.
pub(crate) statement: &'a Stmt,
pub(crate) statement: &'a Stmt<'a>,
/// The "names" of the imported members.
pub(crate) names: Vec<&'a str>,
}

View File

@ -663,17 +663,22 @@ This indicates a bug in Ruff. If you could open an issue at:
}
#[derive(Debug, Clone)]
pub enum ParseSource {
pub enum ParseSource<'a> {
/// Parse the [`Parsed`] from the given source code.
None,
/// Use the precomputed [`Parsed`].
Precomputed(Parsed<ModModule>),
Precomputed(Parsed<ModModule<'a>>),
}
impl ParseSource {
impl<'a> ParseSource<'a> {
/// Consumes the [`ParseSource`] and returns the parsed [`Parsed`], parsing the source code if
/// necessary.
fn into_parsed(self, source_kind: &SourceKind, source_type: PySourceType) -> Parsed<ModModule> {
fn into_parsed(
self,
source_kind: &SourceKind,
source_type: PySourceType,
allocator: &'a ruff_allocator::Allocator,
) -> Parsed<ModModule<'a>> {
match self {
ParseSource::None => {
ruff_python_parser::parse_unchecked_source(source_kind.source_code(), source_type)

View File

@ -305,13 +305,13 @@ impl<'a> EmitterContext<'a> {
#[cfg(test)]
mod tests {
use rustc_hash::FxHashMap;
use ruff_allocator::Allocator;
use ruff_diagnostics::{Diagnostic, DiagnosticKind, Edit, Fix};
use ruff_notebook::NotebookIndex;
use ruff_python_parser::{parse_unchecked, Mode};
use ruff_source_file::{Locator, OneIndexed, SourceFileBuilder};
use ruff_text_size::{Ranged, TextRange, TextSize};
use rustc_hash::FxHashMap;
use crate::message::{Emitter, EmitterContext, Message};
@ -324,7 +324,8 @@ if call(foo
";
let locator = Locator::new(source);
let source_file = SourceFileBuilder::new("syntax_errors.py", source).finish();
parse_unchecked(source, Mode::Module)
let allocator = Allocator::new();
parse_unchecked(source, Mode::Module, &allocator)
.errors()
.iter()
.map(|parse_error| {

View File

@ -102,11 +102,11 @@ pub(crate) fn fastapi_redundant_response_model(
}
}
fn check_decorator<'a>(
fn check_decorator<'a, 'ast>(
function_def: &StmtFunctionDef,
decorator: &'a Decorator,
decorator: &'a Decorator<'ast>,
semantic: &'a SemanticModel,
) -> Option<(&'a ExprCall, &'a Keyword)> {
) -> Option<(&'a ExprCall<'ast>, &'a Keyword<'ast>)> {
let call = is_fastapi_route_decorator(decorator, semantic)?;
let response_model_arg = call.arguments.find_keyword("response_model")?;
let return_value = function_def.returns.as_ref()?;

View File

@ -17,10 +17,10 @@ pub(crate) fn is_fastapi_route(function_def: &StmtFunctionDef, semantic: &Semant
}
/// Returns `true` if the decorator is indicative of a FastAPI route.
pub(crate) fn is_fastapi_route_decorator<'a>(
decorator: &'a Decorator,
semantic: &'a SemanticModel,
) -> Option<&'a ExprCall> {
pub(crate) fn is_fastapi_route_decorator<'a, 'ast>(
decorator: &'a Decorator<'ast>,
semantic: &SemanticModel,
) -> Option<&'a ExprCall<'ast>> {
let call = decorator.expression.as_call_expr()?;
let decorator_method = call.func.as_attribute_expr()?;
let method_name = &decorator_method.attr;

View File

@ -210,25 +210,25 @@ impl AutoPythonType {
}
/// Given a [`PythonType`], return an [`Expr`] that resolves to that type.
fn type_expr(python_type: PythonType) -> Option<Expr> {
fn name(name: &str) -> Expr {
fn type_expr(python_type: PythonType, allocator: &ruff_allocator::Allocator) -> Option<Expr> {
fn name<'a>(name: &str, allocator: &'a ruff_allocator::Allocator) -> Expr<'a> {
Expr::Name(ast::ExprName {
id: name.into(),
id: allocator.alloc_str(name),
range: TextRange::default(),
ctx: ExprContext::Load,
})
}
match python_type {
PythonType::String => Some(name("str")),
PythonType::Bytes => Some(name("bytes")),
PythonType::String => Some(name("str", allocator)),
PythonType::Bytes => Some(name("bytes", allocator)),
PythonType::Number(number) => match number {
NumberLike::Integer => Some(name("int")),
NumberLike::Float => Some(name("float")),
NumberLike::Complex => Some(name("complex")),
NumberLike::Bool => Some(name("bool")),
NumberLike::Integer => Some(name("int", allocator)),
NumberLike::Float => Some(name("float", allocator)),
NumberLike::Complex => Some(name("complex", allocator)),
NumberLike::Bool => Some(name("bool", allocator)),
},
PythonType::None => Some(name("None")),
PythonType::None => Some(name("None", allocator)),
PythonType::Ellipsis => None,
PythonType::Dict => None,
PythonType::List => None,

View File

@ -8,7 +8,7 @@ static PASSWORD_CANDIDATE_REGEX: Lazy<Regex> = Lazy::new(|| {
Regex::new(r"(^|_)(?i)(pas+wo?r?d|pass(phrase)?|pwd|token|secrete?)($|_)").unwrap()
});
pub(super) fn string_literal(expr: &Expr) -> Option<&str> {
pub(super) fn string_literal<'a>(expr: &'a Expr) -> Option<&'a str> {
match expr {
Expr::StringLiteral(ast::ExprStringLiteral { value, .. }) => Some(value.to_str()),
_ => None,

View File

@ -49,7 +49,7 @@ impl Violation for HardcodedPasswordString {
}
}
fn password_target(target: &Expr) -> Option<&str> {
fn password_target<'a>(target: &'a Expr<'a>) -> Option<&'a str> {
let target_name = match target {
// variable = "s3cr3t"
Expr::Name(ast::ExprName { id, .. }) => id.as_str(),

View File

@ -464,7 +464,7 @@ enum Safety {
Unknown,
}
impl From<&Expr> for Safety {
impl From<&Expr<'_>> for Safety {
/// Return the [`Safety`] level for the [`Expr`]. This is based on Bandit's definition: string
/// literals are considered okay, but dynamically-computed values are not.
fn from(expr: &Expr) -> Self {

View File

@ -847,7 +847,7 @@ pub(crate) fn suspicious_function_call(checker: &mut Checker, call: &ExprCall) {
/// Return the leading characters for an expression, if it's a string literal, f-string, or
/// string concatenation.
fn leading_chars(expr: &Expr) -> Option<impl Iterator<Item = char> + Clone + '_> {
fn leading_chars<'a>(expr: &Expr<'a>) -> Option<impl Iterator<Item = char> + Clone + 'a> {
match expr {
// Ex) `"foo"`
Expr::StringLiteral(ast::ExprStringLiteral { value, .. }) => {

View File

@ -1,9 +1,10 @@
use ruff_python_ast::{self as ast, Arguments, Expr, ExprContext, Stmt};
use ruff_text_size::{Ranged, TextRange};
use ruff_allocator::CloneIn;
use ruff_diagnostics::{AlwaysFixableViolation, Diagnostic, Edit, Fix};
use ruff_macros::{derive_message_formats, violation};
use ruff_python_ast::helpers::is_const_false;
use ruff_python_ast::{self as ast, Arguments, Expr, ExprContext, Stmt};
use ruff_text_size::{Ranged, TextRange};
use std::alloc::alloc;
use crate::checkers::ast::Checker;
@ -48,22 +49,28 @@ impl AlwaysFixableViolation for AssertFalse {
}
}
fn assertion_error(msg: Option<&Expr>) -> Stmt {
fn assertion_error<'ast>(
msg: Option<&Expr<'ast>>,
allocator: &'ast ruff_allocator::Allocator,
) -> Stmt<'ast> {
Stmt::Raise(ast::StmtRaise {
range: TextRange::default(),
exc: Some(Box::new(Expr::Call(ast::ExprCall {
func: Box::new(Expr::Name(ast::ExprName {
id: "AssertionError".into(),
ctx: ExprContext::Load,
range: TextRange::default(),
})),
exc: Some(ruff_allocator::Box::new_in(Expr::Call(ast::ExprCall {
func: ruff_allocator::Box::new_in(
Expr::Name(ast::ExprName {
id: "AssertionError".into(),
ctx: ExprContext::Load,
range: TextRange::default(),
}),
allocator,
),
arguments: Arguments {
args: if let Some(msg) = msg {
Box::from([msg.clone()])
allocator.alloc_slice_fill_iter([msg.clone_in(allocator)])
} else {
Box::from([])
&mut []
},
keywords: Box::from([]),
keywords: &mut [],
range: TextRange::default(),
},
range: TextRange::default(),

View File

@ -1,12 +1,12 @@
use itertools::Itertools;
use rustc_hash::{FxHashMap, FxHashSet};
use ruff_allocator::{Allocator, CloneIn};
use ruff_diagnostics::{AlwaysFixableViolation, Violation};
use ruff_diagnostics::{Diagnostic, Edit, Fix};
use ruff_macros::{derive_message_formats, violation};
use ruff_python_ast::name::UnqualifiedName;
use ruff_python_ast::{self as ast, ExceptHandler, Expr, ExprContext};
use ruff_text_size::{Ranged, TextRange};
use rustc_hash::{FxHashMap, FxHashSet};
use crate::checkers::ast::Checker;
use crate::fix::edits::pad;
@ -103,9 +103,12 @@ impl AlwaysFixableViolation for DuplicateHandlerException {
}
}
fn type_pattern(elts: Vec<&Expr>) -> Expr {
fn type_pattern<'ast>(elts: Vec<&Expr>, allocator: &'ast Allocator) -> Expr<'ast> {
ast::ExprTuple {
elts: elts.into_iter().cloned().collect(),
elts: elts
.into_iter()
.map(|elt| elt.clone_in(allocator))
.collect(),
ctx: ExprContext::Load,
range: TextRange::default(),
parenthesized: true,
@ -116,9 +119,9 @@ fn type_pattern(elts: Vec<&Expr>) -> Expr {
/// B014
fn duplicate_handler_exceptions<'a>(
checker: &mut Checker,
expr: &'a Expr,
elts: &'a [Expr],
) -> FxHashMap<UnqualifiedName<'a>, &'a Expr> {
expr: &'a Expr<'a>,
elts: &'a [Expr<'a>],
) -> FxHashMap<UnqualifiedName<'a>, &'a Expr<'a>> {
let mut seen: FxHashMap<UnqualifiedName, &Expr> = FxHashMap::default();
let mut duplicates: FxHashSet<UnqualifiedName> = FxHashSet::default();
let mut unique_elts: Vec<&Expr> = Vec::default();

View File

@ -71,7 +71,7 @@ pub(crate) fn except_with_non_exception_classes(
///
/// This should leave any unstarred iterables alone (subsequently raising a
/// warning for B029).
fn flatten_iterables(expr: &Expr) -> Vec<&Expr> {
fn flatten_iterables<'a>(expr: &'a Expr<'a>) -> Vec<&'a Expr<'a>> {
// Unpack the top-level Tuple into queue, otherwise add as-is.
let mut exprs_to_process: VecDeque<&Expr> = match expr {
Expr::Tuple(ast::ExprTuple { elts, .. }) => elts.iter().collect(),

View File

@ -98,7 +98,7 @@ impl<'a, 'b> ArgumentDefaultVisitor<'a, 'b> {
}
}
impl Visitor<'_> for ArgumentDefaultVisitor<'_, '_> {
impl Visitor<'_, '_> for ArgumentDefaultVisitor<'_, '_> {
fn visit_expr(&mut self, expr: &Expr) {
match expr {
Expr::Call(ast::ExprCall { func, .. }) => {

View File

@ -56,13 +56,13 @@ impl Violation for FunctionUsesLoopVariable {
#[derive(Default)]
struct LoadedNamesVisitor<'a> {
loaded: Vec<&'a ast::ExprName>,
stored: Vec<&'a ast::ExprName>,
loaded: Vec<&'a ast::ExprName<'a>>,
stored: Vec<&'a ast::ExprName<'a>>,
}
/// `Visitor` to collect all used identifiers in a statement.
impl<'a> Visitor<'a> for LoadedNamesVisitor<'a> {
fn visit_expr(&mut self, expr: &'a Expr) {
impl<'a> Visitor<'a, 'a> for LoadedNamesVisitor<'a> {
fn visit_expr(&mut self, expr: &'a Expr<'a>) {
match expr {
Expr::Name(name) => match &name.ctx {
ExprContext::Load => self.loaded.push(name),
@ -76,14 +76,14 @@ impl<'a> Visitor<'a> for LoadedNamesVisitor<'a> {
#[derive(Default)]
struct SuspiciousVariablesVisitor<'a> {
names: Vec<&'a ast::ExprName>,
safe_functions: Vec<&'a Expr>,
names: Vec<&'a ast::ExprName<'a>>,
safe_functions: Vec<&'a Expr<'a>>,
}
/// `Visitor` to collect all suspicious variables (those referenced in
/// functions, but not bound as arguments).
impl<'a> Visitor<'a> for SuspiciousVariablesVisitor<'a> {
fn visit_stmt(&mut self, stmt: &'a Stmt) {
impl<'a> Visitor<'a, 'a> for SuspiciousVariablesVisitor<'a> {
fn visit_stmt(&mut self, stmt: &'a Stmt<'a>) {
match stmt {
Stmt::FunctionDef(ast::StmtFunctionDef {
parameters, body, ..
@ -122,7 +122,7 @@ impl<'a> Visitor<'a> for SuspiciousVariablesVisitor<'a> {
visitor::walk_stmt(self, stmt);
}
fn visit_expr(&mut self, expr: &'a Expr) {
fn visit_expr(&mut self, expr: &'a Expr<'a>) {
match expr {
Expr::Call(ast::ExprCall {
func,
@ -205,11 +205,11 @@ struct NamesFromAssignmentsVisitor<'a> {
}
/// `Visitor` to collect all names used in an assignment expression.
impl<'a> Visitor<'a> for NamesFromAssignmentsVisitor<'a> {
fn visit_expr(&mut self, expr: &'a Expr) {
impl<'a> Visitor<'a, 'a> for NamesFromAssignmentsVisitor<'a> {
fn visit_expr(&mut self, expr: &Expr<'a>) {
match expr {
Expr::Name(ast::ExprName { id, .. }) => {
self.names.push(id.as_str());
self.names.push(id);
}
Expr::Starred(ast::ExprStarred { value, .. }) => {
self.visit_expr(value);
@ -230,8 +230,8 @@ struct AssignedNamesVisitor<'a> {
}
/// `Visitor` to collect all used identifiers in a statement.
impl<'a> Visitor<'a> for AssignedNamesVisitor<'a> {
fn visit_stmt(&mut self, stmt: &'a Stmt) {
impl<'a> Visitor<'a, 'a> for AssignedNamesVisitor<'a> {
fn visit_stmt(&mut self, stmt: &'a Stmt<'a>) {
if stmt.is_function_def_stmt() {
// Don't recurse.
return;
@ -258,7 +258,7 @@ impl<'a> Visitor<'a> for AssignedNamesVisitor<'a> {
visitor::walk_stmt(self, stmt);
}
fn visit_expr(&mut self, expr: &'a Expr) {
fn visit_expr(&mut self, expr: &'a Expr<'a>) {
if expr.is_lambda_expr() {
// Don't recurse.
return;
@ -267,7 +267,7 @@ impl<'a> Visitor<'a> for AssignedNamesVisitor<'a> {
visitor::walk_expr(self, expr);
}
fn visit_comprehension(&mut self, comprehension: &'a Comprehension) {
fn visit_comprehension(&mut self, comprehension: &'a Comprehension<'a>) {
let mut visitor = NamesFromAssignmentsVisitor::default();
visitor.visit_expr(&comprehension.target);
self.names.extend(visitor.names);
@ -303,7 +303,7 @@ pub(crate) fn function_uses_loop_variable(checker: &mut Checker, node: &Node) {
// If a variable was used in a function or lambda body, and assigned in the
// loop, flag it.
for name in suspicious_variables {
if reassigned_in_loop.contains(&name.id.as_str()) {
if reassigned_in_loop.contains(&name.id) {
if !checker.flake8_bugbear_seen.contains(&name.range()) {
checker.flake8_bugbear_seen.push(name.range());
checker.diagnostics.push(Diagnostic::new(

View File

@ -143,9 +143,9 @@ fn is_mutating_function(function_name: &str) -> bool {
/// A visitor to collect mutations to a variable in a loop.
#[derive(Debug, Clone)]
struct LoopMutationsVisitor<'a> {
iter: &'a Expr,
target: &'a Expr,
index: &'a Expr,
iter: &'a Expr<'a>,
target: &'a Expr<'a>,
index: &'a Expr<'a>,
mutations: HashMap<u32, Vec<TextRange>>,
branches: Vec<u32>,
branch: u32,
@ -153,7 +153,7 @@ struct LoopMutationsVisitor<'a> {
impl<'a> LoopMutationsVisitor<'a> {
/// Initialize the visitor.
fn new(iter: &'a Expr, target: &'a Expr, index: &'a Expr) -> Self {
fn new(iter: &'a Expr<'a>, target: &'a Expr<'a>, index: &'a Expr<'a>) -> Self {
Self {
iter,
target,
@ -237,8 +237,8 @@ impl<'a> LoopMutationsVisitor<'a> {
}
/// `Visitor` to collect all used identifiers in a statement.
impl<'a> Visitor<'a> for LoopMutationsVisitor<'a> {
fn visit_stmt(&mut self, stmt: &'a Stmt) {
impl<'a> Visitor<'a, 'a> for LoopMutationsVisitor<'a> {
fn visit_stmt(&mut self, stmt: &'a Stmt<'a>) {
match stmt {
// Ex) `del items[0]`
Stmt::Delete(StmtDelete { range, targets }) => {
@ -302,7 +302,7 @@ impl<'a> Visitor<'a> for LoopMutationsVisitor<'a> {
}
}
fn visit_expr(&mut self, expr: &'a Expr) {
fn visit_expr(&mut self, expr: &'a Expr<'a>) {
// Ex) `items.append(1)`
if let Expr::Call(ExprCall { func, .. }) = expr {
self.handle_call(func);

View File

@ -76,11 +76,11 @@ pub(crate) fn loop_variable_overrides_iterator(checker: &mut Checker, target: &E
#[derive(Default)]
struct NameFinder<'a> {
names: FxHashMap<&'a str, &'a Expr>,
names: FxHashMap<&'a str, &'a Expr<'a>>,
}
impl<'a> Visitor<'a> for NameFinder<'a> {
fn visit_expr(&mut self, expr: &'a Expr) {
impl<'a> Visitor<'a, 'a> for NameFinder<'a> {
fn visit_expr(&mut self, expr: &'a Expr<'a>) {
match expr {
Expr::Name(ast::ExprName { id, .. }) => {
self.names.insert(id, expr);

View File

@ -113,7 +113,7 @@ struct ReturnInGeneratorVisitor {
has_yield: bool,
}
impl StatementVisitor<'_> for ReturnInGeneratorVisitor {
impl StatementVisitor<'_, '_> for ReturnInGeneratorVisitor {
fn visit_stmt(&mut self, stmt: &Stmt) {
match stmt {
Stmt::Expr(ast::StmtExpr { value, .. }) => match **value {

View File

@ -66,7 +66,7 @@ struct GroupNameFinder<'a> {
/// branch order.
counter_stack: Vec<Vec<u32>>,
/// A list of reused expressions.
exprs: Vec<&'a Expr>,
exprs: Vec<&'a Expr<'a>>,
}
impl<'a> GroupNameFinder<'a> {
@ -112,8 +112,8 @@ impl<'a> GroupNameFinder<'a> {
}
}
impl<'a> Visitor<'a> for GroupNameFinder<'a> {
fn visit_stmt(&mut self, stmt: &'a Stmt) {
impl<'a> Visitor<'a, 'a> for GroupNameFinder<'a> {
fn visit_stmt(&mut self, stmt: &'a Stmt<'a>) {
if self.overridden {
return;
}
@ -220,7 +220,7 @@ impl<'a> Visitor<'a> for GroupNameFinder<'a> {
}
}
fn visit_comprehension(&mut self, comprehension: &'a Comprehension) {
fn visit_comprehension(&mut self, comprehension: &'a Comprehension<'a>) {
if self.name_matches(&comprehension.target) {
self.overridden = true;
}
@ -235,7 +235,7 @@ impl<'a> Visitor<'a> for GroupNameFinder<'a> {
}
}
fn visit_expr(&mut self, expr: &'a Expr) {
fn visit_expr(&mut self, expr: &'a Expr<'a>) {
if let Expr::Named(ast::ExprNamed { target, .. }) = expr {
if self.name_matches(target) {
self.overridden = true;

View File

@ -1,11 +1,11 @@
use ruff_python_ast::{Expr, Keyword};
pub(super) fn exactly_one_argument_with_matching_function<'a>(
pub(super) fn exactly_one_argument_with_matching_function<'a, 'ast>(
name: &str,
func: &Expr,
args: &'a [Expr],
args: &'a [Expr<'ast>],
keywords: &[Keyword],
) -> Option<&'a Expr> {
) -> Option<&'a Expr<'ast>> {
let [arg] = args else {
return None;
};
@ -19,11 +19,11 @@ pub(super) fn exactly_one_argument_with_matching_function<'a>(
Some(arg)
}
pub(super) fn first_argument_with_matching_function<'a>(
pub(super) fn first_argument_with_matching_function<'a, 'ast>(
name: &str,
func: &Expr,
args: &'a [Expr],
) -> Option<&'a Expr> {
args: &'a [Expr<'ast>],
) -> Option<&'a Expr<'ast>> {
if func.as_name_expr().is_some_and(|func| func.id == name) {
args.first()
} else {

View File

@ -270,9 +270,9 @@ fn late_binding(parameters: &Parameters, body: &Expr) -> bool {
#[derive(Debug)]
struct LateBindingVisitor<'a> {
/// The arguments to the current lambda.
parameters: &'a Parameters,
parameters: &'a Parameters<'a>,
/// The arguments to any lambdas within the current lambda body.
lambdas: Vec<Option<&'a Parameters>>,
lambdas: Vec<Option<&'a Parameters<'a>>>,
/// Whether any names within the current lambda body are late-bound within nested lambdas.
late_bound: bool,
}
@ -287,10 +287,10 @@ impl<'a> LateBindingVisitor<'a> {
}
}
impl<'a> Visitor<'a> for LateBindingVisitor<'a> {
fn visit_stmt(&mut self, _stmt: &'a Stmt) {}
impl<'a> Visitor<'a, 'a> for LateBindingVisitor<'a> {
fn visit_stmt(&mut self, _stmt: &'a Stmt<'a>) {}
fn visit_expr(&mut self, expr: &'a Expr) {
fn visit_expr(&mut self, expr: &'a Expr<'a>) {
match expr {
Expr::Lambda(ast::ExprLambda { parameters, .. }) => {
self.lambdas.push(parameters.as_deref());
@ -322,5 +322,5 @@ impl<'a> Visitor<'a> for LateBindingVisitor<'a> {
}
}
fn visit_body(&mut self, _body: &'a [Stmt]) {}
fn visit_body(&mut self, _body: &'a [Stmt<'a>]) {}
}

View File

@ -147,7 +147,7 @@ pub(crate) fn unnecessary_dict_kwargs(checker: &mut Checker, call: &ast::ExprCal
/// Determine the set of keywords that appear in multiple positions (either directly, as in
/// `func(x=1)`, or indirectly, as in `func(**{"x": 1})`).
fn duplicates(call: &ast::ExprCall) -> FxHashSet<&str> {
fn duplicates<'a>(call: &'a ast::ExprCall<'a>) -> FxHashSet<&'a str> {
let mut seen =
FxHashSet::with_capacity_and_hasher(call.arguments.keywords.len(), FxBuildHasher);
let mut duplicates =
@ -171,7 +171,7 @@ fn duplicates(call: &ast::ExprCall) -> FxHashSet<&str> {
}
/// Return `Some` if a key is a valid keyword argument name, or `None` otherwise.
fn as_kwarg(key: &Expr) -> Option<&str> {
fn as_kwarg<'a>(key: &'a Expr<'a>) -> Option<&'a str> {
if let Expr::StringLiteral(ast::ExprStringLiteral { value, .. }) = key {
if is_identifier(value.to_str()) {
return Some(value.to_str());

View File

@ -118,7 +118,7 @@ pub(crate) fn bad_generator_return_type(
}
let returns = match &function_def.returns {
Some(returns) => returns.as_ref(),
Some(returns) => &**returns,
_ => return,
};
@ -282,7 +282,7 @@ fn generate_fix(
#[derive(Debug)]
struct YieldTypeInfo<'a> {
expr: &'a ast::Expr,
expr: &'a ast::Expr<'a>,
range: TextRange,
}

View File

@ -290,9 +290,9 @@ fn check_positional_args_for_overloaded_method(
parent_class_def: &StmtClassDef,
parameters_range: TextRange,
) {
fn parameter_annotation_loosely_matches_predicate(
parameter: &ParameterWithDefault,
predicate: impl FnOnce(&Expr) -> bool,
fn parameter_annotation_loosely_matches_predicate<'a, 'ast>(
parameter: &'a ParameterWithDefault<'ast>,
predicate: impl FnOnce(&'a Expr<'ast>) -> bool,
semantic: &SemanticModel,
) -> bool {
parameter
@ -424,10 +424,10 @@ fn check_positional_args_for_overloaded_method(
}
/// Return the non-`None` annotation element of a PEP 604-style union or `Optional` annotation.
fn non_none_annotation_element<'a>(
annotation: &'a Expr,
fn non_none_annotation_element<'a, 'ast>(
annotation: &'a Expr<'ast>,
semantic: &SemanticModel,
) -> Option<&'a Expr> {
) -> Option<&'a Expr<'ast>> {
// E.g., `typing.Union` or `typing.Optional`
if let Expr::Subscript(ExprSubscript { value, slice, .. }) = annotation {
let qualified_name = semantic.resolve_qualified_name(value)?;

View File

@ -58,13 +58,13 @@ impl Violation for RedundantLiteralUnion {
}
/// PYI051
pub(crate) fn redundant_literal_union<'a>(checker: &mut Checker, union: &'a Expr) {
pub(crate) fn redundant_literal_union<'a, 'ast>(checker: &mut Checker, union: &'a Expr<'ast>) {
let mut typing_literal_exprs = Vec::new();
let mut builtin_types_in_union = FxHashSet::default();
// Adds a member to `literal_exprs` for each value in a `Literal`, and any builtin types
// to `builtin_types_in_union`.
let mut func = |expr: &'a Expr, _parent: &'a Expr| {
let mut func = |expr: &'a Expr<'ast>, _parent: &'a Expr<'ast>| {
if let Expr::Subscript(ast::ExprSubscript { value, slice, .. }) = expr {
if checker.semantic().match_typing_expr(value, "Literal") {
if let Expr::Tuple(ast::ExprTuple { elts, .. }) = slice.as_ref() {

View File

@ -209,7 +209,7 @@ impl Violation for PytestUnittestAssertion {
/// the exception name.
struct ExceptionHandlerVisitor<'a> {
exception_name: &'a str,
current_assert: Option<&'a Stmt>,
current_assert: Option<&'a Stmt<'a>>,
errors: Vec<Diagnostic>,
}
@ -223,8 +223,8 @@ impl<'a> ExceptionHandlerVisitor<'a> {
}
}
impl<'a> Visitor<'a> for ExceptionHandlerVisitor<'a> {
fn visit_stmt(&mut self, stmt: &'a Stmt) {
impl<'a> Visitor<'a, 'a> for ExceptionHandlerVisitor<'a> {
fn visit_stmt(&mut self, stmt: &'a Stmt<'a>) {
match stmt {
Stmt::Assert(_) => {
self.current_assert = Some(stmt);
@ -235,7 +235,7 @@ impl<'a> Visitor<'a> for ExceptionHandlerVisitor<'a> {
}
}
fn visit_expr(&mut self, expr: &'a Expr) {
fn visit_expr(&mut self, expr: &'a Expr<'a>) {
match expr {
Expr::Name(ast::ExprName { id, .. }) => {
if let Some(current_assert) = self.current_assert {

View File

@ -622,12 +622,12 @@ impl fmt::Display for Parentheses {
struct SkipFunctionsVisitor<'a> {
has_return_with_value: bool,
has_yield_from: bool,
yield_statements: Vec<&'a Expr>,
addfinalizer_call: Option<&'a Expr>,
yield_statements: Vec<&'a Expr<'a>>,
addfinalizer_call: Option<&'a Expr<'a>>,
}
impl<'a> Visitor<'a> for SkipFunctionsVisitor<'a> {
fn visit_stmt(&mut self, stmt: &'a Stmt) {
impl<'a> Visitor<'a, 'a> for SkipFunctionsVisitor<'a> {
fn visit_stmt(&mut self, stmt: &'a Stmt<'a>) {
match stmt {
Stmt::Return(ast::StmtReturn { value, range: _ }) => {
if value.is_some() {
@ -639,7 +639,7 @@ impl<'a> Visitor<'a> for SkipFunctionsVisitor<'a> {
}
}
fn visit_expr(&mut self, expr: &'a Expr) {
fn visit_expr(&mut self, expr: &'a Expr<'a>) {
match expr {
Expr::YieldFrom(_) => {
self.has_yield_from = true;
@ -663,10 +663,10 @@ impl<'a> Visitor<'a> for SkipFunctionsVisitor<'a> {
}
}
fn fixture_decorator<'a>(
decorators: &'a [Decorator],
fn fixture_decorator<'a, 'ast>(
decorators: &'a [Decorator<'ast>],
semantic: &SemanticModel,
) -> Option<&'a Decorator> {
) -> Option<&'a Decorator<'ast>> {
decorators.iter().find(|decorator| {
is_pytest_fixture(decorator, semantic) || is_pytest_yield_fixture(decorator, semantic)
})

View File

@ -4,9 +4,9 @@ use ruff_python_ast::{self as ast, Decorator, Expr, Keyword};
use ruff_python_semantic::SemanticModel;
use ruff_python_trivia::PythonWhitespace;
pub(super) fn get_mark_decorators(
decorators: &[Decorator],
) -> impl Iterator<Item = (&Decorator, &str)> {
pub(super) fn get_mark_decorators<'a, 'ast>(
decorators: &'a [Decorator<'ast>],
) -> impl Iterator<Item = (&'a Decorator<'ast>, &'a str)> {
decorators.iter().filter_map(|decorator| {
let name = UnqualifiedName::from_expr(map_callable(&decorator.expression))?;
let ["pytest", "mark", marker] = name.segments() else {

View File

@ -52,12 +52,12 @@ impl Violation for PytestPatchWithLambda {
/// Visitor that checks references the argument names in the lambda body.
#[derive(Debug)]
struct LambdaBodyVisitor<'a> {
parameters: &'a Parameters,
parameters: &'a Parameters<'a>,
uses_args: bool,
}
impl<'a> Visitor<'a> for LambdaBodyVisitor<'a> {
fn visit_expr(&mut self, expr: &'a Expr) {
impl<'a> Visitor<'a, 'a> for LambdaBodyVisitor<'a> {
fn visit_expr(&mut self, expr: &'a Expr<'a>) {
match expr {
Expr::Name(ast::ExprName { id, .. }) => {
if self.parameters.includes(id) {

View File

@ -1,10 +1,13 @@
use anyhow::{anyhow, bail, Result};
use itertools::all;
use rustc_hash::{FxBuildHasher, FxHashMap};
use ruff_allocator::{Allocator, CloneIn};
use ruff_python_ast::name::Name;
use ruff_python_ast::{
self as ast, Arguments, CmpOp, Expr, ExprContext, Identifier, Keyword, Stmt, UnaryOp,
};
use ruff_text_size::TextRange;
use rustc_hash::{FxBuildHasher, FxHashMap};
/// An enum to represent the different types of assertions present in the
/// `unittest` module. Note: any variants that can't be replaced with plain
@ -161,19 +164,24 @@ impl TryFrom<&str> for UnittestAssert {
}
}
fn assert(expr: &Expr, msg: Option<&Expr>) -> Stmt {
fn assert<'ast>(expr: &Expr, msg: Option<&Expr>, allocator: &'ast Allocator) -> Stmt<'ast> {
Stmt::Assert(ast::StmtAssert {
test: Box::new(expr.clone()),
msg: msg.map(|msg| Box::new(msg.clone())),
test: ruff_allocator::Box::new_in(expr.clone_in(allocator), allocator),
msg: msg.map(|msg| ruff_allocator::Box::new_in(msg.clone_in(allocator), allocator)),
range: TextRange::default(),
})
}
fn compare(left: &Expr, cmp_op: CmpOp, right: &Expr) -> Expr {
fn compare<'ast>(
left: &Expr,
cmp_op: CmpOp,
right: &Expr,
allocator: &'ast Allocator,
) -> Expr<'ast> {
Expr::Compare(ast::ExprCompare {
left: Box::new(left.clone()),
ops: Box::from([cmp_op]),
comparators: Box::from([right.clone()]),
left: ruff_allocator::Box::new_in(left.clone_in(allocator), allocator),
ops: allocator.alloc_slice_fill_iter([cmp_op.clone_in(allocator)]),
comparators: allocator.alloc_slice_fill_iter([right.clone_in(allocator)]),
range: TextRange::default(),
})
}
@ -276,7 +284,12 @@ impl UnittestAssert {
Ok(args_map)
}
pub(crate) fn generate_assert(self, args: &[Expr], keywords: &[Keyword]) -> Result<Stmt> {
pub(crate) fn generate_assert<'ast>(
self,
args: &[Expr],
keywords: &[Keyword],
allocator: &'ast Allocator,
) -> Result<Stmt<'ast>> {
let args = self.args_map(args, keywords)?;
match self {
UnittestAssert::True
@ -292,13 +305,17 @@ impl UnittestAssert {
assert(
&Expr::UnaryOp(ast::ExprUnaryOp {
op: UnaryOp::Not,
operand: Box::new(expr.clone()),
operand: ruff_allocator::Box::new_in(
expr.clone_in(allocator),
allocator,
),
range: TextRange::default(),
}),
msg,
allocator,
)
} else {
assert(expr, msg)
assert(expr, msg, allocator)
},
)
}
@ -336,8 +353,8 @@ impl UnittestAssert {
UnittestAssert::IsNot => CmpOp::IsNot,
_ => unreachable!(),
};
let expr = compare(first, cmp_op, second);
Ok(assert(&expr, msg))
let expr = compare(first, cmp_op, second, allocator);
Ok(assert(&expr, msg, allocator))
}
UnittestAssert::In | UnittestAssert::NotIn => {
let member = args
@ -352,8 +369,8 @@ impl UnittestAssert {
} else {
CmpOp::NotIn
};
let expr = compare(member, cmp_op, container);
Ok(assert(&expr, msg))
let expr = compare(member, cmp_op, container, allocator);
Ok(assert(&expr, msg, allocator))
}
UnittestAssert::IsNone | UnittestAssert::IsNotNone => {
let expr = args
@ -368,8 +385,8 @@ impl UnittestAssert {
let node = Expr::NoneLiteral(ast::ExprNoneLiteral {
range: TextRange::default(),
});
let expr = compare(expr, cmp_op, &node);
Ok(assert(&expr, msg))
let expr = compare(expr, cmp_op, &node, allocator);
Ok(assert(&expr, msg, allocator))
}
UnittestAssert::IsInstance | UnittestAssert::NotIsInstance => {
let obj = args
@ -380,30 +397,33 @@ impl UnittestAssert {
.ok_or_else(|| anyhow!("Missing argument `cls`"))?;
let msg = args.get("msg").copied();
let node = ast::ExprName {
id: Name::new_static("isinstance"),
id: "isinstance",
ctx: ExprContext::Load,
range: TextRange::default(),
};
let node1 = ast::ExprCall {
func: Box::new(node.into()),
func: ruff_allocator::Box::new_in(node.into(), allocator),
arguments: Arguments {
args: Box::from([(**obj).clone(), (**cls).clone()]),
keywords: Box::from([]),
args: allocator.alloc_slice_fill_iter([
(**obj).clone_in(allocator),
(**cls).clone_in(allocator),
]),
keywords: &mut [],
range: TextRange::default(),
},
range: TextRange::default(),
};
let isinstance = node1.into();
if matches!(self, UnittestAssert::IsInstance) {
Ok(assert(&isinstance, msg))
Ok(assert(&isinstance, msg, allocator))
} else {
let node = ast::ExprUnaryOp {
op: UnaryOp::Not,
operand: Box::new(isinstance),
operand: ruff_allocator::Box::new_in(isinstance, allocator),
range: TextRange::default(),
};
let expr = node.into();
Ok(assert(&expr, msg))
Ok(assert(&expr, msg, allocator))
}
}
UnittestAssert::Regex
@ -418,18 +438,18 @@ impl UnittestAssert {
.ok_or_else(|| anyhow!("Missing argument `regex`"))?;
let msg = args.get("msg").copied();
let node = ast::ExprName {
id: Name::new_static("re"),
id: "re",
ctx: ExprContext::Load,
range: TextRange::default(),
};
let node1 = ast::ExprAttribute {
value: Box::new(node.into()),
attr: Identifier::new("search".to_string(), TextRange::default()),
value: ruff_allocator::Box::new_in(node.into(), allocator),
attr: Identifier::new("search", TextRange::default()),
ctx: ExprContext::Load,
range: TextRange::default(),
};
let node2 = ast::ExprCall {
func: Box::new(node1.into()),
func: ruff_allocator::Box::new_in(node1.into(), allocator),
arguments: Arguments {
args: Box::from([(**regex).clone(), (**text).clone()]),
keywords: Box::from([]),
@ -439,14 +459,14 @@ impl UnittestAssert {
};
let re_search = node2.into();
if matches!(self, UnittestAssert::Regex | UnittestAssert::RegexpMatches) {
Ok(assert(&re_search, msg))
Ok(assert(&re_search, msg, allocator))
} else {
let node = ast::ExprUnaryOp {
op: UnaryOp::Not,
operand: Box::new(re_search),
range: TextRange::default(),
};
Ok(assert(&node.into(), msg))
Ok(assert(&node.into(), msg, allocator))
}
}
_ => bail!("Cannot fix `{self}`"),

View File

@ -104,7 +104,7 @@ impl<'a> AvoidableEscapedQuoteChecker<'a> {
}
}
impl Visitor<'_> for AvoidableEscapedQuoteChecker<'_> {
impl Visitor<'_, '_> for AvoidableEscapedQuoteChecker<'_> {
fn visit_string_literal(&mut self, string_literal: &'_ ast::StringLiteral) {
if let Some(diagnostic) = check_string_or_bytes(
self.locator,
@ -323,7 +323,7 @@ struct ContainsAnyString {
result: bool,
}
impl Visitor<'_> for ContainsAnyString {
impl Visitor<'_, '_> for ContainsAnyString {
fn visit_string_literal(&mut self, _: &'_ ast::StringLiteral) {
self.result = true;
}

View File

@ -8,9 +8,9 @@ use ruff_python_semantic::SemanticModel;
#[derive(Default)]
pub(super) struct Stack<'data> {
/// The `return` statements in the current function.
pub(super) returns: Vec<&'data ast::StmtReturn>,
pub(super) returns: Vec<&'data ast::StmtReturn<'data>>,
/// The `elif` or `else` statements in the current function.
pub(super) elifs_elses: Vec<(&'data [Stmt], &'data ElifElseClause)>,
pub(super) elifs_elses: Vec<(&'data [Stmt<'data>], &'data ElifElseClause<'data>)>,
/// The non-local variables in the current function.
pub(super) non_locals: FxHashSet<&'data str>,
/// The annotated variables in the current function.
@ -33,8 +33,11 @@ pub(super) struct Stack<'data> {
/// The `assignment`-to-`return` statement pairs in the current function.
/// TODO(charlie): Remove the extra [`Stmt`] here, which is necessary to support statement
/// removal for the `return` statement.
pub(super) assignment_return:
Vec<(&'data ast::StmtAssign, &'data ast::StmtReturn, &'data Stmt)>,
pub(super) assignment_return: Vec<(
&'data ast::StmtAssign<'data>,
&'data ast::StmtReturn<'data>,
&'data Stmt<'data>,
)>,
}
pub(super) struct ReturnVisitor<'semantic, 'data> {
@ -43,9 +46,9 @@ pub(super) struct ReturnVisitor<'semantic, 'data> {
/// The current stack of nodes.
pub(super) stack: Stack<'data>,
/// The preceding sibling of the current node.
sibling: Option<&'data Stmt>,
sibling: Option<&'data Stmt<'data>>,
/// The parent nodes of the current node.
parents: Vec<&'data Stmt>,
parents: Vec<&'data Stmt<'data>>,
}
impl<'semantic, 'data> ReturnVisitor<'semantic, 'data> {
@ -59,8 +62,8 @@ impl<'semantic, 'data> ReturnVisitor<'semantic, 'data> {
}
}
impl<'semantic, 'a> Visitor<'a> for ReturnVisitor<'semantic, 'a> {
fn visit_stmt(&mut self, stmt: &'a Stmt) {
impl<'semantic, 'a> Visitor<'a, 'a> for ReturnVisitor<'semantic, 'a> {
fn visit_stmt(&mut self, stmt: &'a Stmt<'a>) {
match stmt {
Stmt::ClassDef(ast::StmtClassDef { decorator_list, .. }) => {
// Visit the decorators, etc.
@ -105,7 +108,7 @@ impl<'semantic, 'a> Visitor<'a> for ReturnVisitor<'semantic, 'a> {
// Ex) `x: int`
if value.is_none() {
if let Expr::Name(name) = target.as_ref() {
self.stack.annotations.insert(name.id.as_str());
self.stack.annotations.insert(name.id);
}
}
}
@ -169,7 +172,7 @@ impl<'semantic, 'a> Visitor<'a> for ReturnVisitor<'semantic, 'a> {
self.parents.pop();
}
fn visit_expr(&mut self, expr: &'a Expr) {
fn visit_expr(&mut self, expr: &'a Expr<'a>) {
match expr {
Expr::YieldFrom(_) | Expr::Yield(_) => {
self.stack.is_generator = true;
@ -178,7 +181,7 @@ impl<'semantic, 'a> Visitor<'a> for ReturnVisitor<'semantic, 'a> {
}
}
fn visit_body(&mut self, body: &'a [Stmt]) {
fn visit_body(&mut self, body: &'a [Stmt<'a>]) {
let sibling = self.sibling;
self.sibling = None;
visitor::walk_body(self, body);

View File

@ -301,7 +301,7 @@ pub(crate) fn is_same_expr<'a>(a: &'a Expr, b: &'a Expr) -> Option<&'a str> {
}
/// If `call` is an `isinstance()` call, return its target.
fn isinstance_target<'a>(call: &'a Expr, semantic: &'a SemanticModel) -> Option<&'a Expr> {
fn isinstance_target<'a>(call: &Expr<'a>, semantic: &SemanticModel) -> Option<&'a Expr<'a>> {
// Verify that this is an `isinstance` call.
let ast::ExprCall {
func,
@ -470,7 +470,7 @@ pub(crate) fn duplicate_isinstance_call(checker: &mut Checker, expr: &Expr) {
}
}
fn match_eq_target(expr: &Expr) -> Option<(&Name, &Expr)> {
fn match_eq_target<'a>(expr: &Expr<'a>) -> Option<(&'a Name, &'a Expr<'a>)> {
let Expr::Compare(ast::ExprCompare {
left,
ops,

View File

@ -195,7 +195,7 @@ fn check_os_environ_subscript(checker: &mut Checker, expr: &Expr) {
let Expr::Name(ast::ExprName { id, .. }) = attr_value.as_ref() else {
return;
};
if id != "os" || attr != "environ" {
if *id != "os" || attr != "environ" {
return;
}
let Expr::StringLiteral(ast::ExprStringLiteral { value: env_var, .. }) = slice.as_ref() else {

View File

@ -179,7 +179,7 @@ pub(crate) fn if_expr_with_true_false(
&ast::ExprCall {
func: Box::new(
ast::ExprName {
id: Name::new_static("bool"),
id: "bool",
ctx: ExprContext::Load,
range: TextRange::default(),
}

View File

@ -68,7 +68,7 @@ impl Violation for MultipleWithStatements {
/// Returns a boolean indicating whether it's an async with statement, the items
/// and body.
fn next_with(body: &[Stmt]) -> Option<(bool, &[WithItem], &[Stmt])> {
fn next_with<'a>(body: &'a [Stmt<'a>]) -> Option<(bool, &'a [WithItem<'a>], &'a [Stmt<'a>])> {
let [Stmt::With(ast::StmtWith {
is_async,
items,

View File

@ -139,12 +139,12 @@ pub(crate) fn nested_if_statements(
#[derive(Debug, Clone, Copy)]
pub(super) enum NestedIf<'a> {
If(&'a ast::StmtIf),
Elif(&'a ElifElseClause),
If(&'a ast::StmtIf<'a>),
Elif(&'a ElifElseClause<'a>),
}
impl<'a> NestedIf<'a> {
pub(super) fn body(self) -> &'a [Stmt] {
pub(super) fn body(self) -> &'a [Stmt<'a>] {
match self {
NestedIf::If(stmt_if) => &stmt_if.body,
NestedIf::Elif(clause) => &clause.body,
@ -165,7 +165,7 @@ impl Ranged for NestedIf<'_> {
}
}
impl<'a> From<&NestedIf<'a>> for AnyNodeRef<'a> {
impl<'a> From<&NestedIf<'a>> for AnyNodeRef<'a, 'a> {
fn from(value: &NestedIf<'a>) -> Self {
match value {
NestedIf::If(stmt_if) => (*stmt_if).into(),
@ -175,7 +175,7 @@ impl<'a> From<&NestedIf<'a>> for AnyNodeRef<'a> {
}
/// Returns the body, the range of the `if` or `elif` and whether the range is for an `if` or `elif`
fn nested_if_body(stmt_if: &ast::StmtIf) -> Option<NestedIf> {
fn nested_if_body<'a>(stmt_if: &'a ast::StmtIf<'a>) -> Option<NestedIf<'a>> {
let ast::StmtIf {
test,
body,
@ -225,7 +225,7 @@ fn nested_if_body(stmt_if: &ast::StmtIf) -> Option<NestedIf> {
/// z = 1
/// ...
/// ```
fn find_last_nested_if(body: &[Stmt]) -> Option<&Expr> {
fn find_last_nested_if<'a>(body: &'a [Stmt<'a>]) -> Option<&'a Expr<'a>> {
let [Stmt::If(ast::StmtIf {
test,
body: inner_body,
@ -248,7 +248,7 @@ fn is_main_check(expr: &Expr) -> bool {
}) = expr
{
if let Expr::Name(ast::ExprName { id, .. }) = left.as_ref() {
if id == "__name__" {
if *id == "__name__" {
if let [Expr::StringLiteral(ast::ExprStringLiteral { value, .. })] = &**comparators
{
if value == "__main__" {

View File

@ -152,7 +152,7 @@ pub(crate) fn enumerate_for_loop(checker: &mut Checker, for_stmt: &ast::StmtFor)
/// If the statement is an index increment statement (e.g., `i += 1`), return
/// the name of the index variable.
fn match_index_increment(stmt: &Stmt) -> Option<&ast::ExprName> {
fn match_index_increment<'a, 'ast>(stmt: &'a Stmt<'ast>) -> Option<&'a ast::ExprName<'ast>> {
let Stmt::AugAssign(ast::StmtAugAssign {
target,
op: Operator::Add,
@ -183,7 +183,7 @@ struct LoopControlFlowVisitor {
has_continue: bool,
}
impl StatementVisitor<'_> for LoopControlFlowVisitor {
impl StatementVisitor<'_, '_> for LoopControlFlowVisitor {
fn visit_stmt(&mut self, stmt: &Stmt) {
match stmt {
Stmt::Continue(_) => self.has_continue = true,

View File

@ -1,3 +1,4 @@
use ruff_allocator::{Allocator, CloneIn};
use ruff_diagnostics::{Diagnostic, Edit, Fix, FixAvailability, Violation};
use ruff_macros::{derive_message_formats, violation};
use ruff_python_ast::{self as ast, ElifElseClause, Expr, Stmt};
@ -154,16 +155,22 @@ pub(crate) fn if_else_block_instead_of_if_exp(checker: &mut Checker, stmt_if: &a
checker.diagnostics.push(diagnostic);
}
fn ternary(target_var: &Expr, body_value: &Expr, test: &Expr, orelse_value: &Expr) -> Stmt {
fn ternary<'ast>(
target_var: &Expr,
body_value: &Expr,
test: &Expr,
orelse_value: &Expr,
allocator: &'ast Allocator,
) -> Stmt<'ast> {
let node = ast::ExprIf {
test: Box::new(test.clone()),
body: Box::new(body_value.clone()),
orelse: Box::new(orelse_value.clone()),
test: ruff_allocator::Box::new_in(test.clone_in(allocator), allocator),
body: ruff_allocator::Box::new_in(body_value.clone_in(allocator), allocator),
orelse: ruff_allocator::Box::new_in(orelse_value.clone_in(allocator), allocator),
range: TextRange::default(),
};
let node1 = ast::StmtAssign {
targets: vec![target_var.clone()],
value: Box::new(node.into()),
targets: vec![target_var.clone_in(allocator)],
value: ruff_allocator::Box::new_in(node.into(), allocator),
range: TextRange::default(),
};
node1.into()

View File

@ -1,3 +1,5 @@
use itertools::all;
use ruff_allocator::{Allocator, CloneIn};
use ruff_diagnostics::{Diagnostic, Edit, Fix, FixAvailability, Violation};
use ruff_macros::{derive_message_formats, violation};
use ruff_python_ast::helpers::any_over_expr;
@ -58,7 +60,11 @@ impl Violation for ReimplementedBuiltin {
}
/// SIM110, SIM111
pub(crate) fn convert_for_loop_to_any_all(checker: &mut Checker, stmt: &Stmt) {
pub(crate) fn convert_for_loop_to_any_all(
checker: &mut Checker,
stmt: &Stmt,
allocator: &Allocator,
) {
if !checker.semantic().current_scope().kind.is_function() {
return;
}
@ -90,11 +96,12 @@ pub(crate) fn convert_for_loop_to_any_all(checker: &mut Checker, stmt: &Stmt) {
// Replace with `any`.
(true, false) => {
let contents = return_stmt(
Name::new_static("any"),
"any",
loop_.test,
loop_.target,
loop_.iter,
checker.generator(),
allocator,
);
// Don't flag if the resulting expression would exceed the maximum line length.
@ -133,7 +140,7 @@ pub(crate) fn convert_for_loop_to_any_all(checker: &mut Checker, stmt: &Stmt) {
range: _,
}) = &loop_.test
{
*operand.clone()
(**operand).clone_in(allocator)
} else if let Expr::Compare(ast::ExprCompare {
left,
ops,
@ -155,35 +162,43 @@ pub(crate) fn convert_for_loop_to_any_all(checker: &mut Checker, stmt: &Stmt) {
CmpOp::NotIn => CmpOp::In,
};
let node = ast::ExprCompare {
left: left.clone(),
ops: Box::from([op]),
comparators: Box::from([comparator.clone()]),
left: left.clone_in(allocator),
ops: allocator.alloc_slice_fill_iter([op]),
comparators: allocator
.alloc_slice_fill_iter([comparator.clone_in(allocator)]),
range: TextRange::default(),
};
node.into()
Expr::Compare(node)
} else {
let node = ast::ExprUnaryOp {
op: UnaryOp::Not,
operand: Box::new(loop_.test.clone()),
operand: ruff_allocator::Box::new_in(
loop_.test.clone_in(allocator),
allocator,
),
range: TextRange::default(),
};
node.into()
Expr::UnaryOp(node)
}
} else {
let node = ast::ExprUnaryOp {
op: UnaryOp::Not,
operand: Box::new(loop_.test.clone()),
operand: ruff_allocator::Box::new_in(
loop_.test.clone_in(allocator),
allocator,
),
range: TextRange::default(),
};
node.into()
Expr::UnaryOp(node)
}
};
let contents = return_stmt(
Name::new_static("all"),
"all",
&test,
loop_.target,
loop_.iter,
checker.generator(),
allocator,
);
// Don't flag if the resulting expression would exceed the maximum line length.
@ -230,11 +245,11 @@ struct Loop<'a> {
/// The `return` value of the loop.
return_value: bool,
/// The test condition in the loop.
test: &'a Expr,
test: &'a Expr<'a>,
/// The target of the loop.
target: &'a Expr,
target: &'a Expr<'a>,
/// The iterator of the loop.
iter: &'a Expr,
iter: &'a Expr<'a>,
}
/// Represents a `return` statement following a `for` loop, like:
@ -256,10 +271,10 @@ struct Loop<'a> {
#[derive(Debug)]
struct Terminal<'a> {
return_value: bool,
stmt: &'a Stmt,
stmt: &'a Stmt<'a>,
}
fn match_loop(stmt: &Stmt) -> Option<Loop> {
fn match_loop<'a>(stmt: &Stmt<'a>) -> Option<Loop<'a>> {
let Stmt::For(ast::StmtFor {
body, target, iter, ..
}) = stmt
@ -310,7 +325,7 @@ fn match_loop(stmt: &Stmt) -> Option<Loop> {
/// return True
/// return False
/// ```
fn match_else_return(stmt: &Stmt) -> Option<Terminal> {
fn match_else_return<'a>(stmt: &Stmt<'a>) -> Option<Terminal<'a>> {
let Stmt::For(ast::StmtFor { orelse, .. }) = stmt else {
return None;
};
@ -379,12 +394,19 @@ fn match_sibling_return<'a>(stmt: &'a Stmt, sibling: &'a Stmt) -> Option<Termina
}
/// Generate a return statement for an `any` or `all` builtin comprehension.
fn return_stmt(id: Name, test: &Expr, target: &Expr, iter: &Expr, generator: Generator) -> String {
fn return_stmt(
id: &str,
test: &Expr,
target: &Expr,
iter: &Expr,
generator: Generator,
allocator: &Allocator,
) -> String {
let node = ast::ExprGenerator {
elt: Box::new(test.clone()),
elt: ruff_allocator::Box::new_in(test.clone_in(allocator), allocator),
generators: vec![Comprehension {
target: target.clone(),
iter: iter.clone(),
target: target.clone_in(allocator),
iter: iter.clone_in(allocator),
ifs: vec![],
is_async: false,
range: TextRange::default(),
@ -393,21 +415,21 @@ fn return_stmt(id: Name, test: &Expr, target: &Expr, iter: &Expr, generator: Gen
parenthesized: false,
};
let node1 = ast::ExprName {
id,
id: allocator.alloc_str(id),
ctx: ExprContext::Load,
range: TextRange::default(),
};
let node2 = ast::ExprCall {
func: Box::new(node1.into()),
func: ruff_allocator::Box::new_in(node1.into(), allocator),
arguments: Arguments {
args: Box::from([node.into()]),
keywords: Box::from([]),
args: allocator.alloc_slice_fill_iter([node.into()]),
keywords: &mut [],
range: TextRange::default(),
},
range: TextRange::default(),
};
let node3 = ast::StmtReturn {
value: Some(Box::new(node2.into())),
value: Some(ruff_allocator::Box::new_in(node2.into(), allocator)),
range: TextRange::default(),
};
generator.stmt(&node3.into())

View File

@ -50,7 +50,7 @@ impl Violation for ReturnInTryExceptFinally {
}
}
fn find_return(stmts: &[Stmt]) -> Option<&Stmt> {
fn find_return<'a, 'ast>(stmts: &'a [Stmt<'ast>]) -> Option<&'a Stmt<'ast>> {
stmts.iter().find(|stmt| stmt.is_return_stmt())
}

View File

@ -82,7 +82,7 @@ enum ConstantLikelihood {
Definitely = 2,
}
impl From<&Expr> for ConstantLikelihood {
impl From<&Expr<'_>> for ConstantLikelihood {
/// Determine the [`ConstantLikelihood`] of an expression.
fn from(expr: &Expr) -> Self {
match expr {

View File

@ -116,14 +116,14 @@ pub(crate) fn zip_dict_keys_and_values(checker: &mut Checker, expr: &ast::ExprCa
checker.diagnostics.push(diagnostic);
}
fn get_var_attr(expr: &Expr) -> Option<(&ExprName, &Identifier)> {
fn get_var_attr<'ast>(expr: &Expr<'ast>) -> Option<(&'ast ExprName<'ast>, &'ast Identifier<'ast>)> {
let Expr::Call(ast::ExprCall { func, .. }) = expr else {
return None;
};
let Expr::Attribute(ExprAttribute { value, attr, .. }) = func.as_ref() else {
let Expr::Attribute(ExprAttribute { value, attr, .. }) = &**func else {
return None;
};
let Expr::Name(var_name) = value.as_ref() else {
let Expr::Name(var_name) = &**value else {
return None;
};
Some((var_name, attr))

View File

@ -7,7 +7,7 @@ pub(super) fn has_slots(body: &[Stmt]) -> bool {
Stmt::Assign(ast::StmtAssign { targets, .. }) => {
for target in targets {
if let Expr::Name(ast::ExprName { id, .. }) = target {
if id.as_str() == "__slots__" {
if *id == "__slots__" {
return true;
}
}
@ -15,7 +15,7 @@ pub(super) fn has_slots(body: &[Stmt]) -> bool {
}
Stmt::AnnAssign(ast::StmtAnnAssign { target, .. }) => {
if let Expr::Name(ast::ExprName { id, .. }) = target.as_ref() {
if id.as_str() == "__slots__" {
if *id == "__slots__" {
return true;
}
}

View File

@ -94,10 +94,7 @@ fn fix_banned_relative_import(
panic!("Expected Stmt::ImportFrom");
};
let node = ast::StmtImportFrom {
module: Some(Identifier::new(
module_path.to_string(),
TextRange::default(),
)),
module: Some(Identifier::new(&module_path, TextRange::default())),
names: names.clone(),
level: 0,
range: TextRange::default(),

View File

@ -311,7 +311,7 @@ fn move_imports(checker: &Checker, node_id: NodeId, imports: &[ImportBinding]) -
// Step 1) Remove the import.
let remove_import_edit = fix::edits::remove_unused_imports(
member_names.iter().map(AsRef::as_ref),
member_names.iter().map(|name| *name),
statement,
parent,
checker.locator(),

View File

@ -73,7 +73,7 @@ pub(crate) fn runtime_string_union(checker: &mut Checker, expr: &Expr) {
}
/// Collect all string members in possibly-nested binary `|` expressions.
fn traverse_op<'a>(expr: &'a Expr, strings: &mut Vec<&'a Expr>) {
fn traverse_op<'a, 'ast>(expr: &'a Expr<'ast>, strings: &mut Vec<&'a Expr<'ast>>) {
match expr {
Expr::StringLiteral(_) => {
strings.push(expr);

View File

@ -287,7 +287,7 @@ fn method(
fn call<'a>(
argumentable: Argumentable,
parameters: impl Iterator<Item = &'a Parameter>,
parameters: impl Iterator<Item = &'a Parameter<'a>>,
scope: &Scope,
semantic: &SemanticModel,
dummy_variable_rgx: &Regex,

View File

@ -1,10 +1,14 @@
use ruff_allocator::{Allocator, CloneIn};
use ruff_python_ast::{self as ast, Arguments, ConversionFlag, Expr};
use ruff_text_size::TextRange;
/// Wrap an expression in a [`ast::FStringElement::Expression`] with no special formatting.
fn to_f_string_expression_element(inner: &Expr) -> ast::FStringElement {
fn to_f_string_expression_element<'ast>(
inner: &Expr,
allocator: &'ast Allocator,
) -> ast::FStringElement<'ast> {
ast::FStringElement::Expression(ast::FStringExpressionElement {
expression: Box::new(inner.clone()),
expression: ruff_allocator::Box::new_in(inner.clone_in(allocator), allocator),
debug_text: None,
conversion: ConversionFlag::None,
format_spec: None,
@ -13,9 +17,12 @@ fn to_f_string_expression_element(inner: &Expr) -> ast::FStringElement {
}
/// Convert a string to a [`ast::FStringElement::Literal`].
pub(super) fn to_f_string_literal_element(s: &str) -> ast::FStringElement {
pub(super) fn to_f_string_literal_element<'ast>(
s: &str,
allocator: &'ast Allocator,
) -> ast::FStringElement<'ast> {
ast::FStringElement::Literal(ast::FStringLiteralElement {
value: s.to_string().into_boxed_str(),
value: allocator.alloc_str(s),
range: TextRange::default(),
})
}
@ -49,19 +56,24 @@ fn is_simple_callee(func: &Expr) -> bool {
}
/// Convert an expression to a f-string element (if it looks like a good idea).
pub(super) fn to_f_string_element(expr: &Expr) -> Option<ast::FStringElement> {
pub(super) fn to_f_string_element<'ast>(
expr: &Expr,
allocator: &'ast Allocator,
) -> Option<ast::FStringElement<'ast>> {
match expr {
Expr::StringLiteral(ast::ExprStringLiteral { value, range }) => {
Some(ast::FStringElement::Literal(ast::FStringLiteralElement {
value: value.to_string().into_boxed_str(),
value: allocator.alloc_str(value.to_str()),
range: *range,
}))
}
// These should be pretty safe to wrap in a formatted value.
Expr::NumberLiteral(_) | Expr::BooleanLiteral(_) | Expr::Name(_) | Expr::Attribute(_) => {
Some(to_f_string_expression_element(expr))
Some(to_f_string_expression_element(expr, allocator))
}
Expr::Call(_) if is_simple_call(expr) => {
Some(to_f_string_expression_element(expr, allocator))
}
Expr::Call(_) if is_simple_call(expr) => Some(to_f_string_expression_element(expr)),
_ => None,
}
}

View File

@ -1,7 +1,7 @@
use crate::fix::edits::pad;
use ast::FStringFlags;
use itertools::Itertools;
use crate::fix::edits::pad;
use ruff_allocator::Allocator;
use ruff_diagnostics::{AlwaysFixableViolation, Diagnostic, Edit, Fix};
use ruff_macros::{derive_message_formats, violation};
use ruff_python_ast::{self as ast, Arguments, Expr};
@ -60,21 +60,26 @@ fn is_static_length(elts: &[Expr]) -> bool {
elts.iter().all(|e| !e.is_starred_expr())
}
fn build_fstring(joiner: &str, joinees: &[Expr]) -> Option<Expr> {
fn build_fstring<'ast>(
joiner: &str,
joinees: &[Expr],
allocator: &'ast Allocator,
) -> Option<Expr<'ast>> {
// If all elements are string constants, join them into a single string.
if joinees.iter().all(Expr::is_string_literal_expr) {
let node = ast::StringLiteral {
value: joinees
.iter()
.filter_map(|expr| {
if let Expr::StringLiteral(ast::ExprStringLiteral { value, .. }) = expr {
Some(value.to_str())
} else {
None
}
})
.join(joiner)
.into_boxed_str(),
value: allocator.alloc_str(
&joinees
.iter()
.filter_map(|expr| {
if let Expr::StringLiteral(ast::ExprStringLiteral { value, .. }) = expr {
Some(value.to_str())
} else {
None
}
})
.join(joiner),
),
..ast::StringLiteral::default()
};
return Some(node.into());
@ -90,9 +95,9 @@ fn build_fstring(joiner: &str, joinees: &[Expr]) -> Option<Expr> {
return None;
}
if !std::mem::take(&mut first) {
f_string_elements.push(helpers::to_f_string_literal_element(joiner));
f_string_elements.push(helpers::to_f_string_literal_element(joiner, allocator));
}
f_string_elements.push(helpers::to_f_string_element(expr)?);
f_string_elements.push(helpers::to_f_string_element(expr, allocator)?);
}
let node = ast::FString {
@ -131,7 +136,7 @@ pub(crate) fn static_join_to_fstring(checker: &mut Checker, expr: &Expr, joiner:
// Try to build the fstring (internally checks whether e.g. the elements are
// convertible to f-string elements).
let Some(new_expr) = build_fstring(joiner, joinees) else {
let Some(new_expr) = build_fstring(joiner, joinees, checker.allocator()) else {
return;
};

View File

@ -14,7 +14,7 @@ use crate::rules::isort::helpers;
#[derive(Debug, Default)]
pub(crate) struct Block<'a> {
pub(crate) nested: bool,
pub(crate) imports: Vec<&'a Stmt>,
pub(crate) imports: Vec<&'a Stmt<'a>>,
pub(crate) trailer: Option<Trailer>,
}
@ -55,7 +55,7 @@ impl<'a> BlockBuilder<'a> {
}
}
fn track_import(&mut self, stmt: &'a Stmt) {
fn track_import(&mut self, stmt: &'a Stmt<'a>) {
let index = self.blocks.len() - 1;
self.blocks[index].imports.push(stmt);
self.blocks[index].nested = self.nested;
@ -120,8 +120,8 @@ impl<'a> BlockBuilder<'a> {
}
}
impl<'a> StatementVisitor<'a> for BlockBuilder<'a> {
fn visit_stmt(&mut self, stmt: &'a Stmt) {
impl<'a> StatementVisitor<'a, 'a> for BlockBuilder<'a> {
fn visit_stmt(&mut self, stmt: &'a Stmt<'a>) {
// Track manual splits (e.g., `# isort: split`).
if self
.splits
@ -273,7 +273,7 @@ impl<'a> StatementVisitor<'a> for BlockBuilder<'a> {
self.nested = prev_nested;
}
fn visit_except_handler(&mut self, except_handler: &'a ExceptHandler) {
fn visit_except_handler(&mut self, except_handler: &'a ExceptHandler<'a>) {
let prev_nested = self.nested;
self.nested = true;
@ -287,14 +287,14 @@ impl<'a> StatementVisitor<'a> for BlockBuilder<'a> {
self.nested = prev_nested;
}
fn visit_match_case(&mut self, match_case: &'a MatchCase) {
fn visit_match_case(&mut self, match_case: &'a MatchCase<'a>) {
for stmt in &match_case.body {
self.visit_stmt(stmt);
}
self.finalize(None);
}
fn visit_elif_else_clause(&mut self, elif_else_clause: &'a ElifElseClause) {
fn visit_elif_else_clause(&mut self, elif_else_clause: &'a ElifElseClause<'a>) {
for stmt in &elif_else_clause.body {
self.visit_stmt(stmt);
}

View File

@ -176,14 +176,14 @@ pub(crate) fn function_is_too_complex(
#[cfg(test)]
mod tests {
use anyhow::Result;
use ruff_allocator::Allocator;
use ruff_python_ast::Suite;
use ruff_python_parser::parse_module;
use super::get_complexity_number;
fn parse_suite(source: &str) -> Result<Suite> {
Ok(parse_module(source)?.into_suite())
fn parse_suite<'a>(source: &str, allocator: &'a Allocator) -> Result<Suite<'a>> {
Ok(parse_module(source, allocator)?.into_suite())
}
#[test]
@ -192,7 +192,8 @@ mod tests {
def trivial():
pass
";
let stmts = parse_suite(source)?;
let allocator = Allocator::new();
let stmts = parse_suite(source, &allocator)?;
assert_eq!(get_complexity_number(&stmts), 1);
Ok(())
}
@ -203,7 +204,8 @@ def trivial():
def expr_as_statement():
0xF00D
";
let stmts = parse_suite(source)?;
let allocator = Allocator::new();
let stmts = parse_suite(source, &allocator)?;
assert_eq!(get_complexity_number(&stmts), 1);
Ok(())
}
@ -216,7 +218,8 @@ def sequential(n):
s = k + n
return s
";
let stmts = parse_suite(source)?;
let allocator = Allocator::new();
let stmts = parse_suite(source, &allocator)?;
assert_eq!(get_complexity_number(&stmts), 1);
Ok(())
}
@ -232,7 +235,8 @@ def if_elif_else_dead_path(n):
else:
return "smaller than or equal to three"
"#;
let stmts = parse_suite(source)?;
let allocator = Allocator::new();
let stmts = parse_suite(source, &allocator)?;
assert_eq!(get_complexity_number(&stmts), 3);
Ok(())
}
@ -249,7 +253,8 @@ def nested_ifs():
else:
return "smaller than or equal to three"
"#;
let stmts = parse_suite(source)?;
let allocator = Allocator::new();
let stmts = parse_suite(source, &allocator)?;
assert_eq!(get_complexity_number(&stmts), 3);
Ok(())
}
@ -261,7 +266,8 @@ def for_loop():
for i in range(10):
print(i)
";
let stmts = parse_suite(source)?;
let allocator = Allocator::new();
let stmts = parse_suite(source, &allocator)?;
assert_eq!(get_complexity_number(&stmts), 2);
Ok(())
}
@ -275,7 +281,8 @@ def for_else(mylist):
else:
print(None)
";
let stmts = parse_suite(source)?;
let allocator = Allocator::new();
let stmts = parse_suite(source, &allocator)?;
assert_eq!(get_complexity_number(&stmts), 2);
Ok(())
}
@ -289,7 +296,8 @@ def recursive(n):
else:
return n
";
let stmts = parse_suite(source)?;
let allocator = Allocator::new();
let stmts = parse_suite(source, &allocator)?;
assert_eq!(get_complexity_number(&stmts), 2);
Ok(())
}
@ -306,7 +314,8 @@ def nested_functions():
a()
";
let stmts = parse_suite(source)?;
let allocator = Allocator::new();
let stmts = parse_suite(source, &allocator)?;
assert_eq!(get_complexity_number(&stmts), 3);
Ok(())
}
@ -324,7 +333,8 @@ def try_else():
else:
print(4)
";
let stmts = parse_suite(source)?;
let allocator = Allocator::new();
let stmts = parse_suite(source, &allocator)?;
assert_eq!(get_complexity_number(&stmts), 4);
Ok(())
}
@ -341,7 +351,8 @@ def nested_try_finally():
finally:
print(3)
";
let stmts = parse_suite(source)?;
let allocator = Allocator::new();
let stmts = parse_suite(source, &allocator)?;
assert_eq!(get_complexity_number(&stmts), 1);
Ok(())
}
@ -358,7 +369,8 @@ async def foobar(a, b, c):
async for x in a:
pass
";
let stmts = parse_suite(source)?;
let allocator = Allocator::new();
let stmts = parse_suite(source, &allocator)?;
assert_eq!(get_complexity_number(&stmts), 3);
Ok(())
}
@ -369,7 +381,8 @@ async def foobar(a, b, c):
def annotated_assign():
x: Any = None
";
let stmts = parse_suite(source)?;
let allocator = Allocator::new();
let stmts = parse_suite(source, &allocator)?;
assert_eq!(get_complexity_number(&stmts), 1);
Ok(())
}
@ -405,7 +418,8 @@ class Class:
return ServiceProvider(Logger())
";
let stmts = parse_suite(source)?;
let allocator = Allocator::new();
let stmts = parse_suite(source, &allocator)?;
assert_eq!(get_complexity_number(&stmts), 9);
Ok(())
}
@ -419,7 +433,8 @@ def process_detect_lines():
finally:
pass
";
let stmts = parse_suite(source)?;
let allocator = Allocator::new();
let stmts = parse_suite(source, &allocator)?;
assert_eq!(get_complexity_number(&stmts), 1);
Ok(())
}
@ -434,7 +449,8 @@ def process_detect_lines():
if res:
errors.append(f"Non-zero exit code {res}")
"#;
let stmts = parse_suite(source)?;
let allocator = Allocator::new();
let stmts = parse_suite(source, &allocator)?;
assert_eq!(get_complexity_number(&stmts), 2);
Ok(())
}
@ -447,7 +463,8 @@ def with_lock():
if foo:
print('bar')
";
let stmts = parse_suite(source)?;
let allocator = Allocator::new();
let stmts = parse_suite(source, &allocator)?;
assert_eq!(get_complexity_number(&stmts), 2);
Ok(())
}
@ -462,7 +479,8 @@ def f():
case _:
print('bar')
";
let stmts = parse_suite(source)?;
let allocator = Allocator::new();
let stmts = parse_suite(source, &allocator)?;
assert_eq!(get_complexity_number(&stmts), 2);
Ok(())
}
@ -479,7 +497,8 @@ def f():
case _:
print('baz')
";
let stmts = parse_suite(source)?;
let allocator = Allocator::new();
let stmts = parse_suite(source, &allocator)?;
assert_eq!(get_complexity_number(&stmts), 3);
Ok(())
}
@ -494,7 +513,8 @@ def f():
case x:
print(x)
";
let stmts = parse_suite(source)?;
let allocator = Allocator::new();
let stmts = parse_suite(source, &allocator)?;
assert_eq!(get_complexity_number(&stmts), 2);
Ok(())
}
@ -509,7 +529,8 @@ def f():
case 5 | _:
print(x)
";
let stmts = parse_suite(source)?;
let allocator = Allocator::new();
let stmts = parse_suite(source, &allocator)?;
assert_eq!(get_complexity_number(&stmts), 2);
Ok(())
}

View File

@ -764,7 +764,7 @@ fn is_guarded_by_try_except(
try_block_contains_undeprecated_attribute(try_node, &replacement.details, semantic)
}
Expr::Name(ast::ExprName { id, .. }) => {
let Some(binding_id) = semantic.lookup_symbol(id.as_str()) else {
let Some(binding_id) = semantic.lookup_symbol(id) else {
return false;
};
let binding = semantic.binding(binding_id);
@ -840,7 +840,7 @@ impl<'a> AttributeSearcher<'a> {
}
}
impl Visitor<'_> for AttributeSearcher<'_> {
impl Visitor<'_, '_> for AttributeSearcher<'_> {
fn visit_expr(&mut self, expr: &'_ Expr) {
if self.found_attribute {
return;
@ -911,7 +911,7 @@ impl<'a> ImportSearcher<'a> {
}
}
impl StatementVisitor<'_> for ImportSearcher<'_> {
impl StatementVisitor<'_, '_> for ImportSearcher<'_> {
fn visit_stmt(&mut self, stmt: &ast::Stmt) {
if self.found_import {
return;

View File

@ -37,7 +37,7 @@ pub(super) fn test_expression(expr: &Expr, semantic: &SemanticModel) -> Resoluti
match &semantic.binding(id).kind {
BindingKind::Argument => {
// Avoid, e.g., `self.values`.
if matches!(name.id.as_str(), "self" | "cls") {
if matches!(name.id, "self" | "cls") {
Resolution::IrrelevantBinding
} else {
Resolution::RelevantLocal

View File

@ -45,7 +45,7 @@ pub(crate) fn assignment_to_df(targets: &[Expr]) -> Option<Diagnostic> {
let Expr::Name(ast::ExprName { id, .. }) = target else {
return None;
};
if id != "df" {
if *id != "df" {
return None;
}
Some(Diagnostic::new(PandasDfVariableName, target.range()))

View File

@ -58,7 +58,7 @@ impl Violation for PandasUseOfPdMerge {
pub(crate) fn use_of_pd_merge(checker: &mut Checker, func: &Expr) {
if let Expr::Attribute(ast::ExprAttribute { attr, value, .. }) = func {
if let Expr::Name(ast::ExprName { id, .. }) = value.as_ref() {
if id == "pd" && attr == "merge" {
if *id == "pd" && attr == "merge" {
checker
.diagnostics
.push(Diagnostic::new(PandasUseOfPdMerge, func.range()));

View File

@ -56,7 +56,7 @@ pub(crate) fn error_suffix_on_exception_name(
if !arguments.is_some_and(|arguments| {
arguments.args.iter().any(|base| {
if let Expr::Name(ast::ExprName { id, .. }) = &base {
id == "Exception" || id.ends_with("Error")
*id == "Exception" || id.ends_with("Error")
} else {
false
}

View File

@ -147,8 +147,8 @@ impl<'a> MutationVisitor<'a> {
}
}
impl<'a> StatementVisitor<'a> for MutationVisitor<'a> {
fn visit_stmt(&mut self, stmt: &'a Stmt) {
impl<'a> StatementVisitor<'a, 'a> for MutationVisitor<'a> {
fn visit_stmt(&mut self, stmt: &'a Stmt<'a>) {
if match_mutation(stmt, self.target) {
self.is_mutated = true;
} else {
@ -179,13 +179,13 @@ fn match_mutation(stmt: &Stmt, id: &str) -> bool {
let Some(ast::ExprName { id: target_id, .. }) = value.as_name_expr() else {
return false;
};
target_id == id
*target_id == id
}
// Ex) `foo[0] = bar`
Stmt::Assign(ast::StmtAssign { targets, .. }) => targets.iter().any(|target| {
if let Some(ast::ExprSubscript { value: target, .. }) = target.as_subscript_expr() {
if let Some(ast::ExprName { id: target_id, .. }) = target.as_name_expr() {
return target_id == id;
return *target_id == id;
}
}
false
@ -193,7 +193,7 @@ fn match_mutation(stmt: &Stmt, id: &str) -> bool {
// Ex) `foo += bar`
Stmt::AugAssign(ast::StmtAugAssign { target, .. }) => {
if let Some(ast::ExprName { id: target_id, .. }) = target.as_name_expr() {
target_id == id
*target_id == id
} else {
false
}
@ -202,7 +202,7 @@ fn match_mutation(stmt: &Stmt, id: &str) -> bool {
Stmt::AnnAssign(ast::StmtAnnAssign { target, .. }) => {
if let Some(ast::ExprSubscript { value: target, .. }) = target.as_subscript_expr() {
if let Some(ast::ExprName { id: target_id, .. }) = target.as_name_expr() {
return target_id == id;
return *target_id == id;
}
}
false
@ -211,7 +211,7 @@ fn match_mutation(stmt: &Stmt, id: &str) -> bool {
Stmt::Delete(ast::StmtDelete { targets, .. }) => targets.iter().any(|target| {
if let Some(ast::ExprSubscript { value: target, .. }) = target.as_subscript_expr() {
if let Some(ast::ExprName { id: target_id, .. }) = target.as_name_expr() {
return target_id == id;
return *target_id == id;
}
}
false

View File

@ -1,3 +1,4 @@
use ruff_allocator::{Allocator, CloneIn};
use ruff_diagnostics::{Diagnostic, Edit, Fix, FixAvailability, Violation};
use ruff_macros::{derive_message_formats, violation};
use ruff_python_ast::{
@ -92,6 +93,7 @@ pub(crate) fn lambda_assignment(
annotation,
checker.semantic(),
checker.generator(),
checker.allocator(),
)
.universal_newlines()
.enumerate()
@ -146,7 +148,11 @@ pub(crate) fn lambda_assignment(
/// The `Callable` import can be from either `collections.abc` or `typing`.
/// If an ellipsis is used for the argument types, an empty list is returned.
/// The returned values are cloned, so they can be used as-is.
fn extract_types(annotation: &Expr, semantic: &SemanticModel) -> Option<(Vec<Expr>, Expr)> {
fn extract_types<'ast>(
annotation: &Expr,
semantic: &SemanticModel,
allocator: &'ast Allocator,
) -> Option<(Vec<Expr<'ast>>, Expr<'ast>)> {
let Expr::Subscript(ast::ExprSubscript { value, slice, .. }) = &annotation else {
return None;
};
@ -172,13 +178,16 @@ fn extract_types(annotation: &Expr, semantic: &SemanticModel) -> Option<(Vec<Exp
// The first argument to `Callable` must be a list of types, parameter
// specification, or ellipsis.
let params = match param_types {
Expr::List(ast::ExprList { elts, .. }) => elts.clone(),
Expr::List(ast::ExprList { elts, .. }) => elts
.into_iter()
.map(|element| element.clone_in(allocator))
.collect(),
Expr::EllipsisLiteral(_) => vec![],
_ => return None,
};
// The second argument to `Callable` must be a type.
let return_type = return_type.clone();
let return_type = return_type.clone_in(allocator);
Some((params, return_type))
}
@ -191,9 +200,13 @@ fn function(
annotation: Option<&Expr>,
semantic: &SemanticModel,
generator: Generator,
allocator: &Allocator,
) -> String {
let body = Stmt::Return(ast::StmtReturn {
value: Some(Box::new(body.clone())),
value: Some(ruff_allocator::Box::new_in(
body.clone_in(allocator),
allocator,
)),
range: TextRange::default(),
});
let parameters = parameters.cloned().unwrap_or_default();
@ -207,9 +220,9 @@ fn function(
.enumerate()
.map(|(idx, parameter)| ParameterWithDefault {
parameter: Parameter {
annotation: arg_types
.get(idx)
.map(|arg_type| Box::new(arg_type.clone())),
annotation: arg_types.get(idx).map(|arg_type| {
ruff_allocator::Box::new_in(arg_type.clone_in(allocator), allocator)
}),
..parameter.parameter.clone()
},
..parameter.clone()
@ -221,9 +234,9 @@ fn function(
.enumerate()
.map(|(idx, parameter)| ParameterWithDefault {
parameter: Parameter {
annotation: arg_types
.get(idx + new_posonlyargs.len())
.map(|arg_type| Box::new(arg_type.clone())),
annotation: arg_types.get(idx + new_posonlyargs.len()).map(|arg_type| {
ruff_allocator::Box::new_in(arg_type.clone_in(allocator), allocator)
}),
..parameter.parameter.clone()
},
..parameter.clone()

View File

@ -472,6 +472,7 @@ struct Line {
#[cfg(test)]
mod tests {
use ruff_allocator::Allocator;
use ruff_python_parser::parse_module;
use ruff_source_file::Locator;
@ -556,7 +557,8 @@ if False:
}
fn assert_logical_lines(contents: &str, expected: &[&str]) {
let parsed = parse_module(contents).unwrap();
let allocator = Allocator::new();
let parsed = parse_module(contents, &allocator).unwrap();
let locator = Locator::new(contents);
let actual: Vec<String> = LogicalLines::from_tokens(parsed.tokens(), &locator)
.into_iter()

View File

@ -78,7 +78,7 @@ fn is_type(expr: &Expr, semantic: &SemanticModel) -> bool {
Expr::Name(ast::ExprName { id, .. }) => {
// Ex) `type(obj) == int`
matches!(
id.as_str(),
*id,
"bool"
| "bytearray"
| "bytes"

View File

@ -384,7 +384,7 @@ struct BodyVisitor<'a> {
}
impl<'a> BodyVisitor<'a> {
fn new(semantic: &'a SemanticModel) -> Self {
fn new(semantic: &'a SemanticModel<'a>) -> Self {
Self {
returns: Vec::new(),
raised_exceptions: Vec::new(),
@ -400,8 +400,8 @@ impl<'a> BodyVisitor<'a> {
}
}
impl<'a> StatementVisitor<'a> for BodyVisitor<'a> {
fn visit_stmt(&mut self, stmt: &'a Stmt) {
impl<'a> StatementVisitor<'a, 'a> for BodyVisitor<'a> {
fn visit_stmt(&mut self, stmt: &'a Stmt<'a>) {
match stmt {
Stmt::Raise(ast::StmtRaise { exc: Some(exc), .. }) => {
if let Some(qualified_name) = extract_raised_exception(self.semantic, exc.as_ref())

View File

@ -2014,10 +2014,10 @@ fn parse_numpy_sections(
}
}
fn parse_google_sections(
fn parse_google_sections<'a>(
checker: &mut Checker,
docstring: &Docstring,
section_contexts: &SectionContexts,
section_contexts: &SectionContexts<'a>,
) {
let mut iterator = section_contexts.iter().peekable();
while let Some(context) = iterator.next() {

View File

@ -13,11 +13,11 @@ mod tests {
use regex::Regex;
use rustc_hash::FxHashMap;
use test_case::test_case;
use ruff_allocator::Allocator;
use ruff_python_ast::PySourceType;
use ruff_python_codegen::Stylist;
use ruff_python_index::Indexer;
use test_case::test_case;
use ruff_python_trivia::textwrap::dedent;
use ruff_source_file::Locator;
@ -681,8 +681,12 @@ mod tests {
let source_type = PySourceType::default();
let source_kind = SourceKind::Python(contents.to_string());
let settings = LinterSettings::for_rules(Linter::Pyflakes.rules());
let parsed =
ruff_python_parser::parse_unchecked_source(source_kind.source_code(), source_type);
let allocator = Allocator::new();
let parsed = ruff_python_parser::parse_unchecked_source(
source_kind.source_code(),
source_type,
&allocator,
);
let locator = Locator::new(&contents);
let stylist = Stylist::from_tokens(parsed.tokens(), &locator);
let indexer = Indexer::from_tokens(parsed.tokens(), &locator);

View File

@ -31,8 +31,8 @@ impl Violation for BreakOutsideLoop {
/// F701
pub(crate) fn break_outside_loop<'a>(
stmt: &'a Stmt,
parents: &mut impl Iterator<Item = &'a Stmt>,
stmt: &Stmt<'a>,
parents: &mut impl Iterator<Item = &'a Stmt<'a>>,
) -> Option<Diagnostic> {
let mut child = stmt;
for parent in parents {

View File

@ -31,8 +31,8 @@ impl Violation for ContinueOutsideLoop {
/// F702
pub(crate) fn continue_outside_loop<'a>(
stmt: &'a Stmt,
parents: &mut impl Iterator<Item = &'a Stmt>,
stmt: &'a Stmt<'a>,
parents: &mut impl Iterator<Item = &'a Stmt<'a>>,
) -> Option<Diagnostic> {
let mut child = stmt;
for parent in parents {

View File

@ -237,7 +237,7 @@ impl LocatedCmpOp {
#[cfg(test)]
mod tests {
use anyhow::Result;
use ruff_allocator::Allocator;
use ruff_python_ast::CmpOp;
use ruff_python_parser::parse_expression;
use ruff_text_size::TextSize;
@ -245,7 +245,8 @@ mod tests {
use super::{locate_cmp_ops, LocatedCmpOp};
fn extract_cmp_op_locations(source: &str) -> Result<Vec<LocatedCmpOp>> {
let parsed = parse_expression(source)?;
let allocator = Allocator::new();
let parsed = parse_expression(source, &allocator)?;
Ok(locate_cmp_ops(parsed.expr(), parsed.tokens()))
}

View File

@ -50,17 +50,17 @@ impl Violation for RaiseNotImplemented {
}
}
fn match_not_implemented(expr: &Expr) -> Option<&Expr> {
fn match_not_implemented<'a, 'ast>(expr: &'a Expr<'ast>) -> Option<&'a Expr<'ast>> {
match expr {
Expr::Call(ast::ExprCall { func, .. }) => {
if let Expr::Name(ast::ExprName { id, .. }) = func.as_ref() {
if id == "NotImplemented" {
if *id == "NotImplemented" {
return Some(func);
}
}
}
Expr::Name(ast::ExprName { id, .. }) => {
if id == "NotImplemented" {
if *id == "NotImplemented" {
return Some(expr);
}
}

View File

@ -238,7 +238,7 @@ fn is_first_party(qualified_name: &str, level: u32, checker: &Checker) -> bool {
}
/// Find the `Expr` for top level `__all__` bindings.
fn find_dunder_all_exprs<'a>(semantic: &'a SemanticModel) -> Vec<&'a ast::Expr> {
fn find_dunder_all_exprs<'a>(semantic: &'a SemanticModel<'a>) -> Vec<&'a ast::Expr<'a>> {
semantic
.global_scope()
.get_all("__all__")

View File

@ -10,7 +10,7 @@ use ruff_text_size::TextRange;
use crate::settings::LinterSettings;
/// Returns the value of the `name` parameter to, e.g., a `TypeVar` constructor.
pub(super) fn type_param_name(arguments: &Arguments) -> Option<&str> {
pub(super) fn type_param_name<'a>(arguments: &'a Arguments) -> Option<&'a str> {
// Handle both `TypeVar("T")` and `TypeVar(name="T")`.
let name_param = arguments.find_argument("name", 0)?;
if let Expr::StringLiteral(ast::ExprStringLiteral { value, .. }) = &name_param {
@ -143,7 +143,7 @@ impl SequenceIndexVisitor<'_> {
}
}
impl<'a> Visitor<'_> for SequenceIndexVisitor<'a> {
impl<'a> Visitor<'_, '_> for SequenceIndexVisitor<'a> {
fn visit_stmt(&mut self, stmt: &Stmt) {
if self.modified {
return;

View File

@ -106,7 +106,7 @@ fn is_open(func: &Expr, semantic: &SemanticModel) -> Option<Kind> {
}
/// Returns the mode argument, if present.
fn extract_mode(call: &ast::ExprCall, kind: Kind) -> Option<&Expr> {
fn extract_mode<'a, 'ast>(call: &'a ast::ExprCall<'ast>, kind: Kind) -> Option<&'a Expr<'ast>> {
match kind {
Kind::Builtin => call.arguments.find_argument("mode", 1),
Kind::Pathlib => call.arguments.find_argument("mode", 0),

View File

@ -52,11 +52,11 @@ impl Violation for ComparisonOfConstant {
}
/// PLR0133
pub(crate) fn comparison_of_constant(
pub(crate) fn comparison_of_constant<'a>(
checker: &mut Checker,
left: &Expr,
left: &Expr<'a>,
ops: &[CmpOp],
comparators: &[Expr],
comparators: &[Expr<'a>],
) {
for ((left, right), op) in std::iter::once(left)
.chain(comparators.iter())

View File

@ -95,15 +95,15 @@ pub(crate) fn dict_index_missing_items(checker: &mut Checker, stmt_for: &ast::St
/// A visitor to detect subscript operations on a target dictionary.
struct SubscriptVisitor<'a> {
/// The target of the for loop (e.g., `key` in `for key in obj:`).
target: &'a Expr,
target: &'a Expr<'a>,
/// The name of the iterated object (e.g., `obj` in `for key in obj:`).
dict_name: &'a ast::ExprName,
dict_name: &'a ast::ExprName<'a>,
/// Whether a violation has been detected.
has_violation: bool,
}
impl<'a> SubscriptVisitor<'a> {
fn new(target: &'a Expr, dict_name: &'a ast::ExprName) -> Self {
fn new(target: &'a Expr<'a>, dict_name: &'a ast::ExprName<'a>) -> Self {
Self {
target,
dict_name,
@ -112,8 +112,8 @@ impl<'a> SubscriptVisitor<'a> {
}
}
impl<'a> Visitor<'a> for SubscriptVisitor<'a> {
fn visit_expr(&mut self, expr: &'a Expr) {
impl<'a> Visitor<'a, 'a> for SubscriptVisitor<'a> {
fn visit_expr(&mut self, expr: &'a Expr<'a>) {
// Given `obj[key]`, `value` must be `obj` and `slice` must be `key`.
if let Expr::Subscript(ast::ExprSubscript {
value,
@ -144,7 +144,7 @@ impl<'a> Visitor<'a> for SubscriptVisitor<'a> {
}
/// Extracts the name of the dictionary from the expression.
fn extract_dict_name(expr: &Expr) -> Option<&ast::ExprName> {
fn extract_dict_name<'a>(expr: &'a Expr<'a>) -> Option<&'a ast::ExprName<'a>> {
// Ex) `for key in obj:`
if let Some(name_expr) = expr.as_name_expr() {
return Some(name_expr);

View File

@ -58,7 +58,7 @@ impl Violation for MagicValueComparison {
}
/// If an [`Expr`] is a literal (or unary operation on a literal), return the [`LiteralExpressionRef`].
fn as_literal(expr: &Expr) -> Option<LiteralExpressionRef<'_>> {
fn as_literal<'a, 'ast>(expr: &'a Expr<'ast>) -> Option<LiteralExpressionRef<'a, 'ast>> {
match expr {
Expr::UnaryOp(ast::ExprUnaryOp {
op: UnaryOp::UAdd | UnaryOp::USub | UnaryOp::Invert,
@ -96,7 +96,11 @@ fn is_magic_value(literal_expr: LiteralExpressionRef, allowed_types: &[ConstantT
}
/// PLR2004
pub(crate) fn magic_value_comparison(checker: &mut Checker, left: &Expr, comparators: &[Expr]) {
pub(crate) fn magic_value_comparison<'a>(
checker: &mut Checker,
left: &Expr<'a>,
comparators: &[Expr<'a>],
) {
for (left, right) in std::iter::once(left)
.chain(comparators.iter())
.tuple_windows()

View File

@ -48,7 +48,7 @@ impl Violation for NanComparison {
}
/// PLW0177
pub(crate) fn nan_comparison(checker: &mut Checker, left: &Expr, comparators: &[Expr]) {
pub(crate) fn nan_comparison<'a>(checker: &mut Checker, left: &Expr<'a>, comparators: &[Expr<'a>]) {
for expr in std::iter::once(left).chain(comparators.iter()) {
if let Some(qualified_name) = checker.semantic().resolve_qualified_name(expr) {
match qualified_name.segments() {

View File

@ -1,3 +1,4 @@
use ruff_allocator::{Allocator, CloneIn};
use ruff_python_ast::{self as ast, Arguments, Expr, Keyword};
use ruff_text_size::{Ranged, TextRange};
@ -87,8 +88,19 @@ impl std::fmt::Display for MinMax {
/// Collect a new set of arguments to by either accepting existing args as-is or
/// collecting child arguments, if it's a call to the same function.
fn collect_nested_args(min_max: MinMax, args: &[Expr], semantic: &SemanticModel) -> Vec<Expr> {
fn inner(min_max: MinMax, args: &[Expr], semantic: &SemanticModel, new_args: &mut Vec<Expr>) {
fn collect_nested_args<'ast>(
min_max: MinMax,
args: &[Expr],
semantic: &SemanticModel,
allocator: &'ast Allocator,
) -> Vec<Expr<'ast>> {
fn inner<'ast>(
min_max: MinMax,
args: &[Expr],
semantic: &SemanticModel,
new_args: &mut Vec<Expr>,
allocator: &'ast Allocator,
) {
for arg in args {
if let Expr::Call(ast::ExprCall {
func,
@ -104,7 +116,7 @@ fn collect_nested_args(min_max: MinMax, args: &[Expr], semantic: &SemanticModel)
if let [arg] = &**args {
if arg.as_starred_expr().is_none() {
let new_arg = Expr::Starred(ast::ExprStarred {
value: Box::new(arg.clone()),
value: ruff_allocator::Box::new_in(arg.clone_in(allocator), allocator),
ctx: ast::ExprContext::Load,
range: TextRange::default(),
});
@ -113,16 +125,16 @@ fn collect_nested_args(min_max: MinMax, args: &[Expr], semantic: &SemanticModel)
}
}
if MinMax::try_from_call(func, keywords, semantic) == Some(min_max) {
inner(min_max, args, semantic, new_args);
inner(min_max, args, semantic, new_args, allocator);
continue;
}
}
new_args.push(arg.clone());
new_args.push(arg.clone_in(allocator));
}
}
let mut new_args = Vec::with_capacity(args.len());
inner(min_max, args, semantic, &mut new_args);
inner(min_max, args, semantic, &mut new_args, allocator);
new_args
}
@ -132,7 +144,7 @@ pub(crate) fn nested_min_max(
expr: &Expr,
func: &Expr,
args: &[Expr],
keywords: &[Keyword],
keywords: &mut [Keyword],
) {
let Some(min_max) = MinMax::try_from_call(func, keywords, checker.semantic()) else {
return;
@ -160,10 +172,20 @@ pub(crate) fn nested_min_max(
.has_comments(expr, checker.locator())
{
let flattened_expr = Expr::Call(ast::ExprCall {
func: Box::new(func.clone()),
func: ruff_allocator::Box::new_in(
func.clone_in(checker.allocator()),
checker.allocator(),
),
arguments: Arguments {
args: collect_nested_args(min_max, args, checker.semantic()).into_boxed_slice(),
keywords: Box::from(keywords),
args: checker
.allocator()
.alloc_slice_fill_iter(collect_nested_args(
min_max,
args,
checker.semantic(),
checker.allocator(),
)),
keywords,
range: TextRange::default(),
},
range: TextRange::default(),

View File

@ -102,7 +102,7 @@ fn get_undecorated_methods(checker: &mut Checker, class_stmt: &Stmt, method_type
return;
};
let mut explicit_decorator_calls: HashMap<Name, &Stmt> = HashMap::default();
let mut explicit_decorator_calls: HashMap<&str, &Stmt> = HashMap::default();
let (method_name, diagnostic_type): (&str, DiagnosticKind) = match method_type {
MethodType::Classmethod => ("classmethod", NoClassmethodDecorator.into()),
@ -117,7 +117,7 @@ fn get_undecorated_methods(checker: &mut Checker, class_stmt: &Stmt, method_type
}) = value.as_ref()
{
if let Expr::Name(ast::ExprName { id, .. }) = func.as_ref() {
if id == method_name && checker.semantic().has_builtin_binding(method_name) {
if *id == method_name && checker.semantic().has_builtin_binding(method_name) {
if arguments.args.len() != 1 {
continue;
}
@ -133,7 +133,7 @@ fn get_undecorated_methods(checker: &mut Checker, class_stmt: &Stmt, method_type
if let Expr::Name(ast::ExprName { id, .. }) = &arguments.args[0] {
if target_name == *id {
explicit_decorator_calls.insert(id.clone(), stmt);
explicit_decorator_calls.insert(id, stmt);
}
};
}
@ -160,7 +160,7 @@ fn get_undecorated_methods(checker: &mut Checker, class_stmt: &Stmt, method_type
// if we find the decorator we're looking for, skip
if decorator_list.iter().any(|decorator| {
if let Expr::Name(ast::ExprName { id, .. }) = &decorator.expression {
if id == method_name && checker.semantic().has_builtin_binding(method_name) {
if *id == method_name && checker.semantic().has_builtin_binding(method_name) {
return true;
}
}

View File

@ -1,4 +1,5 @@
use ast::{Expr, StmtAugAssign};
use ruff_allocator::{Allocator, CloneIn};
use ruff_diagnostics::{AlwaysFixableViolation, Diagnostic, Edit, Fix};
use ruff_macros::{derive_message_formats, violation};
use ruff_python_ast as ast;
@ -140,13 +141,14 @@ fn augmented_assignment(
operator: Operator,
right_operand: &Expr,
range: TextRange,
allocator: &Allocator,
) -> Edit {
Edit::range_replacement(
generator.stmt(&ast::Stmt::AugAssign(StmtAugAssign {
range: TextRange::default(),
target: Box::new(target.clone()),
target: ruff_allocator::Box::new_in(target.clone_in(allocator), allocator),
op: operator,
value: Box::new(right_operand.clone()),
value: ruff_allocator::Box::new_in(right_operand.clone_in(allocator), allocator),
})),
range,
)

View File

@ -100,7 +100,7 @@ impl Ranged for AttributeAssignment<'_> {
/// Return a list of attributes that are assigned to but not included in `__slots__`.
///
/// If the `__slots__` attribute cannot be statically determined, returns an empty vector.
fn is_attributes_not_in_slots(body: &[Stmt]) -> Vec<AttributeAssignment> {
fn is_attributes_not_in_slots<'a>(body: &[Stmt<'a>]) -> Vec<AttributeAssignment<'a>> {
// First, collect all the attributes that are assigned to `__slots__`.
let mut slots = FxHashSet::default();
for statement in body {
@ -111,7 +111,7 @@ fn is_attributes_not_in_slots(body: &[Stmt]) -> Vec<AttributeAssignment> {
continue;
};
if id == "__slots__" {
if *id == "__slots__" {
for attribute in slots_attributes(value) {
if let Some(attribute) = attribute {
slots.insert(attribute);
@ -132,7 +132,7 @@ fn is_attributes_not_in_slots(body: &[Stmt]) -> Vec<AttributeAssignment> {
continue;
};
if id == "__slots__" {
if *id == "__slots__" {
for attribute in slots_attributes(value) {
if let Some(attribute) = attribute {
slots.insert(attribute);
@ -149,7 +149,7 @@ fn is_attributes_not_in_slots(body: &[Stmt]) -> Vec<AttributeAssignment> {
continue;
};
if id == "__slots__" {
if *id == "__slots__" {
for attribute in slots_attributes(value) {
if let Some(attribute) = attribute {
slots.insert(attribute);
@ -184,7 +184,7 @@ fn is_attributes_not_in_slots(body: &[Stmt]) -> Vec<AttributeAssignment> {
let Some(ast::ExprName { id, .. }) = value.as_name_expr() else {
continue;
};
slots.insert(id.as_str());
slots.insert(id);
}
}
}
@ -207,7 +207,7 @@ fn is_attributes_not_in_slots(body: &[Stmt]) -> Vec<AttributeAssignment> {
let Expr::Name(ast::ExprName { id, .. }) = attribute.value.as_ref() else {
continue;
};
if id == "self" && !slots.contains(attribute.attr.as_str()) {
if *id == "self" && !slots.contains(attribute.attr.as_str()) {
assignments.push(AttributeAssignment {
name: &attribute.attr,
range: attribute.range(),
@ -223,7 +223,7 @@ fn is_attributes_not_in_slots(body: &[Stmt]) -> Vec<AttributeAssignment> {
let Expr::Name(ast::ExprName { id, .. }) = attribute.value.as_ref() else {
continue;
};
if id == "self" && !slots.contains(attribute.attr.as_str()) {
if *id == "self" && !slots.contains(attribute.attr.as_str()) {
assignments.push(AttributeAssignment {
name: &attribute.attr,
range: attribute.range(),
@ -239,7 +239,7 @@ fn is_attributes_not_in_slots(body: &[Stmt]) -> Vec<AttributeAssignment> {
let Expr::Name(ast::ExprName { id, .. }) = attribute.value.as_ref() else {
continue;
};
if id == "self" && !slots.contains(attribute.attr.as_str()) {
if *id == "self" && !slots.contains(attribute.attr.as_str()) {
assignments.push(AttributeAssignment {
name: &attribute.attr,
range: attribute.range(),
@ -259,7 +259,7 @@ fn is_attributes_not_in_slots(body: &[Stmt]) -> Vec<AttributeAssignment> {
/// Return an iterator over the attributes enumerated in the given `__slots__` value.
///
/// If an attribute can't be statically determined, it will be `None`.
fn slots_attributes(expr: &Expr) -> impl Iterator<Item = Option<&str>> {
fn slots_attributes<'a>(expr: &'a Expr<'a>) -> impl Iterator<Item = Option<&'a str>> {
// Ex) `__slots__ = ("name",)`
let elts_iter = match expr {
Expr::Tuple(ast::ExprTuple { elts, .. })

View File

@ -43,14 +43,14 @@ impl Violation for RedeclaredAssignedName {
/// PLW0128
pub(crate) fn redeclared_assigned_name(checker: &mut Checker, targets: &Vec<Expr>) {
let mut names: Vec<Name> = Vec::new();
let mut names: Vec<&str> = Vec::new();
for target in targets {
check_expr(checker, target, &mut names);
}
}
fn check_expr(checker: &mut Checker, expr: &Expr, names: &mut Vec<Name>) {
fn check_expr(checker: &mut Checker, expr: &Expr, names: &mut Vec<&str>) {
match expr {
Expr::Tuple(ast::ExprTuple { elts, .. }) => {
for target in elts {
@ -70,7 +70,7 @@ fn check_expr(checker: &mut Checker, expr: &Expr, names: &mut Vec<Name>) {
expr.range(),
));
}
names.push(id.clone());
names.push(id);
}
_ => {}
}

View File

@ -129,12 +129,12 @@ impl PartialEq<InnerBindingKind> for OuterBindingKind {
}
struct ExprWithOuterBindingKind<'a> {
expr: &'a Expr,
expr: &'a Expr<'a>,
binding_kind: OuterBindingKind,
}
struct ExprWithInnerBindingKind<'a> {
expr: &'a Expr,
expr: &'a Expr<'a>,
binding_kind: InnerBindingKind,
}
@ -144,8 +144,8 @@ struct InnerForWithAssignTargetsVisitor<'a, 'b> {
assignment_targets: Vec<ExprWithInnerBindingKind<'a>>,
}
impl<'a, 'b> StatementVisitor<'b> for InnerForWithAssignTargetsVisitor<'a, 'b> {
fn visit_stmt(&mut self, stmt: &'b Stmt) {
impl<'a, 'b> StatementVisitor<'b, 'b> for InnerForWithAssignTargetsVisitor<'a, 'b> {
fn visit_stmt(&mut self, stmt: &'b Stmt<'b>) {
// Collect target expressions.
match stmt {
Stmt::For(ast::StmtFor { target, .. }) => {
@ -260,10 +260,10 @@ fn assignment_is_cast_expr(value: &Expr, target: &Expr, semantic: &SemanticModel
semantic.match_typing_expr(func, "cast")
}
fn assignment_targets_from_expr<'a>(
expr: &'a Expr,
fn assignment_targets_from_expr<'a, 'ast>(
expr: &'a Expr<'ast>,
dummy_variable_rgx: &'a Regex,
) -> Box<dyn Iterator<Item = &'a Expr> + 'a> {
) -> Box<dyn Iterator<Item = &'a Expr<'ast>> + 'a> {
// The Box is necessary to ensure the match arms have the same return type - we can't use
// a cast to "impl Iterator", since at the time of writing that is only allowed for
// return types and argument types.

View File

@ -105,7 +105,7 @@ fn check_expr(checker: &mut Checker, target: &Expr, method_type: MethodType) {
match target {
Expr::Name(_) => {
if let Expr::Name(ast::ExprName { id, .. }) = target {
if id.as_str() == method_type.arg_name() {
if *id == method_type.arg_name() {
checker.diagnostics.push(Diagnostic::new(
SelfOrClsAssignment { method_type },
target.range(),

View File

@ -64,7 +64,7 @@ pub(crate) fn single_string_slots(checker: &mut Checker, class: &StmtClassDef) {
Stmt::Assign(ast::StmtAssign { targets, value, .. }) => {
for target in targets {
if let Expr::Name(ast::ExprName { id, .. }) = target {
if id.as_str() == "__slots__" {
if *id == "__slots__" {
if matches!(value.as_ref(), Expr::StringLiteral(_) | Expr::FString(_)) {
checker
.diagnostics
@ -80,7 +80,7 @@ pub(crate) fn single_string_slots(checker: &mut Checker, class: &StmtClassDef) {
..
}) => {
if let Expr::Name(ast::ExprName { id, .. }) = target.as_ref() {
if id.as_str() == "__slots__" {
if *id == "__slots__" {
if matches!(value.as_ref(), Expr::StringLiteral(_) | Expr::FString(_)) {
checker
.diagnostics

View File

@ -71,11 +71,11 @@ pub(crate) fn super_without_brackets(checker: &mut Checker, func: &Expr) {
return;
};
if id.as_str() != "super" {
if *id != "super" {
return;
}
if !checker.semantic().has_builtin_binding(id.as_str()) {
if !checker.semantic().has_builtin_binding(id) {
return;
}

View File

@ -254,12 +254,14 @@ pub(crate) fn too_many_branches(
#[cfg(test)]
mod tests {
use anyhow::Result;
use ruff_allocator::Allocator;
use ruff_python_parser::parse_module;
use super::num_branches;
fn test_helper(source: &str, expected_num_branches: usize) -> Result<()> {
let parsed = parse_module(source)?;
let allocator = Allocator::new();
let parsed = parse_module(source, &allocator)?;
assert_eq!(num_branches(parsed.suite()), expected_num_branches);
Ok(())
}

View File

@ -98,12 +98,14 @@ pub(crate) fn too_many_return_statements(
#[cfg(test)]
mod tests {
use anyhow::Result;
use ruff_allocator::Allocator;
use ruff_python_parser::parse_module;
use super::num_returns;
fn test_helper(source: &str, expected: usize) -> Result<()> {
let parsed = parse_module(source)?;
let allocator = Allocator::new();
let parsed = parse_module(source, &allocator)?;
assert_eq!(num_returns(parsed.suite()), expected);
Ok(())
}

View File

@ -157,14 +157,14 @@ pub(crate) fn too_many_statements(
#[cfg(test)]
mod tests {
use super::num_statements;
use crate::RuleSelector::All;
use anyhow::Result;
use ruff_allocator::Allocator;
use ruff_python_ast::Suite;
use ruff_python_parser::parse_module;
use super::num_statements;
fn parse_suite(source: &str) -> Result<Suite> {
fn parse_suite<'ast>(source: &str, allocator: &'ast Allocator) -> Result<Suite<'ast>> {
Ok(parse_module(source)?.into_suite())
}
@ -174,7 +174,8 @@ mod tests {
def f():
pass
";
let stmts = parse_suite(source)?;
let allocator = Allocator::new();
let stmts = parse_suite(source, &allocator)?;
assert_eq!(num_statements(&stmts), 2);
Ok(())
}
@ -188,7 +189,8 @@ def f():
else:
print()
";
let stmts = parse_suite(source)?;
let allocator = Allocator::new();
let stmts = parse_suite(source, &allocator)?;
assert_eq!(num_statements(&stmts), 5);
Ok(())
}
@ -203,7 +205,8 @@ def f():
if a:
print()
";
let stmts = parse_suite(source)?;
let allocator = Allocator::new();
let stmts = parse_suite(source, &allocator)?;
assert_eq!(num_statements(&stmts), 6);
Ok(())
}
@ -217,7 +220,8 @@ def f():
elif a:
print()
";
let stmts = parse_suite(source)?;
let allocator = Allocator::new();
let stmts = parse_suite(source, &allocator)?;
assert_eq!(num_statements(&stmts), 5);
Ok(())
}
@ -235,7 +239,8 @@ def f():
else:
print()
";
let stmts = parse_suite(source)?;
let allocator = Allocator::new();
let stmts = parse_suite(source, &allocator)?;
assert_eq!(num_statements(&stmts), 9);
Ok(())
}
@ -250,7 +255,8 @@ def f():
case _:
pass
";
let stmts = parse_suite(source)?;
let allocator = Allocator::new();
let stmts = parse_suite(source, &allocator)?;
assert_eq!(num_statements(&stmts), 6);
Ok(())
}
@ -279,7 +285,8 @@ async def f():
import time
pass
";
let stmts = parse_suite(source)?;
let allocator = Allocator::new();
let stmts = parse_suite(source, &allocator)?;
assert_eq!(num_statements(&stmts), 19);
Ok(())
}
@ -291,7 +298,8 @@ def f():
for i in range(10):
pass
";
let stmts = parse_suite(source)?;
let allocator = Allocator::new();
let stmts = parse_suite(source, &allocator)?;
assert_eq!(num_statements(&stmts), 2);
Ok(())
}
@ -305,7 +313,8 @@ def f():
else:
print()
";
let stmts = parse_suite(source)?;
let allocator = Allocator::new();
let stmts = parse_suite(source, &allocator)?;
assert_eq!(num_statements(&stmts), 3);
Ok(())
}
@ -320,7 +329,8 @@ def f():
print()
";
let stmts = parse_suite(source)?;
let allocator = Allocator::new();
let stmts = parse_suite(source, &allocator)?;
assert_eq!(num_statements(&stmts), 5);
Ok(())
}
@ -338,7 +348,8 @@ def f():
print()
";
let stmts = parse_suite(source)?;
let allocator = Allocator::new();
let stmts = parse_suite(source, &allocator)?;
assert_eq!(num_statements(&stmts), 3);
Ok(())
}
@ -349,7 +360,8 @@ def f():
def f():
return
";
let stmts = parse_suite(source)?;
let allocator = Allocator::new();
let stmts = parse_suite(source, &allocator)?;
assert_eq!(num_statements(&stmts), 1);
Ok(())
}
@ -365,7 +377,8 @@ def f():
print()
";
let stmts = parse_suite(source)?;
let allocator = Allocator::new();
let stmts = parse_suite(source, &allocator)?;
assert_eq!(num_statements(&stmts), 6);
Ok(())
}
@ -379,7 +392,8 @@ def f():
except Exception:
raise
";
let stmts = parse_suite(source)?;
let allocator = Allocator::new();
let stmts = parse_suite(source, &allocator)?;
assert_eq!(num_statements(&stmts), 5);
Ok(())
}
@ -395,7 +409,8 @@ def f():
else:
print()
";
let stmts = parse_suite(source)?;
let allocator = Allocator::new();
let stmts = parse_suite(source, &allocator)?;
assert_eq!(num_statements(&stmts), 7);
Ok(())
}
@ -413,7 +428,8 @@ def f():
finally:
pass
";
let stmts = parse_suite(source)?;
let allocator = Allocator::new();
let stmts = parse_suite(source, &allocator)?;
assert_eq!(num_statements(&stmts), 10);
Ok(())
}
@ -429,7 +445,8 @@ def f():
except Exception:
raise
";
let stmts = parse_suite(source)?;
let allocator = Allocator::new();
let stmts = parse_suite(source, &allocator)?;
assert_eq!(num_statements(&stmts), 8);
Ok(())
}
@ -447,7 +464,8 @@ def f():
finally:
print()
";
let stmts = parse_suite(source)?;
let allocator = Allocator::new();
let stmts = parse_suite(source, &allocator)?;
assert_eq!(num_statements(&stmts), 11);
Ok(())
}
@ -459,7 +477,8 @@ def f():
for i in range(10):
yield i
";
let stmts = parse_suite(source)?;
let allocator = Allocator::new();
let stmts = parse_suite(source, &allocator)?;
assert_eq!(num_statements(&stmts), 2);
Ok(())
}

View File

@ -83,7 +83,7 @@ pub(crate) fn type_param_name_mismatch(checker: &mut Checker, value: &Expr, targ
return;
};
if var_name == param_name {
if *var_name == param_name {
return;
}

View File

@ -95,7 +95,7 @@ pub(crate) fn unnecessary_dunder_call(checker: &mut Checker, call: &ast::ExprCal
if let Expr::Call(ast::ExprCall { func, .. }) = value.as_ref() {
if checker.semantic().has_builtin_binding("super") {
if let Expr::Name(ast::ExprName { id, .. }) = func.as_ref() {
if id == "super" {
if *id == "super" {
return;
}
}

View File

@ -65,13 +65,15 @@ pub(crate) fn remove_import_members(
#[cfg(test)]
mod tests {
use ruff_allocator::Allocator;
use ruff_python_parser::parse_module;
use ruff_source_file::Locator;
use super::remove_import_members;
fn test_helper(source: &str, members_to_remove: &[&str]) -> String {
let parsed = parse_module(source).unwrap();
let allocator = Allocator::new();
let parsed = parse_module(source, &allocator).unwrap();
let import_from_stmt = parsed
.suite()
.first()

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