diff --git a/crates/ruff/src/rules/flake8_bandit/rules/bad_file_permissions.rs b/crates/ruff/src/rules/flake8_bandit/rules/bad_file_permissions.rs index e529f25c15..38a5b5d29e 100644 --- a/crates/ruff/src/rules/flake8_bandit/rules/bad_file_permissions.rs +++ b/crates/ruff/src/rules/flake8_bandit/rules/bad_file_permissions.rs @@ -5,7 +5,8 @@ use rustpython_parser::ast::{Constant, Expr, ExprKind, Keyword, Operator}; use ruff_diagnostics::{Diagnostic, Violation}; use ruff_macros::{derive_message_formats, violation}; -use ruff_python_ast::helpers::{compose_call_path, SimpleCallArgs}; +use ruff_python_ast::call_path::compose_call_path; +use ruff_python_ast::helpers::SimpleCallArgs; use ruff_python_ast::types::Range; use crate::checkers::ast::Checker; diff --git a/crates/ruff/src/rules/flake8_boolean_trap/rules.rs b/crates/ruff/src/rules/flake8_boolean_trap/rules.rs index 4d673ca034..671a5a0be6 100644 --- a/crates/ruff/src/rules/flake8_boolean_trap/rules.rs +++ b/crates/ruff/src/rules/flake8_boolean_trap/rules.rs @@ -3,7 +3,7 @@ use rustpython_parser::ast::{Arguments, Constant, Expr, ExprKind}; use ruff_diagnostics::Violation; use ruff_diagnostics::{Diagnostic, DiagnosticKind}; use ruff_macros::{derive_message_formats, violation}; -use ruff_python_ast::helpers::collect_call_path; +use ruff_python_ast::call_path::collect_call_path; use ruff_python_ast::types::Range; use crate::checkers::ast::Checker; diff --git a/crates/ruff/src/rules/flake8_bugbear/rules/duplicate_exceptions.rs b/crates/ruff/src/rules/flake8_bugbear/rules/duplicate_exceptions.rs index 630928f56d..3ec998b2fe 100644 --- a/crates/ruff/src/rules/flake8_bugbear/rules/duplicate_exceptions.rs +++ b/crates/ruff/src/rules/flake8_bugbear/rules/duplicate_exceptions.rs @@ -7,9 +7,10 @@ use rustpython_parser::ast::{ use ruff_diagnostics::{AlwaysAutofixableViolation, Violation}; use ruff_diagnostics::{Diagnostic, Edit}; use ruff_macros::{derive_message_formats, violation}; -use ruff_python_ast::helpers; +use ruff_python_ast::call_path; +use ruff_python_ast::call_path::CallPath; use ruff_python_ast::helpers::unparse_expr; -use ruff_python_ast::types::{CallPath, Range}; +use ruff_python_ast::types::Range; use crate::checkers::ast::Checker; use crate::registry::{AsRule, Rule}; @@ -69,7 +70,7 @@ fn duplicate_handler_exceptions<'a>( let mut duplicates: FxHashSet = FxHashSet::default(); let mut unique_elts: Vec<&Expr> = Vec::default(); for type_ in elts { - let call_path = helpers::collect_call_path(type_); + let call_path = call_path::collect_call_path(type_); if !call_path.is_empty() { if seen.contains_key(&call_path) { duplicates.insert(call_path); @@ -124,7 +125,7 @@ pub fn duplicate_exceptions(checker: &mut Checker, handlers: &[Excepthandler]) { }; match &type_.node { ExprKind::Attribute { .. } | ExprKind::Name { .. } => { - let call_path = helpers::collect_call_path(type_); + let call_path = call_path::collect_call_path(type_); if !call_path.is_empty() { if seen.contains(&call_path) { duplicates.entry(call_path).or_default().push(type_); diff --git a/crates/ruff/src/rules/flake8_bugbear/rules/function_call_argument_default.rs b/crates/ruff/src/rules/flake8_bugbear/rules/function_call_argument_default.rs index 7de79ee843..b3d40e6bc9 100644 --- a/crates/ruff/src/rules/flake8_bugbear/rules/function_call_argument_default.rs +++ b/crates/ruff/src/rules/flake8_bugbear/rules/function_call_argument_default.rs @@ -3,8 +3,9 @@ use rustpython_parser::ast::{Arguments, Constant, Expr, ExprKind}; use ruff_diagnostics::Violation; use ruff_diagnostics::{Diagnostic, DiagnosticKind}; use ruff_macros::{derive_message_formats, violation}; -use ruff_python_ast::helpers::{compose_call_path, to_call_path}; -use ruff_python_ast::types::{CallPath, Range}; +use ruff_python_ast::call_path::to_call_path; +use ruff_python_ast::call_path::{compose_call_path, CallPath}; +use ruff_python_ast::types::Range; use ruff_python_ast::visitor; use ruff_python_ast::visitor::Visitor; diff --git a/crates/ruff/src/rules/flake8_debugger/rules.rs b/crates/ruff/src/rules/flake8_debugger/rules.rs index 5a49463e0d..0818150662 100644 --- a/crates/ruff/src/rules/flake8_debugger/rules.rs +++ b/crates/ruff/src/rules/flake8_debugger/rules.rs @@ -2,7 +2,7 @@ use rustpython_parser::ast::{Expr, Stmt}; use ruff_diagnostics::{Diagnostic, Violation}; use ruff_macros::{derive_message_formats, violation}; -use ruff_python_ast::helpers::format_call_path; +use ruff_python_ast::call_path::format_call_path; use ruff_python_ast::types::Range; use crate::checkers::ast::Checker; diff --git a/crates/ruff/src/rules/flake8_django/rules/helpers.rs b/crates/ruff/src/rules/flake8_django/rules/helpers.rs index 34069d11b9..60f7c1c32a 100644 --- a/crates/ruff/src/rules/flake8_django/rules/helpers.rs +++ b/crates/ruff/src/rules/flake8_django/rules/helpers.rs @@ -1,6 +1,7 @@ -use ruff_python_ast::context::Context; use rustpython_parser::ast::Expr; +use ruff_python_ast::context::Context; + /// Return `true` if a Python class appears to be a Django model, based on its base classes. pub fn is_model(context: &Context, base: &Expr) -> bool { context.resolve_call_path(base).map_or(false, |call_path| { diff --git a/crates/ruff/src/rules/flake8_django/rules/non_leading_receiver_decorator.rs b/crates/ruff/src/rules/flake8_django/rules/non_leading_receiver_decorator.rs index 20888318d4..813b9ac4b5 100644 --- a/crates/ruff/src/rules/flake8_django/rules/non_leading_receiver_decorator.rs +++ b/crates/ruff/src/rules/flake8_django/rules/non_leading_receiver_decorator.rs @@ -2,7 +2,8 @@ use rustpython_parser::ast::{Expr, ExprKind}; use ruff_diagnostics::{Diagnostic, Violation}; use ruff_macros::{derive_message_formats, violation}; -use ruff_python_ast::types::{CallPath, Range}; +use ruff_python_ast::call_path::CallPath; +use ruff_python_ast::types::Range; /// ## What it does /// Checks that Django's `@receiver` decorator is listed first, prior to diff --git a/crates/ruff/src/rules/flake8_django/rules/unordered_body_content_in_model.rs b/crates/ruff/src/rules/flake8_django/rules/unordered_body_content_in_model.rs index ebefddf9f1..efec15141e 100644 --- a/crates/ruff/src/rules/flake8_django/rules/unordered_body_content_in_model.rs +++ b/crates/ruff/src/rules/flake8_django/rules/unordered_body_content_in_model.rs @@ -1,9 +1,10 @@ use std::fmt; +use rustpython_parser::ast::{Expr, ExprKind, Stmt, StmtKind}; + use ruff_diagnostics::{Diagnostic, Violation}; use ruff_macros::{derive_message_formats, violation}; use ruff_python_ast::types::Range; -use rustpython_parser::ast::{Expr, ExprKind, Stmt, StmtKind}; use crate::checkers::ast::Checker; diff --git a/crates/ruff/src/rules/flake8_pytest_style/rules/fixture.rs b/crates/ruff/src/rules/flake8_pytest_style/rules/fixture.rs index 2a6b8abe24..17f929ff50 100644 --- a/crates/ruff/src/rules/flake8_pytest_style/rules/fixture.rs +++ b/crates/ruff/src/rules/flake8_pytest_style/rules/fixture.rs @@ -4,7 +4,8 @@ use rustpython_parser::ast::{Arguments, Expr, ExprKind, Keyword, Location, Stmt, use ruff_diagnostics::{AlwaysAutofixableViolation, Violation}; use ruff_diagnostics::{Diagnostic, Edit}; use ruff_macros::{derive_message_formats, violation}; -use ruff_python_ast::helpers::{collect_arg_names, collect_call_path}; +use ruff_python_ast::call_path::collect_call_path; +use ruff_python_ast::helpers::collect_arg_names; use ruff_python_ast::source_code::Locator; use ruff_python_ast::types::Range; use ruff_python_ast::visitor; diff --git a/crates/ruff/src/rules/flake8_pytest_style/rules/helpers.rs b/crates/ruff/src/rules/flake8_pytest_style/rules/helpers.rs index 007f9f5de5..79820f7ae4 100644 --- a/crates/ruff/src/rules/flake8_pytest_style/rules/helpers.rs +++ b/crates/ruff/src/rules/flake8_pytest_style/rules/helpers.rs @@ -1,7 +1,8 @@ use num_traits::identities::Zero; +use ruff_python_ast::call_path::collect_call_path; use rustpython_parser::ast::{Constant, Expr, ExprKind, Keyword}; -use ruff_python_ast::helpers::{collect_call_path, map_callable}; +use ruff_python_ast::helpers::map_callable; use crate::checkers::ast::Checker; diff --git a/crates/ruff/src/rules/flake8_pytest_style/rules/patch.rs b/crates/ruff/src/rules/flake8_pytest_style/rules/patch.rs index fbb2bfeafc..d992df5728 100644 --- a/crates/ruff/src/rules/flake8_pytest_style/rules/patch.rs +++ b/crates/ruff/src/rules/flake8_pytest_style/rules/patch.rs @@ -3,7 +3,8 @@ use rustpython_parser::ast::{Expr, ExprKind, Keyword}; use ruff_diagnostics::{Diagnostic, Violation}; use ruff_macros::{derive_message_formats, violation}; -use ruff_python_ast::helpers::{collect_arg_names, compose_call_path, SimpleCallArgs}; +use ruff_python_ast::call_path::compose_call_path; +use ruff_python_ast::helpers::{collect_arg_names, SimpleCallArgs}; use ruff_python_ast::types::Range; use ruff_python_ast::visitor; use ruff_python_ast::visitor::Visitor; diff --git a/crates/ruff/src/rules/flake8_pytest_style/rules/raises.rs b/crates/ruff/src/rules/flake8_pytest_style/rules/raises.rs index ab88368d17..b03075744e 100644 --- a/crates/ruff/src/rules/flake8_pytest_style/rules/raises.rs +++ b/crates/ruff/src/rules/flake8_pytest_style/rules/raises.rs @@ -2,7 +2,8 @@ use rustpython_parser::ast::{Expr, ExprKind, Keyword, Stmt, StmtKind, Withitem}; use ruff_diagnostics::{Diagnostic, Violation}; use ruff_macros::{derive_message_formats, violation}; -use ruff_python_ast::helpers::{format_call_path, to_call_path}; +use ruff_python_ast::call_path::format_call_path; +use ruff_python_ast::call_path::to_call_path; use ruff_python_ast::types::Range; use crate::checkers::ast::Checker; diff --git a/crates/ruff/src/rules/flake8_self/rules/private_member_access.rs b/crates/ruff/src/rules/flake8_self/rules/private_member_access.rs index 9d1300f75e..1b600dcaa5 100644 --- a/crates/ruff/src/rules/flake8_self/rules/private_member_access.rs +++ b/crates/ruff/src/rules/flake8_self/rules/private_member_access.rs @@ -2,7 +2,7 @@ use rustpython_parser::ast::{Expr, ExprKind}; use ruff_diagnostics::{Diagnostic, Violation}; use ruff_macros::{derive_message_formats, violation}; -use ruff_python_ast::helpers::collect_call_path; +use ruff_python_ast::call_path::collect_call_path; use ruff_python_ast::scope::ScopeKind; use ruff_python_ast::types::Range; diff --git a/crates/ruff/src/rules/flake8_simplify/rules/suppressible_exception.rs b/crates/ruff/src/rules/flake8_simplify/rules/suppressible_exception.rs index c48e38d708..4118b74d8e 100644 --- a/crates/ruff/src/rules/flake8_simplify/rules/suppressible_exception.rs +++ b/crates/ruff/src/rules/flake8_simplify/rules/suppressible_exception.rs @@ -2,8 +2,8 @@ use rustpython_parser::ast::{Excepthandler, ExcepthandlerKind, Located, Stmt, St use ruff_diagnostics::{Diagnostic, Violation}; use ruff_macros::{derive_message_formats, violation}; +use ruff_python_ast::call_path::compose_call_path; use ruff_python_ast::helpers; -use ruff_python_ast::helpers::compose_call_path; use ruff_python_ast::types::Range; use crate::checkers::ast::Checker; diff --git a/crates/ruff/src/rules/flake8_tidy_imports/banned_api.rs b/crates/ruff/src/rules/flake8_tidy_imports/banned_api.rs index eba8c682a7..69348ad8ed 100644 --- a/crates/ruff/src/rules/flake8_tidy_imports/banned_api.rs +++ b/crates/ruff/src/rules/flake8_tidy_imports/banned_api.rs @@ -5,7 +5,8 @@ use serde::{Deserialize, Serialize}; use ruff_diagnostics::{Diagnostic, Violation}; use ruff_macros::{derive_message_formats, violation, CacheKey}; -use ruff_python_ast::types::{CallPath, Range}; +use ruff_python_ast::call_path::CallPath; +use ruff_python_ast::types::Range; use crate::checkers::ast::Checker; diff --git a/crates/ruff/src/rules/flake8_type_checking/helpers.rs b/crates/ruff/src/rules/flake8_type_checking/helpers.rs index 3d43af3ae9..51cc28f305 100644 --- a/crates/ruff/src/rules/flake8_type_checking/helpers.rs +++ b/crates/ruff/src/rules/flake8_type_checking/helpers.rs @@ -2,8 +2,9 @@ use num_traits::Zero; use rustpython_parser::ast::{Constant, Expr, ExprKind}; use ruff_python_ast::binding::{Binding, BindingKind, ExecutionContext}; +use ruff_python_ast::call_path::to_call_path; use ruff_python_ast::context::Context; -use ruff_python_ast::helpers::{map_callable, to_call_path}; +use ruff_python_ast::helpers::map_callable; use ruff_python_ast::scope::ScopeKind; /// Return `true` if [`Expr`] is a guard for a type-checking block. diff --git a/crates/ruff/src/rules/pydocstyle/helpers.rs b/crates/ruff/src/rules/pydocstyle/helpers.rs index aa3aabf544..5d14b52c90 100644 --- a/crates/ruff/src/rules/pydocstyle/helpers.rs +++ b/crates/ruff/src/rules/pydocstyle/helpers.rs @@ -1,7 +1,8 @@ +use ruff_python_ast::call_path::to_call_path; use std::collections::BTreeSet; use ruff_python_ast::cast; -use ruff_python_ast::helpers::{map_callable, to_call_path}; +use ruff_python_ast::helpers::map_callable; use ruff_python_ast::newlines::StrExt; use ruff_python_ast::str::is_implicit_concatenation; diff --git a/crates/ruff/src/rules/pydocstyle/rules/capitalized.rs b/crates/ruff/src/rules/pydocstyle/rules/capitalized.rs index d46203c5a1..da67fd7d2b 100644 --- a/crates/ruff/src/rules/pydocstyle/rules/capitalized.rs +++ b/crates/ruff/src/rules/pydocstyle/rules/capitalized.rs @@ -1,8 +1,9 @@ +use unicode_width::UnicodeWidthStr; + use ruff_diagnostics::{AlwaysAutofixableViolation, Diagnostic, Edit}; use ruff_macros::{derive_message_formats, violation}; use ruff_python_ast::str::leading_quote; use ruff_python_ast::types::Range; -use unicode_width::UnicodeWidthStr; use crate::checkers::ast::Checker; use crate::docstrings::definition::{DefinitionKind, Docstring}; diff --git a/crates/ruff/src/rules/pydocstyle/rules/non_imperative_mood.rs b/crates/ruff/src/rules/pydocstyle/rules/non_imperative_mood.rs index df9a4c9563..e25652f1dc 100644 --- a/crates/ruff/src/rules/pydocstyle/rules/non_imperative_mood.rs +++ b/crates/ruff/src/rules/pydocstyle/rules/non_imperative_mood.rs @@ -5,10 +5,10 @@ use once_cell::sync::Lazy; use ruff_diagnostics::{Diagnostic, Violation}; use ruff_macros::{derive_message_formats, violation}; +use ruff_python_ast::call_path::{to_call_path, CallPath}; use ruff_python_ast::cast; -use ruff_python_ast::helpers::to_call_path; use ruff_python_ast::newlines::StrExt; -use ruff_python_ast::types::{CallPath, Range}; +use ruff_python_ast::types::Range; use ruff_python_ast::visibility::{is_property, is_test}; use crate::checkers::ast::Checker; diff --git a/crates/ruff/src/rules/pyupgrade/rules/datetime_utc_alias.rs b/crates/ruff/src/rules/pyupgrade/rules/datetime_utc_alias.rs index 9dd838f229..06deb05d7a 100644 --- a/crates/ruff/src/rules/pyupgrade/rules/datetime_utc_alias.rs +++ b/crates/ruff/src/rules/pyupgrade/rules/datetime_utc_alias.rs @@ -2,7 +2,7 @@ use rustpython_parser::ast::Expr; use ruff_diagnostics::{AutofixKind, Diagnostic, Edit, Violation}; use ruff_macros::{derive_message_formats, violation}; -use ruff_python_ast::helpers::collect_call_path; +use ruff_python_ast::call_path::collect_call_path; use ruff_python_ast::types::Range; use crate::checkers::ast::Checker; diff --git a/crates/ruff/src/rules/pyupgrade/rules/deprecated_mock_import.rs b/crates/ruff/src/rules/pyupgrade/rules/deprecated_mock_import.rs index 174c49bf40..a86d69673f 100644 --- a/crates/ruff/src/rules/pyupgrade/rules/deprecated_mock_import.rs +++ b/crates/ruff/src/rules/pyupgrade/rules/deprecated_mock_import.rs @@ -8,7 +8,7 @@ use rustpython_parser::ast::{Expr, ExprKind, Stmt, StmtKind}; use ruff_diagnostics::{AlwaysAutofixableViolation, Diagnostic, Edit}; use ruff_macros::{derive_message_formats, violation}; -use ruff_python_ast::helpers::collect_call_path; +use ruff_python_ast::call_path::collect_call_path; use ruff_python_ast::source_code::{Locator, Stylist}; use ruff_python_ast::types::Range; use ruff_python_ast::whitespace::indentation; diff --git a/crates/ruff/src/rules/pyupgrade/rules/os_error_alias.rs b/crates/ruff/src/rules/pyupgrade/rules/os_error_alias.rs index f1574115e8..e47d51161e 100644 --- a/crates/ruff/src/rules/pyupgrade/rules/os_error_alias.rs +++ b/crates/ruff/src/rules/pyupgrade/rules/os_error_alias.rs @@ -2,8 +2,9 @@ use rustpython_parser::ast::{Excepthandler, ExcepthandlerKind, Expr, ExprContext use ruff_diagnostics::{AlwaysAutofixableViolation, Diagnostic, Edit}; use ruff_macros::{derive_message_formats, violation}; +use ruff_python_ast::call_path::compose_call_path; use ruff_python_ast::context::Context; -use ruff_python_ast::helpers::{compose_call_path, create_expr, unparse_expr}; +use ruff_python_ast::helpers::{create_expr, unparse_expr}; use ruff_python_ast::types::Range; use crate::checkers::ast::Checker; diff --git a/crates/ruff/src/rules/ruff/rules/asyncio_dangling_task.rs b/crates/ruff/src/rules/ruff/rules/asyncio_dangling_task.rs index 90a9bb9a56..07deed868e 100644 --- a/crates/ruff/src/rules/ruff/rules/asyncio_dangling_task.rs +++ b/crates/ruff/src/rules/ruff/rules/asyncio_dangling_task.rs @@ -4,7 +4,8 @@ use rustpython_parser::ast::{Expr, ExprKind}; use ruff_diagnostics::{Diagnostic, Violation}; use ruff_macros::{derive_message_formats, violation}; -use ruff_python_ast::types::{CallPath, Range}; +use ruff_python_ast::call_path::CallPath; +use ruff_python_ast::types::Range; /// ## What it does /// Checks for `asyncio.create_task` and `asyncio.ensure_future` calls diff --git a/crates/ruff_python_ast/src/call_path.rs b/crates/ruff_python_ast/src/call_path.rs new file mode 100644 index 0000000000..59b4d56c9a --- /dev/null +++ b/crates/ruff_python_ast/src/call_path.rs @@ -0,0 +1,63 @@ +use rustpython_parser::ast::{Expr, ExprKind}; +use smallvec::smallvec; + +/// A representation of a qualified name, like `typing.List`. +pub type CallPath<'a> = smallvec::SmallVec<[&'a str; 8]>; + +fn collect_call_path_inner<'a>(expr: &'a Expr, parts: &mut CallPath<'a>) -> bool { + match &expr.node { + ExprKind::Attribute { value, attr, .. } => { + if collect_call_path_inner(value, parts) { + parts.push(attr); + true + } else { + false + } + } + ExprKind::Name { id, .. } => { + parts.push(id); + true + } + _ => false, + } +} + +/// Convert an `Expr` to its [`CallPath`] segments (like `["typing", "List"]`). +pub fn collect_call_path(expr: &Expr) -> CallPath { + let mut segments = smallvec![]; + collect_call_path_inner(expr, &mut segments); + segments +} + +/// Convert an `Expr` to its call path (like `List`, or `typing.List`). +pub fn compose_call_path(expr: &Expr) -> Option { + let call_path = collect_call_path(expr); + if call_path.is_empty() { + None + } else { + Some(format_call_path(&call_path)) + } +} + +/// Format a call path for display. +pub fn format_call_path(call_path: &[&str]) -> String { + if call_path + .first() + .expect("Unable to format empty call path") + .is_empty() + { + call_path[1..].join(".") + } else { + call_path.join(".") + } +} + +/// Split a fully-qualified name (like `typing.List`) into (`typing`, `List`). +pub fn to_call_path(target: &str) -> CallPath { + if target.contains('.') { + target.split('.').collect() + } else { + // Special-case: for builtins, return `["", "int"]` instead of `["int"]`. + smallvec!["", target] + } +} diff --git a/crates/ruff_python_ast/src/comparable.rs b/crates/ruff_python_ast/src/comparable.rs index 98a8c2d5cb..3ddad10a94 100644 --- a/crates/ruff_python_ast/src/comparable.rs +++ b/crates/ruff_python_ast/src/comparable.rs @@ -1,14 +1,13 @@ //! An equivalent object hierarchy to the [`Expr`] hierarchy, but with the //! ability to compare expressions for equality (via [`Eq`] and [`Hash`]). +use num_bigint::BigInt; use rustpython_parser::ast::{ Alias, Arg, Arguments, Boolop, Cmpop, Comprehension, Constant, Excepthandler, ExcepthandlerKind, Expr, ExprContext, ExprKind, Keyword, MatchCase, Operator, Pattern, PatternKind, Stmt, StmtKind, Unaryop, Withitem, }; -use num_bigint::BigInt; - #[derive(Debug, PartialEq, Eq, Hash, Copy, Clone)] pub enum ComparableExprContext { Load, diff --git a/crates/ruff_python_ast/src/context.rs b/crates/ruff_python_ast/src/context.rs index 49f79ebd37..cba1617889 100644 --- a/crates/ruff_python_ast/src/context.rs +++ b/crates/ruff_python_ast/src/context.rs @@ -12,9 +12,10 @@ use crate::binding::{ Binding, BindingId, BindingKind, Bindings, Exceptions, ExecutionContext, FromImportation, Importation, SubmoduleImportation, }; -use crate::helpers::{collect_call_path, from_relative_import}; +use crate::call_path::{collect_call_path, CallPath}; +use crate::helpers::from_relative_import; use crate::scope::{Scope, ScopeId, ScopeKind, ScopeStack, Scopes}; -use crate::types::{CallPath, RefEquality}; +use crate::types::RefEquality; use crate::typing::AnnotationKind; use crate::visibility::{module_visibility, Modifier, VisibleScope}; diff --git a/crates/ruff_python_ast/src/function_type.rs b/crates/ruff_python_ast/src/function_type.rs index 9c3b88ffd5..df60e3b32e 100644 --- a/crates/ruff_python_ast/src/function_type.rs +++ b/crates/ruff_python_ast/src/function_type.rs @@ -1,7 +1,8 @@ use rustpython_parser::ast::Expr; +use crate::call_path::to_call_path; use crate::context::Context; -use crate::helpers::{map_callable, to_call_path}; +use crate::helpers::map_callable; use crate::scope::{Scope, ScopeKind}; const CLASS_METHODS: [&str; 3] = ["__new__", "__init_subclass__", "__class_getitem__"]; diff --git a/crates/ruff_python_ast/src/helpers.rs b/crates/ruff_python_ast/src/helpers.rs index 3da832a020..7c0f301362 100644 --- a/crates/ruff_python_ast/src/helpers.rs +++ b/crates/ruff_python_ast/src/helpers.rs @@ -10,11 +10,12 @@ use rustpython_parser::ast::{ KeywordData, Located, Location, MatchCase, Pattern, PatternKind, Stmt, StmtKind, }; use rustpython_parser::{lexer, Mode, Tok}; -use smallvec::{smallvec, SmallVec}; +use smallvec::SmallVec; +use crate::call_path::CallPath; use crate::context::Context; use crate::source_code::{Generator, Indexer, Locator, Stylist}; -use crate::types::{CallPath, Range}; +use crate::types::Range; use crate::visitor; use crate::visitor::Visitor; @@ -49,54 +50,6 @@ pub fn unparse_constant(constant: &Constant, stylist: &Stylist) -> String { generator.generate() } -fn collect_call_path_inner<'a>(expr: &'a Expr, parts: &mut CallPath<'a>) -> bool { - match &expr.node { - ExprKind::Attribute { value, attr, .. } => { - if collect_call_path_inner(value, parts) { - parts.push(attr); - true - } else { - false - } - } - ExprKind::Name { id, .. } => { - parts.push(id); - true - } - _ => false, - } -} - -/// Convert an `Expr` to its [`CallPath`] segments (like `["typing", "List"]`). -pub fn collect_call_path(expr: &Expr) -> CallPath { - let mut segments = smallvec![]; - collect_call_path_inner(expr, &mut segments); - segments -} - -/// Convert an `Expr` to its call path (like `List`, or `typing.List`). -pub fn compose_call_path(expr: &Expr) -> Option { - let call_path = collect_call_path(expr); - if call_path.is_empty() { - None - } else { - Some(format_call_path(&call_path)) - } -} - -/// Format a call path for display. -pub fn format_call_path(call_path: &[&str]) -> String { - if call_path - .first() - .expect("Unable to format empty call path") - .is_empty() - { - call_path[1..].join(".") - } else { - call_path.join(".") - } -} - /// Return `true` if the `Expr` contains a reference to `${module}.${target}`. pub fn contains_call_path(ctx: &Context, expr: &Expr, target: &[&str]) -> bool { any_over_expr(expr, &|expr| { @@ -748,15 +701,6 @@ pub fn format_import_from_member( full_name } -/// Split a target string (like `typing.List`) into (`typing`, `List`). -pub fn to_call_path(target: &str) -> CallPath { - if target.contains('.') { - target.split('.').collect() - } else { - smallvec!["", target] - } -} - /// Create a module path from a (package, path) pair. /// /// For example, if the package is `foo/bar` and the path is `foo/bar/baz.py`, diff --git a/crates/ruff_python_ast/src/lib.rs b/crates/ruff_python_ast/src/lib.rs index 07f0505658..65d810962a 100644 --- a/crates/ruff_python_ast/src/lib.rs +++ b/crates/ruff_python_ast/src/lib.rs @@ -1,6 +1,7 @@ pub mod all; pub mod binding; pub mod branch_detection; +pub mod call_path; pub mod cast; pub mod comparable; pub mod context; diff --git a/crates/ruff_python_ast/src/logging.rs b/crates/ruff_python_ast/src/logging.rs index 7d3d57383b..35e93eca7e 100644 --- a/crates/ruff_python_ast/src/logging.rs +++ b/crates/ruff_python_ast/src/logging.rs @@ -1,7 +1,8 @@ -use crate::context::Context; -use crate::helpers::collect_call_path; use rustpython_parser::ast::{Expr, ExprKind}; +use crate::call_path::collect_call_path; +use crate::context::Context; + #[derive(Copy, Clone)] pub enum LoggingLevel { Debug, diff --git a/crates/ruff_python_ast/src/source_code/locator.rs b/crates/ruff_python_ast/src/source_code/locator.rs index 2bfd291659..ca177ee89b 100644 --- a/crates/ruff_python_ast/src/source_code/locator.rs +++ b/crates/ruff_python_ast/src/source_code/locator.rs @@ -208,9 +208,10 @@ impl Utf8Index { #[cfg(test)] mod tests { - use crate::source_code::locator::{AsciiIndex, Index, Utf8Index}; use rustpython_parser::ast::Location; + use crate::source_code::locator::{AsciiIndex, Index, Utf8Index}; + fn index_ascii(content: &str) -> AsciiIndex { match Index::from(content) { Index::Ascii(ascii) => ascii, diff --git a/crates/ruff_python_ast/src/source_code/stylist.rs b/crates/ruff_python_ast/src/source_code/stylist.rs index bef99db2bd..31735a86cf 100644 --- a/crates/ruff_python_ast/src/source_code/stylist.rs +++ b/crates/ruff_python_ast/src/source_code/stylist.rs @@ -8,9 +8,9 @@ use rustpython_parser::ast::Location; use rustpython_parser::lexer::LexResult; use rustpython_parser::Tok; -use crate::source_code::Locator; use ruff_rustpython::vendor; +use crate::source_code::Locator; use crate::str::leading_quote; use crate::types::Range; @@ -213,11 +213,12 @@ fn detect_line_ending(contents: &str) -> Option { #[cfg(test)] mod tests { - use crate::source_code::stylist::{detect_line_ending, Indentation, LineEnding, Quote}; - use crate::source_code::{Locator, Stylist}; use rustpython_parser::lexer::lex; use rustpython_parser::Mode; + use crate::source_code::stylist::{detect_line_ending, Indentation, LineEnding, Quote}; + use crate::source_code::{Locator, Stylist}; + #[test] fn indentation() { let contents = r#"x = 1"#; diff --git a/crates/ruff_python_ast/src/types.rs b/crates/ruff_python_ast/src/types.rs index 4af5670d5a..466c95fe41 100644 --- a/crates/ruff_python_ast/src/types.rs +++ b/crates/ruff_python_ast/src/types.rs @@ -97,5 +97,3 @@ impl<'a> From<&RefEquality<'a, Expr>> for &'a Expr { r.0 } } - -pub type CallPath<'a> = smallvec::SmallVec<[&'a str; 8]>; diff --git a/crates/ruff_python_ast/src/visibility.rs b/crates/ruff_python_ast/src/visibility.rs index 7ba1f18d9f..1b596b6479 100644 --- a/crates/ruff_python_ast/src/visibility.rs +++ b/crates/ruff_python_ast/src/visibility.rs @@ -2,9 +2,10 @@ use std::path::Path; use rustpython_parser::ast::{Expr, Stmt, StmtKind}; +use crate::call_path::collect_call_path; +use crate::call_path::CallPath; use crate::context::Context; -use crate::helpers::{collect_call_path, map_callable}; -use crate::types::CallPath; +use crate::helpers::map_callable; #[derive(Debug, Clone, Copy)] pub enum Modifier {