diff --git a/crates/ruff_linter/src/checkers/ast/mod.rs b/crates/ruff_linter/src/checkers/ast/mod.rs index dcac55e62994d..0ed3800a59c93 100644 --- a/crates/ruff_linter/src/checkers/ast/mod.rs +++ b/crates/ruff_linter/src/checkers/ast/mod.rs @@ -1071,27 +1071,27 @@ impl<'a> Visitor<'a> for Checker<'a> { .and_then(|qualified_name| { if self .semantic - .match_typing_call_path(&qualified_name, "cast") + .match_typing_qualified_name(&qualified_name, "cast") { Some(typing::Callable::Cast) } else if self .semantic - .match_typing_call_path(&qualified_name, "NewType") + .match_typing_qualified_name(&qualified_name, "NewType") { Some(typing::Callable::NewType) } else if self .semantic - .match_typing_call_path(&qualified_name, "TypeVar") + .match_typing_qualified_name(&qualified_name, "TypeVar") { Some(typing::Callable::TypeVar) } else if self .semantic - .match_typing_call_path(&qualified_name, "NamedTuple") + .match_typing_qualified_name(&qualified_name, "NamedTuple") { Some(typing::Callable::NamedTuple) } else if self .semantic - .match_typing_call_path(&qualified_name, "TypedDict") + .match_typing_qualified_name(&qualified_name, "TypedDict") { Some(typing::Callable::TypedDict) } else if matches!( diff --git a/crates/ruff_linter/src/cst/helpers.rs b/crates/ruff_linter/src/cst/helpers.rs index e32c6ab887cb3..731ff8a56d2ac 100644 --- a/crates/ruff_linter/src/cst/helpers.rs +++ b/crates/ruff_linter/src/cst/helpers.rs @@ -1,44 +1,7 @@ use libcst_native::{ - Expression, Name, NameOrAttribute, ParenthesizableWhitespace, SimpleWhitespace, UnaryOperation, + Expression, Name, ParenthesizableWhitespace, SimpleWhitespace, UnaryOperation, }; -fn compose_call_path_inner<'a>(expr: &'a Expression, parts: &mut Vec<&'a str>) { - match expr { - Expression::Call(expr) => { - compose_call_path_inner(&expr.func, parts); - } - Expression::Attribute(expr) => { - compose_call_path_inner(&expr.value, parts); - parts.push(expr.attr.value); - } - Expression::Name(expr) => { - parts.push(expr.value); - } - _ => {} - } -} - -pub(crate) fn compose_call_path(expr: &Expression) -> Option { - let mut segments = vec![]; - compose_call_path_inner(expr, &mut segments); - if segments.is_empty() { - None - } else { - Some(segments.join(".")) - } -} - -pub(crate) fn compose_module_path(module: &NameOrAttribute) -> String { - match module { - NameOrAttribute::N(name) => name.value.to_string(), - NameOrAttribute::A(attr) => { - let name = attr.attr.value; - let prefix = compose_call_path(&attr.value); - prefix.map_or_else(|| name.to_string(), |prefix| format!("{prefix}.{name}")) - } - } -} - /// Return a [`ParenthesizableWhitespace`] containing a single space. pub(crate) fn space() -> ParenthesizableWhitespace<'static> { ParenthesizableWhitespace::SimpleWhitespace(SimpleWhitespace(" ")) diff --git a/crates/ruff_linter/src/fix/codemods.rs b/crates/ruff_linter/src/fix/codemods.rs index cf57f2126117b..c3c7691726967 100644 --- a/crates/ruff_linter/src/fix/codemods.rs +++ b/crates/ruff_linter/src/fix/codemods.rs @@ -2,14 +2,16 @@ //! and return the modified code snippet as output. use anyhow::{bail, Result}; use libcst_native::{ - Codegen, CodegenState, ImportNames, ParenthesizableWhitespace, SmallStatement, Statement, + Codegen, CodegenState, Expression, ImportNames, NameOrAttribute, ParenthesizableWhitespace, + SmallStatement, Statement, }; +use ruff_python_ast::name::UnqualifiedName; +use smallvec::{smallvec, SmallVec}; use ruff_python_ast::Stmt; use ruff_python_codegen::Stylist; use ruff_source_file::Locator; -use crate::cst::helpers::compose_module_path; use crate::cst::matchers::match_statement; /// Glue code to make libcst codegen work with ruff's Stylist @@ -78,7 +80,7 @@ pub(crate) fn remove_imports<'a>( for member in member_names { let alias_index = aliases .iter() - .position(|alias| member == compose_module_path(&alias.name)); + .position(|alias| member == qualified_name_from_name_or_attribute(&alias.name)); if let Some(index) = alias_index { aliases.remove(index); } @@ -142,7 +144,7 @@ pub(crate) fn retain_imports( aliases.retain(|alias| { member_names .iter() - .any(|member| *member == compose_module_path(&alias.name)) + .any(|member| *member == qualified_name_from_name_or_attribute(&alias.name)) }); // But avoid destroying any trailing comments. @@ -164,3 +166,40 @@ pub(crate) fn retain_imports( Ok(tree.codegen_stylist(stylist)) } + +fn collect_segments<'a>(expr: &'a Expression, parts: &mut SmallVec<[&'a str; 8]>) { + match expr { + Expression::Call(expr) => { + collect_segments(&expr.func, parts); + } + Expression::Attribute(expr) => { + collect_segments(&expr.value, parts); + parts.push(expr.attr.value); + } + Expression::Name(expr) => { + parts.push(expr.value); + } + _ => {} + } +} + +fn unqualified_name_from_expression<'a>(expr: &'a Expression<'a>) -> Option> { + let mut segments = smallvec![]; + collect_segments(expr, &mut segments); + if segments.is_empty() { + None + } else { + Some(segments.into_iter().collect()) + } +} + +fn qualified_name_from_name_or_attribute(module: &NameOrAttribute) -> String { + match module { + NameOrAttribute::N(name) => name.value.to_string(), + NameOrAttribute::A(attr) => { + let name = attr.attr.value; + let prefix = unqualified_name_from_expression(&attr.value); + prefix.map_or_else(|| name.to_string(), |prefix| format!("{prefix}.{name}")) + } + } +} diff --git a/crates/ruff_linter/src/rules/flake8_2020/helpers.rs b/crates/ruff_linter/src/rules/flake8_2020/helpers.rs index 3149efebf3a96..a838ab333e441 100644 --- a/crates/ruff_linter/src/rules/flake8_2020/helpers.rs +++ b/crates/ruff_linter/src/rules/flake8_2020/helpers.rs @@ -5,5 +5,5 @@ use ruff_python_semantic::SemanticModel; pub(super) fn is_sys(expr: &Expr, target: &str, semantic: &SemanticModel) -> bool { semantic .resolve_qualified_name(expr) - .is_some_and(|call_path| call_path.segments() == ["sys", target]) + .is_some_and(|qualified_name| qualified_name.segments() == ["sys", target]) } diff --git a/crates/ruff_linter/src/rules/flake8_2020/rules/name_or_attribute.rs b/crates/ruff_linter/src/rules/flake8_2020/rules/name_or_attribute.rs index 3f07581180cc7..ad3d40c9069e1 100644 --- a/crates/ruff_linter/src/rules/flake8_2020/rules/name_or_attribute.rs +++ b/crates/ruff_linter/src/rules/flake8_2020/rules/name_or_attribute.rs @@ -54,7 +54,7 @@ pub(crate) fn name_or_attribute(checker: &mut Checker, expr: &Expr) { if checker .semantic() .resolve_qualified_name(expr) - .is_some_and(|call_path| matches!(call_path.segments(), ["six", "PY3"])) + .is_some_and(|qualified_name| matches!(qualified_name.segments(), ["six", "PY3"])) { checker .diagnostics diff --git a/crates/ruff_linter/src/rules/flake8_async/rules/blocking_http_call.rs b/crates/ruff_linter/src/rules/flake8_async/rules/blocking_http_call.rs index 1730748a75c63..1318d4cbd705b 100644 --- a/crates/ruff_linter/src/rules/flake8_async/rules/blocking_http_call.rs +++ b/crates/ruff_linter/src/rules/flake8_async/rules/blocking_http_call.rs @@ -41,9 +41,9 @@ impl Violation for BlockingHttpCallInAsyncFunction { } } -fn is_blocking_http_call(call_path: &QualifiedName) -> bool { +fn is_blocking_http_call(qualified_name: &QualifiedName) -> bool { matches!( - call_path.segments(), + qualified_name.segments(), ["urllib", "request", "urlopen"] | [ "httpx" | "requests", diff --git a/crates/ruff_linter/src/rules/flake8_async/rules/blocking_os_call.rs b/crates/ruff_linter/src/rules/flake8_async/rules/blocking_os_call.rs index c3809677b6bf8..59848aeb7d040 100644 --- a/crates/ruff_linter/src/rules/flake8_async/rules/blocking_os_call.rs +++ b/crates/ruff_linter/src/rules/flake8_async/rules/blocking_os_call.rs @@ -60,9 +60,9 @@ pub(crate) fn blocking_os_call(checker: &mut Checker, call: &ExprCall) { } } -fn is_unsafe_os_method(call_path: &QualifiedName) -> bool { +fn is_unsafe_os_method(qualified_name: &QualifiedName) -> bool { matches!( - call_path.segments(), + qualified_name.segments(), [ "os", "popen" diff --git a/crates/ruff_linter/src/rules/flake8_async/rules/open_sleep_or_subprocess_call.rs b/crates/ruff_linter/src/rules/flake8_async/rules/open_sleep_or_subprocess_call.rs index be737afdd7993..f85383e6468fd 100644 --- a/crates/ruff_linter/src/rules/flake8_async/rules/open_sleep_or_subprocess_call.rs +++ b/crates/ruff_linter/src/rules/flake8_async/rules/open_sleep_or_subprocess_call.rs @@ -60,9 +60,9 @@ pub(crate) fn open_sleep_or_subprocess_call(checker: &mut Checker, call: &ast::E fn is_open_sleep_or_subprocess_call(func: &Expr, semantic: &SemanticModel) -> bool { semantic .resolve_qualified_name(func) - .is_some_and(|call_path| { + .is_some_and(|qualified_name| { matches!( - call_path.segments(), + qualified_name.segments(), ["", "open"] | ["time", "sleep"] | [ @@ -96,10 +96,10 @@ fn is_open_call_from_pathlib(func: &Expr, semantic: &SemanticModel) -> bool { // Path("foo").open() // ``` if let Expr::Call(call) = value.as_ref() { - let Some(call_path) = semantic.resolve_qualified_name(call.func.as_ref()) else { + let Some(qualified_name) = semantic.resolve_qualified_name(call.func.as_ref()) else { return false; }; - if call_path.segments() == ["pathlib", "Path"] { + if qualified_name.segments() == ["pathlib", "Path"] { return true; } } @@ -126,5 +126,5 @@ fn is_open_call_from_pathlib(func: &Expr, semantic: &SemanticModel) -> bool { semantic .resolve_qualified_name(call.func.as_ref()) - .is_some_and(|call_path| call_path.segments() == ["pathlib", "Path"]) + .is_some_and(|qualified_name| qualified_name.segments() == ["pathlib", "Path"]) } diff --git a/crates/ruff_linter/src/rules/flake8_bandit/helpers.rs b/crates/ruff_linter/src/rules/flake8_bandit/helpers.rs index 1ccbd6630d633..53c7e2c039433 100644 --- a/crates/ruff_linter/src/rules/flake8_bandit/helpers.rs +++ b/crates/ruff_linter/src/rules/flake8_bandit/helpers.rs @@ -25,15 +25,21 @@ pub(super) fn is_untyped_exception(type_: Option<&Expr>, semantic: &SemanticMode elts.iter().any(|type_| { semantic .resolve_qualified_name(type_) - .is_some_and(|call_path| { - matches!(call_path.segments(), ["", "Exception" | "BaseException"]) + .is_some_and(|qualified_name| { + matches!( + qualified_name.segments(), + ["", "Exception" | "BaseException"] + ) }) }) } else { semantic .resolve_qualified_name(type_) - .is_some_and(|call_path| { - matches!(call_path.segments(), ["", "Exception" | "BaseException"]) + .is_some_and(|qualified_name| { + matches!( + qualified_name.segments(), + ["", "Exception" | "BaseException"] + ) }) } }) diff --git a/crates/ruff_linter/src/rules/flake8_bandit/rules/bad_file_permissions.rs b/crates/ruff_linter/src/rules/flake8_bandit/rules/bad_file_permissions.rs index 26c5c7712b056..bff5c20674686 100644 --- a/crates/ruff_linter/src/rules/flake8_bandit/rules/bad_file_permissions.rs +++ b/crates/ruff_linter/src/rules/flake8_bandit/rules/bad_file_permissions.rs @@ -67,7 +67,7 @@ pub(crate) fn bad_file_permissions(checker: &mut Checker, call: &ast::ExprCall) if checker .semantic() .resolve_qualified_name(&call.func) - .is_some_and(|call_path| matches!(call_path.segments(), ["os", "chmod"])) + .is_some_and(|qualified_name| matches!(qualified_name.segments(), ["os", "chmod"])) { if let Some(mode_arg) = call.arguments.find_argument("mode", 1) { match parse_mask(mode_arg, checker.semantic()) { @@ -101,8 +101,8 @@ pub(crate) fn bad_file_permissions(checker: &mut Checker, call: &ast::ExprCall) const WRITE_WORLD: u16 = 0o2; const EXECUTE_GROUP: u16 = 0o10; -fn py_stat(call_path: &QualifiedName) -> Option { - match call_path.segments() { +fn py_stat(qualified_name: &QualifiedName) -> Option { + match qualified_name.segments() { ["stat", "ST_MODE"] => Some(0o0), ["stat", "S_IFDOOR"] => Some(0o0), ["stat", "S_IFPORT"] => Some(0o0), diff --git a/crates/ruff_linter/src/rules/flake8_bandit/rules/django_raw_sql.rs b/crates/ruff_linter/src/rules/flake8_bandit/rules/django_raw_sql.rs index e8a08b65f3c73..577accaa36f25 100644 --- a/crates/ruff_linter/src/rules/flake8_bandit/rules/django_raw_sql.rs +++ b/crates/ruff_linter/src/rules/flake8_bandit/rules/django_raw_sql.rs @@ -43,9 +43,9 @@ pub(crate) fn django_raw_sql(checker: &mut Checker, call: &ast::ExprCall) { if checker .semantic() .resolve_qualified_name(&call.func) - .is_some_and(|call_path| { + .is_some_and(|qualified_name| { matches!( - call_path.segments(), + qualified_name.segments(), ["django", "db", "models", "expressions", "RawSQL"] ) }) diff --git a/crates/ruff_linter/src/rules/flake8_bandit/rules/exec_used.rs b/crates/ruff_linter/src/rules/flake8_bandit/rules/exec_used.rs index 1b3e8e371af46..83eae0aec6ce0 100644 --- a/crates/ruff_linter/src/rules/flake8_bandit/rules/exec_used.rs +++ b/crates/ruff_linter/src/rules/flake8_bandit/rules/exec_used.rs @@ -36,7 +36,7 @@ pub(crate) fn exec_used(checker: &mut Checker, func: &Expr) { if checker .semantic() .resolve_qualified_name(func) - .is_some_and(|call_path| matches!(call_path.segments(), ["" | "builtin", "exec"])) + .is_some_and(|qualified_name| matches!(qualified_name.segments(), ["" | "builtin", "exec"])) { checker .diagnostics diff --git a/crates/ruff_linter/src/rules/flake8_bandit/rules/flask_debug_true.rs b/crates/ruff_linter/src/rules/flake8_bandit/rules/flask_debug_true.rs index 3c3908f0bc8f5..db3e3adf34c8b 100644 --- a/crates/ruff_linter/src/rules/flake8_bandit/rules/flask_debug_true.rs +++ b/crates/ruff_linter/src/rules/flake8_bandit/rules/flask_debug_true.rs @@ -65,7 +65,7 @@ pub(crate) fn flask_debug_true(checker: &mut Checker, call: &ExprCall) { } if typing::resolve_assignment(value, checker.semantic()) - .is_some_and(|call_path| matches!(call_path.segments(), ["flask", "Flask"])) + .is_some_and(|qualified_name| matches!(qualified_name.segments(), ["flask", "Flask"])) { checker .diagnostics diff --git a/crates/ruff_linter/src/rules/flake8_bandit/rules/hardcoded_tmp_directory.rs b/crates/ruff_linter/src/rules/flake8_bandit/rules/hardcoded_tmp_directory.rs index b3866efed9c12..fc74174fe32be 100644 --- a/crates/ruff_linter/src/rules/flake8_bandit/rules/hardcoded_tmp_directory.rs +++ b/crates/ruff_linter/src/rules/flake8_bandit/rules/hardcoded_tmp_directory.rs @@ -75,7 +75,7 @@ pub(crate) fn hardcoded_tmp_directory(checker: &mut Checker, string: StringLike) if checker .semantic() .resolve_qualified_name(func) - .is_some_and(|call_path| matches!(call_path.segments(), ["tempfile", ..])) + .is_some_and(|qualified_name| matches!(qualified_name.segments(), ["tempfile", ..])) { return; } diff --git a/crates/ruff_linter/src/rules/flake8_bandit/rules/hashlib_insecure_hash_functions.rs b/crates/ruff_linter/src/rules/flake8_bandit/rules/hashlib_insecure_hash_functions.rs index 0e1d3b850c5a9..38520e804bd74 100644 --- a/crates/ruff_linter/src/rules/flake8_bandit/rules/hashlib_insecure_hash_functions.rs +++ b/crates/ruff_linter/src/rules/flake8_bandit/rules/hashlib_insecure_hash_functions.rs @@ -64,7 +64,7 @@ pub(crate) fn hashlib_insecure_hash_functions(checker: &mut Checker, call: &ast: if let Some(hashlib_call) = checker .semantic() .resolve_qualified_name(&call.func) - .and_then(|call_path| match call_path.segments() { + .and_then(|qualified_name| match qualified_name.segments() { ["hashlib", "new"] => Some(HashlibCall::New), ["hashlib", "md4"] => Some(HashlibCall::WeakHash("md4")), ["hashlib", "md5"] => Some(HashlibCall::WeakHash("md5")), diff --git a/crates/ruff_linter/src/rules/flake8_bandit/rules/jinja2_autoescape_false.rs b/crates/ruff_linter/src/rules/flake8_bandit/rules/jinja2_autoescape_false.rs index 5c98141846c9f..128d0c9c1c8c4 100644 --- a/crates/ruff_linter/src/rules/flake8_bandit/rules/jinja2_autoescape_false.rs +++ b/crates/ruff_linter/src/rules/flake8_bandit/rules/jinja2_autoescape_false.rs @@ -62,7 +62,9 @@ pub(crate) fn jinja2_autoescape_false(checker: &mut Checker, call: &ast::ExprCal if checker .semantic() .resolve_qualified_name(&call.func) - .is_some_and(|call_path| matches!(call_path.segments(), ["jinja2", "Environment"])) + .is_some_and(|qualifieed_name| { + matches!(qualifieed_name.segments(), ["jinja2", "Environment"]) + }) { if let Some(keyword) = call.arguments.find_keyword("autoescape") { match &keyword.value { diff --git a/crates/ruff_linter/src/rules/flake8_bandit/rules/logging_config_insecure_listen.rs b/crates/ruff_linter/src/rules/flake8_bandit/rules/logging_config_insecure_listen.rs index 4dbec356f1dd3..d92eb3466dfdf 100644 --- a/crates/ruff_linter/src/rules/flake8_bandit/rules/logging_config_insecure_listen.rs +++ b/crates/ruff_linter/src/rules/flake8_bandit/rules/logging_config_insecure_listen.rs @@ -43,7 +43,9 @@ pub(crate) fn logging_config_insecure_listen(checker: &mut Checker, call: &ast:: if checker .semantic() .resolve_qualified_name(&call.func) - .is_some_and(|call_path| matches!(call_path.segments(), ["logging", "config", "listen"])) + .is_some_and(|qualified_name| { + matches!(qualified_name.segments(), ["logging", "config", "listen"]) + }) { if call.arguments.find_keyword("verify").is_some() { return; diff --git a/crates/ruff_linter/src/rules/flake8_bandit/rules/mako_templates.rs b/crates/ruff_linter/src/rules/flake8_bandit/rules/mako_templates.rs index a1393f69a0378..d542487cc74bd 100644 --- a/crates/ruff_linter/src/rules/flake8_bandit/rules/mako_templates.rs +++ b/crates/ruff_linter/src/rules/flake8_bandit/rules/mako_templates.rs @@ -48,7 +48,9 @@ pub(crate) fn mako_templates(checker: &mut Checker, call: &ast::ExprCall) { if checker .semantic() .resolve_qualified_name(&call.func) - .is_some_and(|call_path| matches!(call_path.segments(), ["mako", "template", "Template"])) + .is_some_and(|qualified_name| { + matches!(qualified_name.segments(), ["mako", "template", "Template"]) + }) { checker .diagnostics diff --git a/crates/ruff_linter/src/rules/flake8_bandit/rules/paramiko_calls.rs b/crates/ruff_linter/src/rules/flake8_bandit/rules/paramiko_calls.rs index 2b6a3d7a75072..8cf2cd3870ca6 100644 --- a/crates/ruff_linter/src/rules/flake8_bandit/rules/paramiko_calls.rs +++ b/crates/ruff_linter/src/rules/flake8_bandit/rules/paramiko_calls.rs @@ -40,7 +40,9 @@ pub(crate) fn paramiko_call(checker: &mut Checker, func: &Expr) { if checker .semantic() .resolve_qualified_name(func) - .is_some_and(|call_path| matches!(call_path.segments(), ["paramiko", "exec_command"])) + .is_some_and(|qualified_name| { + matches!(qualified_name.segments(), ["paramiko", "exec_command"]) + }) { checker .diagnostics diff --git a/crates/ruff_linter/src/rules/flake8_bandit/rules/request_with_no_cert_validation.rs b/crates/ruff_linter/src/rules/flake8_bandit/rules/request_with_no_cert_validation.rs index 5897e74c6135c..463a73376be7f 100644 --- a/crates/ruff_linter/src/rules/flake8_bandit/rules/request_with_no_cert_validation.rs +++ b/crates/ruff_linter/src/rules/flake8_bandit/rules/request_with_no_cert_validation.rs @@ -50,7 +50,7 @@ pub(crate) fn request_with_no_cert_validation(checker: &mut Checker, call: &ast: if let Some(target) = checker .semantic() .resolve_qualified_name(&call.func) - .and_then(|call_path| match call_path.segments() { + .and_then(|qualified_name| match qualified_name.segments() { ["requests", "get" | "options" | "head" | "post" | "put" | "patch" | "delete"] => { Some("requests") } diff --git a/crates/ruff_linter/src/rules/flake8_bandit/rules/request_without_timeout.rs b/crates/ruff_linter/src/rules/flake8_bandit/rules/request_without_timeout.rs index b01bebe40f58b..26152e9ef76b2 100644 --- a/crates/ruff_linter/src/rules/flake8_bandit/rules/request_without_timeout.rs +++ b/crates/ruff_linter/src/rules/flake8_bandit/rules/request_without_timeout.rs @@ -53,9 +53,9 @@ pub(crate) fn request_without_timeout(checker: &mut Checker, call: &ast::ExprCal if checker .semantic() .resolve_qualified_name(&call.func) - .is_some_and(|call_path| { + .is_some_and(|qualified_name| { matches!( - call_path.segments(), + qualified_name.segments(), [ "requests", "get" | "options" | "head" | "post" | "put" | "patch" | "delete" diff --git a/crates/ruff_linter/src/rules/flake8_bandit/rules/shell_injection.rs b/crates/ruff_linter/src/rules/flake8_bandit/rules/shell_injection.rs index 0b2a3cff15097..952c8b11f418e 100644 --- a/crates/ruff_linter/src/rules/flake8_bandit/rules/shell_injection.rs +++ b/crates/ruff_linter/src/rules/flake8_bandit/rules/shell_injection.rs @@ -420,7 +420,7 @@ enum CallKind { fn get_call_kind(func: &Expr, semantic: &SemanticModel) -> Option { semantic .resolve_qualified_name(func) - .and_then(|call_path| match call_path.segments() { + .and_then(|qualified_name| match qualified_name.segments() { &[module, submodule] => match module { "os" => match submodule { "execl" | "execle" | "execlp" | "execlpe" | "execv" | "execve" | "execvp" diff --git a/crates/ruff_linter/src/rules/flake8_bandit/rules/snmp_insecure_version.rs b/crates/ruff_linter/src/rules/flake8_bandit/rules/snmp_insecure_version.rs index a2c239d80b2a4..0c34637456c0d 100644 --- a/crates/ruff_linter/src/rules/flake8_bandit/rules/snmp_insecure_version.rs +++ b/crates/ruff_linter/src/rules/flake8_bandit/rules/snmp_insecure_version.rs @@ -45,8 +45,11 @@ pub(crate) fn snmp_insecure_version(checker: &mut Checker, call: &ast::ExprCall) if checker .semantic() .resolve_qualified_name(&call.func) - .is_some_and(|call_path| { - matches!(call_path.segments(), ["pysnmp", "hlapi", "CommunityData"]) + .is_some_and(|qualified_name| { + matches!( + qualified_name.segments(), + ["pysnmp", "hlapi", "CommunityData"] + ) }) { if let Some(keyword) = call.arguments.find_keyword("mpModel") { diff --git a/crates/ruff_linter/src/rules/flake8_bandit/rules/snmp_weak_cryptography.rs b/crates/ruff_linter/src/rules/flake8_bandit/rules/snmp_weak_cryptography.rs index 96dce4089be90..73b72335179d1 100644 --- a/crates/ruff_linter/src/rules/flake8_bandit/rules/snmp_weak_cryptography.rs +++ b/crates/ruff_linter/src/rules/flake8_bandit/rules/snmp_weak_cryptography.rs @@ -46,8 +46,11 @@ pub(crate) fn snmp_weak_cryptography(checker: &mut Checker, call: &ast::ExprCall if checker .semantic() .resolve_qualified_name(&call.func) - .is_some_and(|call_path| { - matches!(call_path.segments(), ["pysnmp", "hlapi", "UsmUserData"]) + .is_some_and(|qualified_name| { + matches!( + qualified_name.segments(), + ["pysnmp", "hlapi", "UsmUserData"] + ) }) { checker diff --git a/crates/ruff_linter/src/rules/flake8_bandit/rules/ssh_no_host_key_verification.rs b/crates/ruff_linter/src/rules/flake8_bandit/rules/ssh_no_host_key_verification.rs index 7735113ee685c..8084aa0676ec1 100644 --- a/crates/ruff_linter/src/rules/flake8_bandit/rules/ssh_no_host_key_verification.rs +++ b/crates/ruff_linter/src/rules/flake8_bandit/rules/ssh_no_host_key_verification.rs @@ -61,9 +61,9 @@ pub(crate) fn ssh_no_host_key_verification(checker: &mut Checker, call: &ExprCal if !checker .semantic() .resolve_qualified_name(map_callable(policy_argument)) - .is_some_and(|call_path| { + .is_some_and(|qualified_name| { matches!( - call_path.segments(), + qualified_name.segments(), ["paramiko", "client", "AutoAddPolicy" | "WarningPolicy"] | ["paramiko", "AutoAddPolicy" | "WarningPolicy"] ) @@ -72,9 +72,9 @@ pub(crate) fn ssh_no_host_key_verification(checker: &mut Checker, call: &ExprCal return; } - if typing::resolve_assignment(value, checker.semantic()).is_some_and(|call_path| { + if typing::resolve_assignment(value, checker.semantic()).is_some_and(|qualified_name| { matches!( - call_path.segments(), + qualified_name.segments(), ["paramiko", "client", "SSHClient"] | ["paramiko", "SSHClient"] ) }) { diff --git a/crates/ruff_linter/src/rules/flake8_bandit/rules/ssl_insecure_version.rs b/crates/ruff_linter/src/rules/flake8_bandit/rules/ssl_insecure_version.rs index 4efe63e7af664..796c7979a3e29 100644 --- a/crates/ruff_linter/src/rules/flake8_bandit/rules/ssl_insecure_version.rs +++ b/crates/ruff_linter/src/rules/flake8_bandit/rules/ssl_insecure_version.rs @@ -52,7 +52,7 @@ pub(crate) fn ssl_insecure_version(checker: &mut Checker, call: &ExprCall) { let Some(keyword) = checker .semantic() .resolve_qualified_name(call.func.as_ref()) - .and_then(|call_path| match call_path.segments() { + .and_then(|qualified_name| match qualified_name.segments() { ["ssl", "wrap_socket"] => Some("ssl_version"), ["OpenSSL", "SSL", "Context"] => Some("method"), _ => None, diff --git a/crates/ruff_linter/src/rules/flake8_bandit/rules/ssl_with_no_version.rs b/crates/ruff_linter/src/rules/flake8_bandit/rules/ssl_with_no_version.rs index 5e59d5ab592cd..13514f95ce1cd 100644 --- a/crates/ruff_linter/src/rules/flake8_bandit/rules/ssl_with_no_version.rs +++ b/crates/ruff_linter/src/rules/flake8_bandit/rules/ssl_with_no_version.rs @@ -40,7 +40,7 @@ pub(crate) fn ssl_with_no_version(checker: &mut Checker, call: &ExprCall) { if checker .semantic() .resolve_qualified_name(call.func.as_ref()) - .is_some_and(|call_path| matches!(call_path.segments(), ["ssl", "wrap_socket"])) + .is_some_and(|qualified_name| matches!(qualified_name.segments(), ["ssl", "wrap_socket"])) { if call.arguments.find_keyword("ssl_version").is_none() { checker diff --git a/crates/ruff_linter/src/rules/flake8_bandit/rules/suspicious_function_call.rs b/crates/ruff_linter/src/rules/flake8_bandit/rules/suspicious_function_call.rs index 26bf8c02307c8..bcd7d305fe87d 100644 --- a/crates/ruff_linter/src/rules/flake8_bandit/rules/suspicious_function_call.rs +++ b/crates/ruff_linter/src/rules/flake8_bandit/rules/suspicious_function_call.rs @@ -825,8 +825,8 @@ impl Violation for SuspiciousFTPLibUsage { /// S301, S302, S303, S304, S305, S306, S307, S308, S310, S311, S312, S313, S314, S315, S316, S317, S318, S319, S320, S321, S323 pub(crate) fn suspicious_function_call(checker: &mut Checker, call: &ExprCall) { - let Some(diagnostic_kind) = checker.semantic().resolve_qualified_name(call.func.as_ref()).and_then(|call_path| { - match call_path.segments() { + let Some(diagnostic_kind) = checker.semantic().resolve_qualified_name(call.func.as_ref()).and_then(|qualified_name| { + match qualified_name.segments() { // Pickle ["pickle" | "dill", "load" | "loads" | "Unpickler"] | ["shelve", "open" | "DbfilenameShelf"] | @@ -907,8 +907,8 @@ pub(crate) fn suspicious_function_decorator(checker: &mut Checker, decorator: &D let Some(diagnostic_kind) = checker .semantic() .resolve_qualified_name(&decorator.expression) - .and_then(|call_path| { - match call_path.segments() { + .and_then(|qualified_name| { + match qualified_name.segments() { // MarkSafe ["django", "utils", "safestring" | "html", "mark_safe"] => { Some(SuspiciousMarkSafeUsage.into()) diff --git a/crates/ruff_linter/src/rules/flake8_bandit/rules/unsafe_yaml_load.rs b/crates/ruff_linter/src/rules/flake8_bandit/rules/unsafe_yaml_load.rs index 860871fde2b0c..f63e134b333f3 100644 --- a/crates/ruff_linter/src/rules/flake8_bandit/rules/unsafe_yaml_load.rs +++ b/crates/ruff_linter/src/rules/flake8_bandit/rules/unsafe_yaml_load.rs @@ -63,15 +63,15 @@ pub(crate) fn unsafe_yaml_load(checker: &mut Checker, call: &ast::ExprCall) { if checker .semantic() .resolve_qualified_name(&call.func) - .is_some_and(|call_path| matches!(call_path.segments(), ["yaml", "load"])) + .is_some_and(|qualified_name| matches!(qualified_name.segments(), ["yaml", "load"])) { if let Some(loader_arg) = call.arguments.find_argument("Loader", 1) { if !checker .semantic() .resolve_qualified_name(loader_arg) - .is_some_and(|call_path| { + .is_some_and(|qualified_name| { matches!( - call_path.segments(), + qualified_name.segments(), ["yaml", "SafeLoader" | "CSafeLoader"] | ["yaml", "loader", "SafeLoader" | "CSafeLoader"] ) diff --git a/crates/ruff_linter/src/rules/flake8_bandit/rules/weak_cryptographic_key.rs b/crates/ruff_linter/src/rules/flake8_bandit/rules/weak_cryptographic_key.rs index 1f0d84d7c0ded..48137db7fb4d0 100644 --- a/crates/ruff_linter/src/rules/flake8_bandit/rules/weak_cryptographic_key.rs +++ b/crates/ruff_linter/src/rules/flake8_bandit/rules/weak_cryptographic_key.rs @@ -101,8 +101,8 @@ fn extract_cryptographic_key( checker: &mut Checker, call: &ExprCall, ) -> Option<(CryptographicKey, TextRange)> { - let call_path = checker.semantic().resolve_qualified_name(&call.func)?; - match call_path.segments() { + let qualified_name = checker.semantic().resolve_qualified_name(&call.func)?; + match qualified_name.segments() { ["cryptography", "hazmat", "primitives", "asymmetric", function, "generate_private_key"] => { match *function { "dsa" => { @@ -116,9 +116,9 @@ fn extract_cryptographic_key( "ec" => { let argument = call.arguments.find_argument("curve", 0)?; let ExprAttribute { attr, value, .. } = argument.as_attribute_expr()?; - let call_path = checker.semantic().resolve_qualified_name(value)?; + let qualified_name = checker.semantic().resolve_qualified_name(value)?; if matches!( - call_path.segments(), + qualified_name.segments(), ["cryptography", "hazmat", "primitives", "asymmetric", "ec"] ) { Some(( diff --git a/crates/ruff_linter/src/rules/flake8_blind_except/rules/blind_except.rs b/crates/ruff_linter/src/rules/flake8_blind_except/rules/blind_except.rs index b71ab106b54f0..7d30d4da8577c 100644 --- a/crates/ruff_linter/src/rules/flake8_blind_except/rules/blind_except.rs +++ b/crates/ruff_linter/src/rules/flake8_blind_except/rules/blind_except.rs @@ -141,7 +141,7 @@ pub(crate) fn blind_except( if checker .semantic() .resolve_qualified_name(func.as_ref()) - .is_some_and(|call_path| match call_path.segments() { + .is_some_and(|qualified_name| match qualified_name.segments() { ["logging", "exception"] => true, ["logging", "error"] => { if let Some(keyword) = arguments.find_keyword("exc_info") { diff --git a/crates/ruff_linter/src/rules/flake8_boolean_trap/rules/boolean_default_value_positional_argument.rs b/crates/ruff_linter/src/rules/flake8_boolean_trap/rules/boolean_default_value_positional_argument.rs index bf9be535018d1..7977fc0d47ab8 100644 --- a/crates/ruff_linter/src/rules/flake8_boolean_trap/rules/boolean_default_value_positional_argument.rs +++ b/crates/ruff_linter/src/rules/flake8_boolean_trap/rules/boolean_default_value_positional_argument.rs @@ -120,7 +120,7 @@ pub(crate) fn boolean_default_value_positional_argument( // Allow Boolean defaults in setters. if decorator_list.iter().any(|decorator| { UnqualifiedName::from_expr(&decorator.expression) - .is_some_and(|call_path| call_path.segments() == [name, "setter"]) + .is_some_and(|unqualified_name| unqualified_name.segments() == [name, "setter"]) }) { return; } diff --git a/crates/ruff_linter/src/rules/flake8_boolean_trap/rules/boolean_type_hint_positional_argument.rs b/crates/ruff_linter/src/rules/flake8_boolean_trap/rules/boolean_type_hint_positional_argument.rs index 12063768089b7..81e0dc5dd0382 100644 --- a/crates/ruff_linter/src/rules/flake8_boolean_trap/rules/boolean_type_hint_positional_argument.rs +++ b/crates/ruff_linter/src/rules/flake8_boolean_trap/rules/boolean_type_hint_positional_argument.rs @@ -136,7 +136,7 @@ pub(crate) fn boolean_type_hint_positional_argument( // Allow Boolean type hints in setters. if decorator_list.iter().any(|decorator| { UnqualifiedName::from_expr(&decorator.expression) - .is_some_and(|call_path| call_path.segments() == [name, "setter"]) + .is_some_and(|unqualified_name| unqualified_name.segments() == [name, "setter"]) }) { return; } @@ -195,11 +195,10 @@ fn match_annotation_to_complex_bool(annotation: &Expr, semantic: &SemanticModel) return false; } - let call_path = semantic.resolve_qualified_name(value); - if call_path - .as_ref() - .is_some_and(|call_path| semantic.match_typing_call_path(call_path, "Union")) - { + let qualified_name = semantic.resolve_qualified_name(value); + if qualified_name.as_ref().is_some_and(|qualified_name| { + semantic.match_typing_qualified_name(qualified_name, "Union") + }) { if let Expr::Tuple(ast::ExprTuple { elts, .. }) = slice.as_ref() { elts.iter() .any(|elt| match_annotation_to_complex_bool(elt, semantic)) @@ -207,10 +206,9 @@ fn match_annotation_to_complex_bool(annotation: &Expr, semantic: &SemanticModel) // Union with a single type is an invalid type annotation false } - } else if call_path - .as_ref() - .is_some_and(|call_path| semantic.match_typing_call_path(call_path, "Optional")) - { + } else if qualified_name.as_ref().is_some_and(|qualified_name| { + semantic.match_typing_qualified_name(qualified_name, "Optional") + }) { match_annotation_to_complex_bool(slice, semantic) } else { false diff --git a/crates/ruff_linter/src/rules/flake8_bugbear/rules/abstract_base_class.rs b/crates/ruff_linter/src/rules/flake8_bugbear/rules/abstract_base_class.rs index 6901563ca7223..b7ec09e599be0 100644 --- a/crates/ruff_linter/src/rules/flake8_bugbear/rules/abstract_base_class.rs +++ b/crates/ruff_linter/src/rules/flake8_bugbear/rules/abstract_base_class.rs @@ -110,11 +110,13 @@ fn is_abc_class(bases: &[Expr], keywords: &[Keyword], semantic: &SemanticModel) keyword.arg.as_ref().is_some_and(|arg| arg == "metaclass") && semantic .resolve_qualified_name(&keyword.value) - .is_some_and(|call_path| matches!(call_path.segments(), ["abc", "ABCMeta"])) + .is_some_and(|qualified_name| { + matches!(qualified_name.segments(), ["abc", "ABCMeta"]) + }) }) || bases.iter().any(|base| { semantic .resolve_qualified_name(base) - .is_some_and(|call_path| matches!(call_path.segments(), ["abc", "ABC"])) + .is_some_and(|qualified_name| matches!(qualified_name.segments(), ["abc", "ABC"])) }) } diff --git a/crates/ruff_linter/src/rules/flake8_bugbear/rules/assert_raises_exception.rs b/crates/ruff_linter/src/rules/flake8_bugbear/rules/assert_raises_exception.rs index 53b668744f631..ee2595ae283ea 100644 --- a/crates/ruff_linter/src/rules/flake8_bugbear/rules/assert_raises_exception.rs +++ b/crates/ruff_linter/src/rules/flake8_bugbear/rules/assert_raises_exception.rs @@ -99,7 +99,7 @@ pub(crate) fn assert_raises_exception(checker: &mut Checker, items: &[WithItem]) checker .semantic() .resolve_qualified_name(arg) - .and_then(|call_path| match call_path.segments() { + .and_then(|qualified_name| match qualified_name.segments() { ["", "Exception"] => Some(ExceptionKind::Exception), ["", "BaseException"] => Some(ExceptionKind::BaseException), _ => None, @@ -114,7 +114,7 @@ pub(crate) fn assert_raises_exception(checker: &mut Checker, items: &[WithItem]) } else if checker .semantic() .resolve_qualified_name(func) - .is_some_and(|call_path| matches!(call_path.segments(), ["pytest", "raises"])) + .is_some_and(|qualified_name| matches!(qualified_name.segments(), ["pytest", "raises"])) && arguments.find_keyword("match").is_none() { AssertionKind::PytestRaises diff --git a/crates/ruff_linter/src/rules/flake8_bugbear/rules/cached_instance_method.rs b/crates/ruff_linter/src/rules/flake8_bugbear/rules/cached_instance_method.rs index 89848449d69b8..016bc6bac5412 100644 --- a/crates/ruff_linter/src/rules/flake8_bugbear/rules/cached_instance_method.rs +++ b/crates/ruff_linter/src/rules/flake8_bugbear/rules/cached_instance_method.rs @@ -73,8 +73,11 @@ impl Violation for CachedInstanceMethod { fn is_cache_func(expr: &Expr, semantic: &SemanticModel) -> bool { semantic .resolve_qualified_name(expr) - .is_some_and(|call_path| { - matches!(call_path.segments(), ["functools", "lru_cache" | "cache"]) + .is_some_and(|qualified_name| { + matches!( + qualified_name.segments(), + ["functools", "lru_cache" | "cache"] + ) }) } diff --git a/crates/ruff_linter/src/rules/flake8_bugbear/rules/duplicate_exceptions.rs b/crates/ruff_linter/src/rules/flake8_bugbear/rules/duplicate_exceptions.rs index 54773bec14463..450253b18defc 100644 --- a/crates/ruff_linter/src/rules/flake8_bugbear/rules/duplicate_exceptions.rs +++ b/crates/ruff_linter/src/rules/flake8_bugbear/rules/duplicate_exceptions.rs @@ -123,11 +123,11 @@ fn duplicate_handler_exceptions<'a>( let mut duplicates: FxHashSet = FxHashSet::default(); let mut unique_elts: Vec<&Expr> = Vec::default(); for type_ in elts { - if let Some(call_path) = UnqualifiedName::from_expr(type_) { - if seen.contains_key(&call_path) { - duplicates.insert(call_path); + if let Some(name) = UnqualifiedName::from_expr(type_) { + if seen.contains_key(&name) { + duplicates.insert(name); } else { - seen.entry(call_path).or_insert(type_); + seen.entry(name).or_insert(type_); unique_elts.push(type_); } } @@ -140,7 +140,7 @@ fn duplicate_handler_exceptions<'a>( DuplicateHandlerException { names: duplicates .into_iter() - .map(|call_path| call_path.segments().join(".")) + .map(|qualified_name| qualified_name.segments().join(".")) .sorted() .collect::>(), }, @@ -183,11 +183,11 @@ pub(crate) fn duplicate_exceptions(checker: &mut Checker, handlers: &[ExceptHand }; match type_.as_ref() { Expr::Attribute(_) | Expr::Name(_) => { - if let Some(call_path) = UnqualifiedName::from_expr(type_) { - if seen.contains(&call_path) { - duplicates.entry(call_path).or_default().push(type_); + if let Some(name) = UnqualifiedName::from_expr(type_) { + if seen.contains(&name) { + duplicates.entry(name).or_default().push(type_); } else { - seen.insert(call_path); + seen.insert(name); } } } diff --git a/crates/ruff_linter/src/rules/flake8_bugbear/rules/function_call_in_argument_default.rs b/crates/ruff_linter/src/rules/flake8_bugbear/rules/function_call_in_argument_default.rs index d59addb54a602..dd677dcd69c33 100644 --- a/crates/ruff_linter/src/rules/flake8_bugbear/rules/function_call_in_argument_default.rs +++ b/crates/ruff_linter/src/rules/flake8_bugbear/rules/function_call_in_argument_default.rs @@ -4,7 +4,7 @@ use ruff_text_size::{Ranged, TextRange}; use ruff_diagnostics::Violation; use ruff_diagnostics::{Diagnostic, DiagnosticKind}; use ruff_macros::{derive_message_formats, violation}; -use ruff_python_ast::name::{compose_call_path, QualifiedName}; +use ruff_python_ast::name::{QualifiedName, UnqualifiedName}; use ruff_python_ast::visitor; use ruff_python_ast::visitor::Visitor; use ruff_python_semantic::analyze::typing::{ @@ -107,7 +107,7 @@ impl Visitor<'_> for ArgumentDefaultVisitor<'_, '_> { { self.diagnostics.push(( FunctionCallInDefaultArgument { - name: compose_call_path(func), + name: UnqualifiedName::from_expr(func).map(|name| name.to_string()), } .into(), expr.range(), diff --git a/crates/ruff_linter/src/rules/flake8_bugbear/rules/no_explicit_stacklevel.rs b/crates/ruff_linter/src/rules/flake8_bugbear/rules/no_explicit_stacklevel.rs index 9d23c7aaf0221..35c929b91341b 100644 --- a/crates/ruff_linter/src/rules/flake8_bugbear/rules/no_explicit_stacklevel.rs +++ b/crates/ruff_linter/src/rules/flake8_bugbear/rules/no_explicit_stacklevel.rs @@ -41,7 +41,7 @@ pub(crate) fn no_explicit_stacklevel(checker: &mut Checker, call: &ast::ExprCall if !checker .semantic() .resolve_qualified_name(&call.func) - .is_some_and(|call_path| matches!(call_path.segments(), ["warnings", "warn"])) + .is_some_and(|qualified_name| matches!(qualified_name.segments(), ["warnings", "warn"])) { return; } diff --git a/crates/ruff_linter/src/rules/flake8_bugbear/rules/re_sub_positional_args.rs b/crates/ruff_linter/src/rules/flake8_bugbear/rules/re_sub_positional_args.rs index 0b16a793b9bf4..06bbc8cdd8237 100644 --- a/crates/ruff_linter/src/rules/flake8_bugbear/rules/re_sub_positional_args.rs +++ b/crates/ruff_linter/src/rules/flake8_bugbear/rules/re_sub_positional_args.rs @@ -64,7 +64,7 @@ pub(crate) fn re_sub_positional_args(checker: &mut Checker, call: &ast::ExprCall let Some(method) = checker .semantic() .resolve_qualified_name(&call.func) - .and_then(|call_path| match call_path.segments() { + .and_then(|qualified_name| match qualified_name.segments() { ["re", "sub"] => Some(Method::Sub), ["re", "subn"] => Some(Method::Subn), ["re", "split"] => Some(Method::Split), diff --git a/crates/ruff_linter/src/rules/flake8_bugbear/rules/reuse_of_groupby_generator.rs b/crates/ruff_linter/src/rules/flake8_bugbear/rules/reuse_of_groupby_generator.rs index a687b7f6384f0..269c4c3715133 100644 --- a/crates/ruff_linter/src/rules/flake8_bugbear/rules/reuse_of_groupby_generator.rs +++ b/crates/ruff_linter/src/rules/flake8_bugbear/rules/reuse_of_groupby_generator.rs @@ -311,7 +311,7 @@ pub(crate) fn reuse_of_groupby_generator( if !checker .semantic() .resolve_qualified_name(func) - .is_some_and(|call_path| matches!(call_path.segments(), ["itertools", "groupby"])) + .is_some_and(|qualified_name| matches!(qualified_name.segments(), ["itertools", "groupby"])) { return; } diff --git a/crates/ruff_linter/src/rules/flake8_bugbear/rules/useless_contextlib_suppress.rs b/crates/ruff_linter/src/rules/flake8_bugbear/rules/useless_contextlib_suppress.rs index e11f7e6bd7da4..e96b6c631dac8 100644 --- a/crates/ruff_linter/src/rules/flake8_bugbear/rules/useless_contextlib_suppress.rs +++ b/crates/ruff_linter/src/rules/flake8_bugbear/rules/useless_contextlib_suppress.rs @@ -60,7 +60,9 @@ pub(crate) fn useless_contextlib_suppress( && checker .semantic() .resolve_qualified_name(func) - .is_some_and(|call_path| matches!(call_path.segments(), ["contextlib", "suppress"])) + .is_some_and(|qualified_name| { + matches!(qualified_name.segments(), ["contextlib", "suppress"]) + }) { checker .diagnostics diff --git a/crates/ruff_linter/src/rules/flake8_bugbear/rules/zip_without_explicit_strict.rs b/crates/ruff_linter/src/rules/flake8_bugbear/rules/zip_without_explicit_strict.rs index 6fd6656c0ffa9..f2514b7d42d70 100644 --- a/crates/ruff_linter/src/rules/flake8_bugbear/rules/zip_without_explicit_strict.rs +++ b/crates/ruff_linter/src/rules/flake8_bugbear/rules/zip_without_explicit_strict.rs @@ -101,8 +101,8 @@ fn is_infinite_iterator(arg: &Expr, semantic: &SemanticModel) -> bool { semantic .resolve_qualified_name(func) - .is_some_and(|call_path| { - match call_path.segments() { + .is_some_and(|qualified_name| { + match qualified_name.segments() { ["itertools", "cycle" | "count"] => true, ["itertools", "repeat"] => { // Ex) `itertools.repeat(1)` diff --git a/crates/ruff_linter/src/rules/flake8_datetimez/rules/call_date_fromtimestamp.rs b/crates/ruff_linter/src/rules/flake8_datetimez/rules/call_date_fromtimestamp.rs index b6d1400cb90aa..1a022a3010fb0 100644 --- a/crates/ruff_linter/src/rules/flake8_datetimez/rules/call_date_fromtimestamp.rs +++ b/crates/ruff_linter/src/rules/flake8_datetimez/rules/call_date_fromtimestamp.rs @@ -65,8 +65,11 @@ pub(crate) fn call_date_fromtimestamp(checker: &mut Checker, func: &Expr, locati if checker .semantic() .resolve_qualified_name(func) - .is_some_and(|call_path| { - matches!(call_path.segments(), ["datetime", "date", "fromtimestamp"]) + .is_some_and(|qualified_name| { + matches!( + qualified_name.segments(), + ["datetime", "date", "fromtimestamp"] + ) }) { checker diff --git a/crates/ruff_linter/src/rules/flake8_datetimez/rules/call_date_today.rs b/crates/ruff_linter/src/rules/flake8_datetimez/rules/call_date_today.rs index febfdaee6f0c3..9e0bacbff8921 100644 --- a/crates/ruff_linter/src/rules/flake8_datetimez/rules/call_date_today.rs +++ b/crates/ruff_linter/src/rules/flake8_datetimez/rules/call_date_today.rs @@ -64,7 +64,9 @@ pub(crate) fn call_date_today(checker: &mut Checker, func: &Expr, location: Text if checker .semantic() .resolve_qualified_name(func) - .is_some_and(|call_path| matches!(call_path.segments(), ["datetime", "date", "today"])) + .is_some_and(|qualified_name| { + matches!(qualified_name.segments(), ["datetime", "date", "today"]) + }) { checker .diagnostics diff --git a/crates/ruff_linter/src/rules/flake8_datetimez/rules/call_datetime_fromtimestamp.rs b/crates/ruff_linter/src/rules/flake8_datetimez/rules/call_datetime_fromtimestamp.rs index 2c9bc216b794d..30e67d827e06e 100644 --- a/crates/ruff_linter/src/rules/flake8_datetimez/rules/call_datetime_fromtimestamp.rs +++ b/crates/ruff_linter/src/rules/flake8_datetimez/rules/call_datetime_fromtimestamp.rs @@ -68,9 +68,9 @@ pub(crate) fn call_datetime_fromtimestamp(checker: &mut Checker, call: &ast::Exp if !checker .semantic() .resolve_qualified_name(&call.func) - .is_some_and(|call_path| { + .is_some_and(|qualified_name| { matches!( - call_path.segments(), + qualified_name.segments(), ["datetime", "datetime", "fromtimestamp"] ) }) diff --git a/crates/ruff_linter/src/rules/flake8_datetimez/rules/call_datetime_now_without_tzinfo.rs b/crates/ruff_linter/src/rules/flake8_datetimez/rules/call_datetime_now_without_tzinfo.rs index d2aaa78a07cde..6a40aa2734b40 100644 --- a/crates/ruff_linter/src/rules/flake8_datetimez/rules/call_datetime_now_without_tzinfo.rs +++ b/crates/ruff_linter/src/rules/flake8_datetimez/rules/call_datetime_now_without_tzinfo.rs @@ -64,7 +64,9 @@ pub(crate) fn call_datetime_now_without_tzinfo(checker: &mut Checker, call: &ast if !checker .semantic() .resolve_qualified_name(&call.func) - .is_some_and(|call_path| matches!(call_path.segments(), ["datetime", "datetime", "now"])) + .is_some_and(|qualified_name| { + matches!(qualified_name.segments(), ["datetime", "datetime", "now"]) + }) { return; } diff --git a/crates/ruff_linter/src/rules/flake8_datetimez/rules/call_datetime_strptime_without_zone.rs b/crates/ruff_linter/src/rules/flake8_datetimez/rules/call_datetime_strptime_without_zone.rs index 1eb83893d796e..9fdce6ee34829 100644 --- a/crates/ruff_linter/src/rules/flake8_datetimez/rules/call_datetime_strptime_without_zone.rs +++ b/crates/ruff_linter/src/rules/flake8_datetimez/rules/call_datetime_strptime_without_zone.rs @@ -72,8 +72,11 @@ pub(crate) fn call_datetime_strptime_without_zone(checker: &mut Checker, call: & if !checker .semantic() .resolve_qualified_name(&call.func) - .is_some_and(|call_path| { - matches!(call_path.segments(), ["datetime", "datetime", "strptime"]) + .is_some_and(|qualified_name| { + matches!( + qualified_name.segments(), + ["datetime", "datetime", "strptime"] + ) }) { return; diff --git a/crates/ruff_linter/src/rules/flake8_datetimez/rules/call_datetime_today.rs b/crates/ruff_linter/src/rules/flake8_datetimez/rules/call_datetime_today.rs index 82b2d1359a5e7..d712f604ec1d7 100644 --- a/crates/ruff_linter/src/rules/flake8_datetimez/rules/call_datetime_today.rs +++ b/crates/ruff_linter/src/rules/flake8_datetimez/rules/call_datetime_today.rs @@ -63,7 +63,9 @@ pub(crate) fn call_datetime_today(checker: &mut Checker, func: &Expr, location: if !checker .semantic() .resolve_qualified_name(func) - .is_some_and(|call_path| matches!(call_path.segments(), ["datetime", "datetime", "today"])) + .is_some_and(|qualified_name| { + matches!(qualified_name.segments(), ["datetime", "datetime", "today"]) + }) { return; } diff --git a/crates/ruff_linter/src/rules/flake8_datetimez/rules/call_datetime_utcfromtimestamp.rs b/crates/ruff_linter/src/rules/flake8_datetimez/rules/call_datetime_utcfromtimestamp.rs index 1600050fd52d0..2db2649e73793 100644 --- a/crates/ruff_linter/src/rules/flake8_datetimez/rules/call_datetime_utcfromtimestamp.rs +++ b/crates/ruff_linter/src/rules/flake8_datetimez/rules/call_datetime_utcfromtimestamp.rs @@ -71,9 +71,9 @@ pub(crate) fn call_datetime_utcfromtimestamp( if !checker .semantic() .resolve_qualified_name(func) - .is_some_and(|call_path| { + .is_some_and(|qualified_name| { matches!( - call_path.segments(), + qualified_name.segments(), ["datetime", "datetime", "utcfromtimestamp"] ) }) diff --git a/crates/ruff_linter/src/rules/flake8_datetimez/rules/call_datetime_utcnow.rs b/crates/ruff_linter/src/rules/flake8_datetimez/rules/call_datetime_utcnow.rs index 8b43816e566f6..9ff0de0549cb3 100644 --- a/crates/ruff_linter/src/rules/flake8_datetimez/rules/call_datetime_utcnow.rs +++ b/crates/ruff_linter/src/rules/flake8_datetimez/rules/call_datetime_utcnow.rs @@ -67,7 +67,12 @@ pub(crate) fn call_datetime_utcnow(checker: &mut Checker, func: &Expr, location: if !checker .semantic() .resolve_qualified_name(func) - .is_some_and(|call_path| matches!(call_path.segments(), ["datetime", "datetime", "utcnow"])) + .is_some_and(|qualified_name| { + matches!( + qualified_name.segments(), + ["datetime", "datetime", "utcnow"] + ) + }) { return; } diff --git a/crates/ruff_linter/src/rules/flake8_datetimez/rules/call_datetime_without_tzinfo.rs b/crates/ruff_linter/src/rules/flake8_datetimez/rules/call_datetime_without_tzinfo.rs index 2b867ab7270ab..5c3bc9cec39a4 100644 --- a/crates/ruff_linter/src/rules/flake8_datetimez/rules/call_datetime_without_tzinfo.rs +++ b/crates/ruff_linter/src/rules/flake8_datetimez/rules/call_datetime_without_tzinfo.rs @@ -60,7 +60,7 @@ pub(crate) fn call_datetime_without_tzinfo(checker: &mut Checker, call: &ast::Ex if !checker .semantic() .resolve_qualified_name(&call.func) - .is_some_and(|call_path| matches!(call_path.segments(), ["datetime", "datetime"])) + .is_some_and(|qualified_name| matches!(qualified_name.segments(), ["datetime", "datetime"])) { return; } diff --git a/crates/ruff_linter/src/rules/flake8_debugger/rules/debugger.rs b/crates/ruff_linter/src/rules/flake8_debugger/rules/debugger.rs index 16c97e07ebca8..615e7a7916772 100644 --- a/crates/ruff_linter/src/rules/flake8_debugger/rules/debugger.rs +++ b/crates/ruff_linter/src/rules/flake8_debugger/rules/debugger.rs @@ -52,9 +52,9 @@ pub(crate) fn debugger_call(checker: &mut Checker, expr: &Expr, func: &Expr) { checker .semantic() .resolve_qualified_name(func) - .and_then(|call_path| { - if is_debugger_call(&call_path) { - Some(DebuggerUsingType::Call(call_path.to_string())) + .and_then(|qualified_name| { + if is_debugger_call(&qualified_name) { + Some(DebuggerUsingType::Call(qualified_name.to_string())) } else { None } @@ -72,20 +72,20 @@ pub(crate) fn debugger_import(stmt: &Stmt, module: Option<&str>, name: &str) -> let mut builder = QualifiedNameBuilder::from_qualified_name(QualifiedName::imported(module)); builder.push(name); - let call_path = builder.build(); + let qualified_name = builder.build(); - if is_debugger_call(&call_path) { + if is_debugger_call(&qualified_name) { return Some(Diagnostic::new( Debugger { - using_type: DebuggerUsingType::Import(call_path.to_string()), + using_type: DebuggerUsingType::Import(qualified_name.to_string()), }, stmt.range(), )); } } else { - let call_path = QualifiedName::imported(name); + let qualified_name = QualifiedName::imported(name); - if is_debugger_import(&call_path) { + if is_debugger_import(&qualified_name) { return Some(Diagnostic::new( Debugger { using_type: DebuggerUsingType::Import(name.to_string()), @@ -97,9 +97,9 @@ pub(crate) fn debugger_import(stmt: &Stmt, module: Option<&str>, name: &str) -> None } -fn is_debugger_call(call_path: &QualifiedName) -> bool { +fn is_debugger_call(qualified_name: &QualifiedName) -> bool { matches!( - call_path.segments(), + qualified_name.segments(), ["pdb" | "pudb" | "ipdb", "set_trace"] | ["ipdb", "sset_trace"] | ["IPython", "terminal", "embed", "InteractiveShellEmbed"] @@ -117,13 +117,13 @@ fn is_debugger_call(call_path: &QualifiedName) -> bool { ) } -fn is_debugger_import(call_path: &QualifiedName) -> bool { +fn is_debugger_import(qualified_name: &QualifiedName) -> bool { // Constructed by taking every pattern in `is_debugger_call`, removing the last element in // each pattern, and de-duplicating the values. // As a special-case, we omit `builtins` to allow `import builtins`, which is far more general // than (e.g.) `import celery.contrib.rdb`. matches!( - call_path.segments(), + qualified_name.segments(), ["pdb" | "pudb" | "ipdb" | "debugpy" | "ptvsd"] | ["IPython", "terminal", "embed"] | ["IPython", "frontend", "terminal", "embed",] diff --git a/crates/ruff_linter/src/rules/flake8_django/rules/helpers.rs b/crates/ruff_linter/src/rules/flake8_django/rules/helpers.rs index f658dadcbce74..2a8198021a262 100644 --- a/crates/ruff_linter/src/rules/flake8_django/rules/helpers.rs +++ b/crates/ruff_linter/src/rules/flake8_django/rules/helpers.rs @@ -4,16 +4,19 @@ use ruff_python_semantic::{analyze, SemanticModel}; /// Return `true` if a Python class appears to be a Django model, based on its base classes. pub(super) fn is_model(class_def: &ast::StmtClassDef, semantic: &SemanticModel) -> bool { - analyze::class::any_call_path(class_def, semantic, &|call_path| { - matches!(call_path.segments(), ["django", "db", "models", "Model"]) + analyze::class::any_qualified_name(class_def, semantic, &|qualified_name| { + matches!( + qualified_name.segments(), + ["django", "db", "models", "Model"] + ) }) } /// Return `true` if a Python class appears to be a Django model form, based on its base classes. pub(super) fn is_model_form(class_def: &ast::StmtClassDef, semantic: &SemanticModel) -> bool { - analyze::class::any_call_path(class_def, semantic, &|call_path| { + analyze::class::any_qualified_name(class_def, semantic, &|qualified_name| { matches!( - call_path.segments(), + qualified_name.segments(), ["django", "forms", "ModelForm"] | ["django", "forms", "models", "ModelForm"] ) }) @@ -23,8 +26,8 @@ pub(super) fn is_model_form(class_def: &ast::StmtClassDef, semantic: &SemanticMo pub(super) fn is_model_field(expr: &Expr, semantic: &SemanticModel) -> bool { semantic .resolve_qualified_name(expr) - .is_some_and(|call_path| { - call_path + .is_some_and(|qualified_name| { + qualified_name .segments() .starts_with(&["django", "db", "models"]) }) @@ -35,11 +38,13 @@ pub(super) fn get_model_field_name<'a>( expr: &'a Expr, semantic: &'a SemanticModel, ) -> Option<&'a str> { - semantic.resolve_qualified_name(expr).and_then(|call_path| { - let call_path = call_path.segments(); - if !call_path.starts_with(&["django", "db", "models"]) { - return None; - } - call_path.last().copied() - }) + semantic + .resolve_qualified_name(expr) + .and_then(|qualified_name| { + let qualified_name = qualified_name.segments(); + if !qualified_name.starts_with(&["django", "db", "models"]) { + return None; + } + qualified_name.last().copied() + }) } diff --git a/crates/ruff_linter/src/rules/flake8_django/rules/locals_in_render_function.rs b/crates/ruff_linter/src/rules/flake8_django/rules/locals_in_render_function.rs index 43aecd9a72f34..9785a2cb9426a 100644 --- a/crates/ruff_linter/src/rules/flake8_django/rules/locals_in_render_function.rs +++ b/crates/ruff_linter/src/rules/flake8_django/rules/locals_in_render_function.rs @@ -52,7 +52,9 @@ pub(crate) fn locals_in_render_function(checker: &mut Checker, call: &ast::ExprC if !checker .semantic() .resolve_qualified_name(&call.func) - .is_some_and(|call_path| matches!(call_path.segments(), ["django", "shortcuts", "render"])) + .is_some_and(|qualified_name| { + matches!(qualified_name.segments(), ["django", "shortcuts", "render"]) + }) { return; } @@ -73,5 +75,5 @@ fn is_locals_call(expr: &Expr, semantic: &SemanticModel) -> bool { }; semantic .resolve_qualified_name(func) - .is_some_and(|call_path| matches!(call_path.segments(), ["", "locals"])) + .is_some_and(|qualified_name| matches!(qualified_name.segments(), ["", "locals"])) } diff --git a/crates/ruff_linter/src/rules/flake8_django/rules/non_leading_receiver_decorator.rs b/crates/ruff_linter/src/rules/flake8_django/rules/non_leading_receiver_decorator.rs index 5771012c15221..52118e6b67df8 100644 --- a/crates/ruff_linter/src/rules/flake8_django/rules/non_leading_receiver_decorator.rs +++ b/crates/ruff_linter/src/rules/flake8_django/rules/non_leading_receiver_decorator.rs @@ -62,8 +62,11 @@ pub(crate) fn non_leading_receiver_decorator(checker: &mut Checker, decorator_li checker .semantic() .resolve_qualified_name(&call.func) - .is_some_and(|call_path| { - matches!(call_path.segments(), ["django", "dispatch", "receiver"]) + .is_some_and(|qualified_name| { + matches!( + qualified_name.segments(), + ["django", "dispatch", "receiver"] + ) }) }); if i > 0 && is_receiver && !seen_receiver { diff --git a/crates/ruff_linter/src/rules/flake8_logging/rules/direct_logger_instantiation.rs b/crates/ruff_linter/src/rules/flake8_logging/rules/direct_logger_instantiation.rs index a06cf58a05ca9..ee93f507c4ee0 100644 --- a/crates/ruff_linter/src/rules/flake8_logging/rules/direct_logger_instantiation.rs +++ b/crates/ruff_linter/src/rules/flake8_logging/rules/direct_logger_instantiation.rs @@ -62,7 +62,7 @@ pub(crate) fn direct_logger_instantiation(checker: &mut Checker, call: &ast::Exp if checker .semantic() .resolve_qualified_name(call.func.as_ref()) - .is_some_and(|call_path| matches!(call_path.segments(), ["logging", "Logger"])) + .is_some_and(|qualified_name| matches!(qualified_name.segments(), ["logging", "Logger"])) { let mut diagnostic = Diagnostic::new(DirectLoggerInstantiation, call.func.range()); diagnostic.try_set_fix(|| { diff --git a/crates/ruff_linter/src/rules/flake8_logging/rules/exception_without_exc_info.rs b/crates/ruff_linter/src/rules/flake8_logging/rules/exception_without_exc_info.rs index ec39feac73f89..3953dd215c739 100644 --- a/crates/ruff_linter/src/rules/flake8_logging/rules/exception_without_exc_info.rs +++ b/crates/ruff_linter/src/rules/flake8_logging/rules/exception_without_exc_info.rs @@ -63,7 +63,9 @@ pub(crate) fn exception_without_exc_info(checker: &mut Checker, call: &ExprCall) if !checker .semantic() .resolve_qualified_name(call.func.as_ref()) - .is_some_and(|call_path| matches!(call_path.segments(), ["logging", "exception"])) + .is_some_and(|qualified_name| { + matches!(qualified_name.segments(), ["logging", "exception"]) + }) { return; } diff --git a/crates/ruff_linter/src/rules/flake8_logging/rules/invalid_get_logger_argument.rs b/crates/ruff_linter/src/rules/flake8_logging/rules/invalid_get_logger_argument.rs index 80a6725d39cfa..1133b9dbcf3f2 100644 --- a/crates/ruff_linter/src/rules/flake8_logging/rules/invalid_get_logger_argument.rs +++ b/crates/ruff_linter/src/rules/flake8_logging/rules/invalid_get_logger_argument.rs @@ -78,7 +78,7 @@ pub(crate) fn invalid_get_logger_argument(checker: &mut Checker, call: &ast::Exp if !checker .semantic() .resolve_qualified_name(call.func.as_ref()) - .is_some_and(|call_path| matches!(call_path.segments(), ["logging", "getLogger"])) + .is_some_and(|qualified_name| matches!(qualified_name.segments(), ["logging", "getLogger"])) { return; } diff --git a/crates/ruff_linter/src/rules/flake8_logging/rules/undocumented_warn.rs b/crates/ruff_linter/src/rules/flake8_logging/rules/undocumented_warn.rs index e1ef76d2f9dbe..8e38ad5e55a6a 100644 --- a/crates/ruff_linter/src/rules/flake8_logging/rules/undocumented_warn.rs +++ b/crates/ruff_linter/src/rules/flake8_logging/rules/undocumented_warn.rs @@ -56,7 +56,7 @@ pub(crate) fn undocumented_warn(checker: &mut Checker, expr: &Expr) { if checker .semantic() .resolve_qualified_name(expr) - .is_some_and(|call_path| matches!(call_path.segments(), ["logging", "WARN"])) + .is_some_and(|qualified_name| matches!(qualified_name.segments(), ["logging", "WARN"])) { let mut diagnostic = Diagnostic::new(UndocumentedWarn, expr.range()); diagnostic.try_set_fix(|| { diff --git a/crates/ruff_linter/src/rules/flake8_logging_format/rules/logging_call.rs b/crates/ruff_linter/src/rules/flake8_logging_format/rules/logging_call.rs index d27f847a0b534..124759b1f6d59 100644 --- a/crates/ruff_linter/src/rules/flake8_logging_format/rules/logging_call.rs +++ b/crates/ruff_linter/src/rules/flake8_logging_format/rules/logging_call.rs @@ -111,7 +111,7 @@ fn check_log_record_attr_clash(checker: &mut Checker, extra: &Keyword) { if checker .semantic() .resolve_qualified_name(func) - .is_some_and(|call_path| matches!(call_path.segments(), ["", "dict"])) + .is_some_and(|qualified_name| matches!(qualified_name.segments(), ["", "dict"])) { for keyword in keywords.iter() { if let Some(attr) = &keyword.arg { @@ -165,13 +165,13 @@ pub(crate) fn logging_call(checker: &mut Checker, call: &ast::ExprCall) { (call_type, attr.range()) } Expr::Name(_) => { - let Some(call_path) = checker + let Some(qualified_name) = checker .semantic() .resolve_qualified_name(call.func.as_ref()) else { return; }; - let ["logging", attribute] = call_path.segments() else { + let ["logging", attribute] = qualified_name.segments() else { return; }; let Some(call_type) = LoggingCallType::from_attribute(attribute) else { diff --git a/crates/ruff_linter/src/rules/flake8_pie/rules/non_unique_enums.rs b/crates/ruff_linter/src/rules/flake8_pie/rules/non_unique_enums.rs index face4a0834760..3b41b15e75d66 100644 --- a/crates/ruff_linter/src/rules/flake8_pie/rules/non_unique_enums.rs +++ b/crates/ruff_linter/src/rules/flake8_pie/rules/non_unique_enums.rs @@ -63,7 +63,7 @@ pub(crate) fn non_unique_enums(checker: &mut Checker, parent: &Stmt, body: &[Stm checker .semantic() .resolve_qualified_name(expr) - .is_some_and(|call_path| matches!(call_path.segments(), ["enum", "Enum"])) + .is_some_and(|qualified_name| matches!(qualified_name.segments(), ["enum", "Enum"])) }) { return; } @@ -78,7 +78,7 @@ pub(crate) fn non_unique_enums(checker: &mut Checker, parent: &Stmt, body: &[Stm if checker .semantic() .resolve_qualified_name(func) - .is_some_and(|call_path| matches!(call_path.segments(), ["enum", "auto"])) + .is_some_and(|qualified_name| matches!(qualified_name.segments(), ["enum", "auto"])) { continue; } diff --git a/crates/ruff_linter/src/rules/flake8_print/rules/print_call.rs b/crates/ruff_linter/src/rules/flake8_print/rules/print_call.rs index c85642a323171..6f7bf3ccb131f 100644 --- a/crates/ruff_linter/src/rules/flake8_print/rules/print_call.rs +++ b/crates/ruff_linter/src/rules/flake8_print/rules/print_call.rs @@ -98,10 +98,10 @@ impl Violation for PPrint { /// T201, T203 pub(crate) fn print_call(checker: &mut Checker, call: &ast::ExprCall) { let mut diagnostic = { - let call_path = checker.semantic().resolve_qualified_name(&call.func); - if call_path + let qualified_name = checker.semantic().resolve_qualified_name(&call.func); + if qualified_name .as_ref() - .is_some_and(|call_path| matches!(call_path.segments(), ["", "print"])) + .is_some_and(|qualified_name| matches!(qualified_name.segments(), ["", "print"])) { // If the print call has a `file=` argument (that isn't `None`, `"sys.stdout"`, // or `"sys.stderr"`), don't trigger T201. @@ -110,9 +110,9 @@ pub(crate) fn print_call(checker: &mut Checker, call: &ast::ExprCall) { if checker .semantic() .resolve_qualified_name(&keyword.value) - .map_or(true, |call_path| { - call_path.segments() != ["sys", "stdout"] - && call_path.segments() != ["sys", "stderr"] + .map_or(true, |qualified_name| { + qualified_name.segments() != ["sys", "stdout"] + && qualified_name.segments() != ["sys", "stderr"] }) { return; @@ -120,9 +120,9 @@ pub(crate) fn print_call(checker: &mut Checker, call: &ast::ExprCall) { } } Diagnostic::new(Print, call.func.range()) - } else if call_path + } else if qualified_name .as_ref() - .is_some_and(|call_path| matches!(call_path.segments(), ["pprint", "pprint"])) + .is_some_and(|qualified_name| matches!(qualified_name.segments(), ["pprint", "pprint"])) { Diagnostic::new(PPrint, call.func.range()) } else { diff --git a/crates/ruff_linter/src/rules/flake8_pyi/rules/bad_generator_return_type.rs b/crates/ruff_linter/src/rules/flake8_pyi/rules/bad_generator_return_type.rs index fad3556e6bb81..4427a643351c6 100644 --- a/crates/ruff_linter/src/rules/flake8_pyi/rules/bad_generator_return_type.rs +++ b/crates/ruff_linter/src/rules/flake8_pyi/rules/bad_generator_return_type.rs @@ -125,10 +125,10 @@ pub(crate) fn bad_generator_return_type( // Determine the module from which the existing annotation is imported (e.g., `typing` or // `collections.abc`) let (method, module, member) = { - let Some(call_path) = semantic.resolve_qualified_name(map_subscript(returns)) else { + let Some(qualified_name) = semantic.resolve_qualified_name(map_subscript(returns)) else { return; }; - match (name, call_path.segments()) { + match (name, qualified_name.segments()) { ("__iter__", ["typing", "Generator"]) => { (Method::Iter, Module::Typing, Generator::Generator) } diff --git a/crates/ruff_linter/src/rules/flake8_pyi/rules/bad_version_info_comparison.rs b/crates/ruff_linter/src/rules/flake8_pyi/rules/bad_version_info_comparison.rs index 46a3c811dbeb6..056e89e3ead37 100644 --- a/crates/ruff_linter/src/rules/flake8_pyi/rules/bad_version_info_comparison.rs +++ b/crates/ruff_linter/src/rules/flake8_pyi/rules/bad_version_info_comparison.rs @@ -76,7 +76,7 @@ pub(crate) fn bad_version_info_comparison(checker: &mut Checker, test: &Expr) { if !checker .semantic() .resolve_qualified_name(left) - .is_some_and(|call_path| matches!(call_path.segments(), ["sys", "version_info"])) + .is_some_and(|qualified_name| matches!(qualified_name.segments(), ["sys", "version_info"])) { return; } diff --git a/crates/ruff_linter/src/rules/flake8_pyi/rules/collections_named_tuple.rs b/crates/ruff_linter/src/rules/flake8_pyi/rules/collections_named_tuple.rs index f435af54f817d..ea873ebf95221 100644 --- a/crates/ruff_linter/src/rules/flake8_pyi/rules/collections_named_tuple.rs +++ b/crates/ruff_linter/src/rules/flake8_pyi/rules/collections_named_tuple.rs @@ -58,7 +58,9 @@ pub(crate) fn collections_named_tuple(checker: &mut Checker, expr: &Expr) { if checker .semantic() .resolve_qualified_name(expr) - .is_some_and(|call_path| matches!(call_path.segments(), ["collections", "namedtuple"])) + .is_some_and(|qualified_name| { + matches!(qualified_name.segments(), ["collections", "namedtuple"]) + }) { checker .diagnostics diff --git a/crates/ruff_linter/src/rules/flake8_pyi/rules/complex_if_statement_in_stub.rs b/crates/ruff_linter/src/rules/flake8_pyi/rules/complex_if_statement_in_stub.rs index 998701ffcf696..4c5471e7c6c7f 100644 --- a/crates/ruff_linter/src/rules/flake8_pyi/rules/complex_if_statement_in_stub.rs +++ b/crates/ruff_linter/src/rules/flake8_pyi/rules/complex_if_statement_in_stub.rs @@ -68,8 +68,11 @@ pub(crate) fn complex_if_statement_in_stub(checker: &mut Checker, test: &Expr) { if checker .semantic() .resolve_qualified_name(left) - .is_some_and(|call_path| { - matches!(call_path.segments(), ["sys", "version_info" | "platform"]) + .is_some_and(|qualified_name| { + matches!( + qualified_name.segments(), + ["sys", "version_info" | "platform"] + ) }) { return; diff --git a/crates/ruff_linter/src/rules/flake8_pyi/rules/exit_annotations.rs b/crates/ruff_linter/src/rules/flake8_pyi/rules/exit_annotations.rs index 57523ff9191c5..19af0bd6f5e80 100644 --- a/crates/ruff_linter/src/rules/flake8_pyi/rules/exit_annotations.rs +++ b/crates/ruff_linter/src/rules/flake8_pyi/rules/exit_annotations.rs @@ -244,11 +244,11 @@ fn non_none_annotation_element<'a>( ) -> Option<&'a Expr> { // E.g., `typing.Union` or `typing.Optional` if let Expr::Subscript(ExprSubscript { value, slice, .. }) = annotation { - let call_path = semantic.resolve_qualified_name(value); + let qualified_name = semantic.resolve_qualified_name(value); - if call_path + if qualified_name .as_ref() - .is_some_and(|value| semantic.match_typing_call_path(value, "Optional")) + .is_some_and(|value| semantic.match_typing_qualified_name(value, "Optional")) { return if slice.is_none_literal_expr() { None @@ -257,9 +257,9 @@ fn non_none_annotation_element<'a>( }; } - if !call_path + if !qualified_name .as_ref() - .is_some_and(|value| semantic.match_typing_call_path(value, "Union")) + .is_some_and(|value| semantic.match_typing_qualified_name(value, "Union")) { return None; } @@ -307,9 +307,9 @@ fn is_object_or_unused(expr: &Expr, semantic: &SemanticModel) -> bool { semantic .resolve_qualified_name(expr) .as_ref() - .is_some_and(|call_path| { + .is_some_and(|qualified_name| { matches!( - call_path.segments(), + qualified_name.segments(), ["" | "builtins", "object"] | ["_typeshed", "Unused"] ) }) @@ -320,7 +320,12 @@ fn is_base_exception(expr: &Expr, semantic: &SemanticModel) -> bool { semantic .resolve_qualified_name(expr) .as_ref() - .is_some_and(|call_path| matches!(call_path.segments(), ["" | "builtins", "BaseException"])) + .is_some_and(|qualified_name| { + matches!( + qualified_name.segments(), + ["" | "builtins", "BaseException"] + ) + }) } /// Return `true` if the [`Expr`] is the `types.TracebackType` type. @@ -328,7 +333,9 @@ fn is_traceback_type(expr: &Expr, semantic: &SemanticModel) -> bool { semantic .resolve_qualified_name(expr) .as_ref() - .is_some_and(|call_path| matches!(call_path.segments(), ["types", "TracebackType"])) + .is_some_and(|qualified_name| { + matches!(qualified_name.segments(), ["types", "TracebackType"]) + }) } /// Return `true` if the [`Expr`] is, e.g., `Type[BaseException]`. @@ -341,7 +348,9 @@ fn is_base_exception_type(expr: &Expr, semantic: &SemanticModel) -> bool { || semantic .resolve_qualified_name(value) .as_ref() - .is_some_and(|call_path| matches!(call_path.segments(), ["" | "builtins", "type"])) + .is_some_and(|qualified_name| { + matches!(qualified_name.segments(), ["" | "builtins", "type"]) + }) { is_base_exception(slice, semantic) } else { diff --git a/crates/ruff_linter/src/rules/flake8_pyi/rules/iter_method_return_iterable.rs b/crates/ruff_linter/src/rules/flake8_pyi/rules/iter_method_return_iterable.rs index 64ab60ddb9c2a..aec883d862bb6 100644 --- a/crates/ruff_linter/src/rules/flake8_pyi/rules/iter_method_return_iterable.rs +++ b/crates/ruff_linter/src/rules/flake8_pyi/rules/iter_method_return_iterable.rs @@ -89,15 +89,15 @@ pub(crate) fn iter_method_return_iterable(checker: &mut Checker, definition: &De if checker .semantic() .resolve_qualified_name(map_subscript(annotation)) - .is_some_and(|call_path| { + .is_some_and(|qualified_name| { if is_async { matches!( - call_path.segments(), + qualified_name.segments(), ["typing", "AsyncIterable"] | ["collections", "abc", "AsyncIterable"] ) } else { matches!( - call_path.segments(), + qualified_name.segments(), ["typing", "Iterable"] | ["collections", "abc", "Iterable"] ) } diff --git a/crates/ruff_linter/src/rules/flake8_pyi/rules/non_self_return_type.rs b/crates/ruff_linter/src/rules/flake8_pyi/rules/non_self_return_type.rs index 056770e5c905e..c946eba080107 100644 --- a/crates/ruff_linter/src/rules/flake8_pyi/rules/non_self_return_type.rs +++ b/crates/ruff_linter/src/rules/flake8_pyi/rules/non_self_return_type.rs @@ -233,9 +233,9 @@ fn is_metaclass(class_def: &ast::StmtClassDef, semantic: &SemanticModel) -> bool fn is_metaclass_base(base: &Expr, semantic: &SemanticModel) -> bool { semantic .resolve_qualified_name(base) - .is_some_and(|call_path| { + .is_some_and(|qualified_name| { matches!( - call_path.segments(), + qualified_name.segments(), ["" | "builtins", "type"] | ["abc", "ABCMeta"] | ["enum", "EnumMeta" | "EnumType"] ) }) @@ -282,9 +282,9 @@ fn is_iterator(arguments: Option<&Arguments>, semantic: &SemanticModel) -> bool bases.iter().any(|expr| { semantic .resolve_qualified_name(map_subscript(expr)) - .is_some_and(|call_path| { + .is_some_and(|qualified_name| { matches!( - call_path.segments(), + qualified_name.segments(), ["typing", "Iterator"] | ["collections", "abc", "Iterator"] ) }) @@ -295,9 +295,9 @@ fn is_iterator(arguments: Option<&Arguments>, semantic: &SemanticModel) -> bool fn is_iterable(expr: &Expr, semantic: &SemanticModel) -> bool { semantic .resolve_qualified_name(map_subscript(expr)) - .is_some_and(|call_path| { + .is_some_and(|qualified_name| { matches!( - call_path.segments(), + qualified_name.segments(), ["typing", "Iterable" | "Iterator"] | ["collections", "abc", "Iterable" | "Iterator"] ) @@ -312,9 +312,9 @@ fn is_async_iterator(arguments: Option<&Arguments>, semantic: &SemanticModel) -> bases.iter().any(|expr| { semantic .resolve_qualified_name(map_subscript(expr)) - .is_some_and(|call_path| { + .is_some_and(|qualified_name| { matches!( - call_path.segments(), + qualified_name.segments(), ["typing", "AsyncIterator"] | ["collections", "abc", "AsyncIterator"] ) }) @@ -325,9 +325,9 @@ fn is_async_iterator(arguments: Option<&Arguments>, semantic: &SemanticModel) -> fn is_async_iterable(expr: &Expr, semantic: &SemanticModel) -> bool { semantic .resolve_qualified_name(map_subscript(expr)) - .is_some_and(|call_path| { + .is_some_and(|qualified_name| { matches!( - call_path.segments(), + qualified_name.segments(), ["typing", "AsyncIterable" | "AsyncIterator"] | ["collections", "abc", "AsyncIterable" | "AsyncIterator"] ) diff --git a/crates/ruff_linter/src/rules/flake8_pyi/rules/prefix_type_params.rs b/crates/ruff_linter/src/rules/flake8_pyi/rules/prefix_type_params.rs index 01724c2d21782..e4c237c3cb807 100644 --- a/crates/ruff_linter/src/rules/flake8_pyi/rules/prefix_type_params.rs +++ b/crates/ruff_linter/src/rules/flake8_pyi/rules/prefix_type_params.rs @@ -82,20 +82,20 @@ pub(crate) fn prefix_type_params(checker: &mut Checker, value: &Expr, targets: & let Some(kind) = checker .semantic() .resolve_qualified_name(func) - .and_then(|call_path| { + .and_then(|qualified_name| { if checker .semantic() - .match_typing_call_path(&call_path, "ParamSpec") + .match_typing_qualified_name(&qualified_name, "ParamSpec") { Some(VarKind::ParamSpec) } else if checker .semantic() - .match_typing_call_path(&call_path, "TypeVar") + .match_typing_qualified_name(&qualified_name, "TypeVar") { Some(VarKind::TypeVar) } else if checker .semantic() - .match_typing_call_path(&call_path, "TypeVarTuple") + .match_typing_qualified_name(&qualified_name, "TypeVarTuple") { Some(VarKind::TypeVarTuple) } else { diff --git a/crates/ruff_linter/src/rules/flake8_pyi/rules/redundant_numeric_union.rs b/crates/ruff_linter/src/rules/flake8_pyi/rules/redundant_numeric_union.rs index f42f8457c3513..672e4c40f4993 100644 --- a/crates/ruff_linter/src/rules/flake8_pyi/rules/redundant_numeric_union.rs +++ b/crates/ruff_linter/src/rules/flake8_pyi/rules/redundant_numeric_union.rs @@ -90,11 +90,11 @@ fn check_annotation(checker: &mut Checker, annotation: &Expr) { let mut has_int = false; let mut func = |expr: &Expr, _parent: &Expr| { - let Some(call_path) = checker.semantic().resolve_qualified_name(expr) else { + let Some(qualified_name) = checker.semantic().resolve_qualified_name(expr) else { return; }; - match call_path.segments() { + match qualified_name.segments() { ["" | "builtins", "int"] => has_int = true, ["" | "builtins", "float"] => has_float = true, ["" | "builtins", "complex"] => has_complex = true, diff --git a/crates/ruff_linter/src/rules/flake8_pyi/rules/simple_defaults.rs b/crates/ruff_linter/src/rules/flake8_pyi/rules/simple_defaults.rs index d81334336ab43..fdf868aba09ac 100644 --- a/crates/ruff_linter/src/rules/flake8_pyi/rules/simple_defaults.rs +++ b/crates/ruff_linter/src/rules/flake8_pyi/rules/simple_defaults.rs @@ -248,13 +248,16 @@ impl AlwaysFixableViolation for TypeAliasWithoutAnnotation { } } -fn is_allowed_negated_math_attribute(call_path: &QualifiedName) -> bool { - matches!(call_path.segments(), ["math", "inf" | "e" | "pi" | "tau"]) +fn is_allowed_negated_math_attribute(qualified_name: &QualifiedName) -> bool { + matches!( + qualified_name.segments(), + ["math", "inf" | "e" | "pi" | "tau"] + ) } -fn is_allowed_math_attribute(call_path: &QualifiedName) -> bool { +fn is_allowed_math_attribute(qualified_name: &QualifiedName) -> bool { matches!( - call_path.segments(), + qualified_name.segments(), ["math", "inf" | "nan" | "e" | "pi" | "tau"] | [ "sys", @@ -437,9 +440,9 @@ fn is_type_var_like_call(expr: &Expr, semantic: &SemanticModel) -> bool { }; semantic .resolve_qualified_name(func) - .is_some_and(|call_path| { + .is_some_and(|qualified_name| { matches!( - call_path.segments(), + qualified_name.segments(), [ "typing" | "typing_extensions", "TypeVar" | "TypeVarTuple" | "NewType" | "ParamSpec" @@ -483,9 +486,9 @@ fn is_enum(class_def: &ast::StmtClassDef, semantic: &SemanticModel) -> bool { // If the base class is `enum.Enum`, `enum.Flag`, etc., then this is an enum. if semantic .resolve_qualified_name(map_subscript(expr)) - .is_some_and(|call_path| { + .is_some_and(|qualified_name| { matches!( - call_path.segments(), + qualified_name.segments(), [ "enum", "Enum" | "Flag" | "IntEnum" | "IntFlag" | "StrEnum" | "ReprEnum" diff --git a/crates/ruff_linter/src/rules/flake8_pyi/rules/str_or_repr_defined_in_stub.rs b/crates/ruff_linter/src/rules/flake8_pyi/rules/str_or_repr_defined_in_stub.rs index 0a45af97f8fb7..d8a3f3d3e9dd0 100644 --- a/crates/ruff_linter/src/rules/flake8_pyi/rules/str_or_repr_defined_in_stub.rs +++ b/crates/ruff_linter/src/rules/flake8_pyi/rules/str_or_repr_defined_in_stub.rs @@ -81,8 +81,8 @@ pub(crate) fn str_or_repr_defined_in_stub(checker: &mut Checker, stmt: &Stmt) { if checker .semantic() .resolve_qualified_name(returns) - .map_or(true, |call_path| { - !matches!(call_path.segments(), ["" | "builtins", "str"]) + .map_or(true, |qualified_name| { + !matches!(qualified_name.segments(), ["" | "builtins", "str"]) }) { return; diff --git a/crates/ruff_linter/src/rules/flake8_pyi/rules/string_or_bytes_too_long.rs b/crates/ruff_linter/src/rules/flake8_pyi/rules/string_or_bytes_too_long.rs index 3e972972048dd..7be2510933e32 100644 --- a/crates/ruff_linter/src/rules/flake8_pyi/rules/string_or_bytes_too_long.rs +++ b/crates/ruff_linter/src/rules/flake8_pyi/rules/string_or_bytes_too_long.rs @@ -85,9 +85,9 @@ fn is_warnings_dot_deprecated(expr: Option<&ast::Expr>, semantic: &SemanticModel }; semantic .resolve_qualified_name(&call.func) - .is_some_and(|call_path| { + .is_some_and(|qualified_name| { matches!( - call_path.segments(), + qualified_name.segments(), ["warnings" | "typing_extensions", "deprecated"] ) }) diff --git a/crates/ruff_linter/src/rules/flake8_pyi/rules/unnecessary_type_union.rs b/crates/ruff_linter/src/rules/flake8_pyi/rules/unnecessary_type_union.rs index 0f274bb928f40..0a29b9ad28694 100644 --- a/crates/ruff_linter/src/rules/flake8_pyi/rules/unnecessary_type_union.rs +++ b/crates/ruff_linter/src/rules/flake8_pyi/rules/unnecessary_type_union.rs @@ -81,7 +81,9 @@ pub(crate) fn unnecessary_type_union<'a>(checker: &mut Checker, union: &'a Expr) if checker .semantic() .resolve_qualified_name(unwrapped.value.as_ref()) - .is_some_and(|call_path| matches!(call_path.segments(), ["" | "builtins", "type"])) + .is_some_and(|qualified_name| { + matches!(qualified_name.segments(), ["" | "builtins", "type"]) + }) { type_exprs.push(unwrapped.slice.as_ref()); } else { diff --git a/crates/ruff_linter/src/rules/flake8_pyi/rules/unrecognized_platform.rs b/crates/ruff_linter/src/rules/flake8_pyi/rules/unrecognized_platform.rs index 2670069e91fe0..3565e5d6a8fb2 100644 --- a/crates/ruff_linter/src/rules/flake8_pyi/rules/unrecognized_platform.rs +++ b/crates/ruff_linter/src/rules/flake8_pyi/rules/unrecognized_platform.rs @@ -108,7 +108,7 @@ pub(crate) fn unrecognized_platform(checker: &mut Checker, test: &Expr) { if !checker .semantic() .resolve_qualified_name(left) - .is_some_and(|call_path| matches!(call_path.segments(), ["sys", "platform"])) + .is_some_and(|qualified_name| matches!(qualified_name.segments(), ["sys", "platform"])) { return; } diff --git a/crates/ruff_linter/src/rules/flake8_pyi/rules/unrecognized_version_info.rs b/crates/ruff_linter/src/rules/flake8_pyi/rules/unrecognized_version_info.rs index a3ff522d95d99..7bc2180afe5f9 100644 --- a/crates/ruff_linter/src/rules/flake8_pyi/rules/unrecognized_version_info.rs +++ b/crates/ruff_linter/src/rules/flake8_pyi/rules/unrecognized_version_info.rs @@ -136,7 +136,7 @@ pub(crate) fn unrecognized_version_info(checker: &mut Checker, test: &Expr) { if !checker .semantic() .resolve_qualified_name(map_subscript(left)) - .is_some_and(|call_path| matches!(call_path.segments(), ["sys", "version_info"])) + .is_some_and(|qualified_name| matches!(qualified_name.segments(), ["sys", "version_info"])) { return; } diff --git a/crates/ruff_linter/src/rules/flake8_pyi/rules/unused_private_type_definition.rs b/crates/ruff_linter/src/rules/flake8_pyi/rules/unused_private_type_definition.rs index f931b5b29e6ac..cf4f7248b8aba 100644 --- a/crates/ruff_linter/src/rules/flake8_pyi/rules/unused_private_type_definition.rs +++ b/crates/ruff_linter/src/rules/flake8_pyi/rules/unused_private_type_definition.rs @@ -196,17 +196,20 @@ pub(crate) fn unused_private_type_var( let semantic = checker.semantic(); let Some(type_var_like_kind) = - semantic.resolve_qualified_name(func).and_then(|call_path| { - if semantic.match_typing_call_path(&call_path, "TypeVar") { - Some("TypeVar") - } else if semantic.match_typing_call_path(&call_path, "ParamSpec") { - Some("ParamSpec") - } else if semantic.match_typing_call_path(&call_path, "TypeVarTuple") { - Some("TypeVarTuple") - } else { - None - } - }) + semantic + .resolve_qualified_name(func) + .and_then(|qualified_name| { + if semantic.match_typing_qualified_name(&qualified_name, "TypeVar") { + Some("TypeVar") + } else if semantic.match_typing_qualified_name(&qualified_name, "ParamSpec") { + Some("ParamSpec") + } else if semantic.match_typing_qualified_name(&qualified_name, "TypeVarTuple") + { + Some("TypeVarTuple") + } else { + None + } + }) else { continue; }; diff --git a/crates/ruff_linter/src/rules/flake8_pytest_style/rules/fixture.rs b/crates/ruff_linter/src/rules/flake8_pytest_style/rules/fixture.rs index c7290fb329362..a65f59915f16e 100644 --- a/crates/ruff_linter/src/rules/flake8_pytest_style/rules/fixture.rs +++ b/crates/ruff_linter/src/rules/flake8_pytest_style/rules/fixture.rs @@ -646,9 +646,9 @@ impl<'a> Visitor<'a> for SkipFunctionsVisitor<'a> { } } Expr::Call(ast::ExprCall { func, .. }) => { - if UnqualifiedName::from_expr(func).is_some_and(|call_path| { - matches!(call_path.segments(), ["request", "addfinalizer"]) - }) { + if UnqualifiedName::from_expr(func) + .is_some_and(|name| matches!(name.segments(), ["request", "addfinalizer"])) + { self.addfinalizer_call = Some(expr); }; visitor::walk_expr(self, expr); diff --git a/crates/ruff_linter/src/rules/flake8_pytest_style/rules/helpers.rs b/crates/ruff_linter/src/rules/flake8_pytest_style/rules/helpers.rs index 8e86eaa185c17..664dd8878eeeb 100644 --- a/crates/ruff_linter/src/rules/flake8_pytest_style/rules/helpers.rs +++ b/crates/ruff_linter/src/rules/flake8_pytest_style/rules/helpers.rs @@ -8,11 +8,10 @@ pub(super) fn get_mark_decorators( decorators: &[Decorator], ) -> impl Iterator { decorators.iter().filter_map(|decorator| { - let Some(call_path) = UnqualifiedName::from_expr(map_callable(&decorator.expression)) - else { + let Some(name) = UnqualifiedName::from_expr(map_callable(&decorator.expression)) else { return None; }; - let ["pytest", "mark", marker] = call_path.segments() else { + let ["pytest", "mark", marker] = name.segments() else { return None; }; Some((decorator, *marker)) @@ -22,25 +21,29 @@ pub(super) fn get_mark_decorators( pub(super) fn is_pytest_fail(call: &Expr, semantic: &SemanticModel) -> bool { semantic .resolve_qualified_name(call) - .is_some_and(|call_path| matches!(call_path.segments(), ["pytest", "fail"])) + .is_some_and(|qualified_name| matches!(qualified_name.segments(), ["pytest", "fail"])) } pub(super) fn is_pytest_fixture(decorator: &Decorator, semantic: &SemanticModel) -> bool { semantic .resolve_qualified_name(map_callable(&decorator.expression)) - .is_some_and(|call_path| matches!(call_path.segments(), ["pytest", "fixture"])) + .is_some_and(|qualified_name| matches!(qualified_name.segments(), ["pytest", "fixture"])) } pub(super) fn is_pytest_yield_fixture(decorator: &Decorator, semantic: &SemanticModel) -> bool { semantic .resolve_qualified_name(map_callable(&decorator.expression)) - .is_some_and(|call_path| matches!(call_path.segments(), ["pytest", "yield_fixture"])) + .is_some_and(|qualified_name| { + matches!(qualified_name.segments(), ["pytest", "yield_fixture"]) + }) } pub(super) fn is_pytest_parametrize(decorator: &Decorator, semantic: &SemanticModel) -> bool { semantic .resolve_qualified_name(map_callable(&decorator.expression)) - .is_some_and(|call_path| matches!(call_path.segments(), ["pytest", "mark", "parametrize"])) + .is_some_and(|qualified_name| { + matches!(qualified_name.segments(), ["pytest", "mark", "parametrize"]) + }) } pub(super) fn keyword_is_literal(keyword: &Keyword, literal: &str) -> bool { diff --git a/crates/ruff_linter/src/rules/flake8_pytest_style/rules/patch.rs b/crates/ruff_linter/src/rules/flake8_pytest_style/rules/patch.rs index fe014ea7b2504..1b99eb9e21bbd 100644 --- a/crates/ruff_linter/src/rules/flake8_pytest_style/rules/patch.rs +++ b/crates/ruff_linter/src/rules/flake8_pytest_style/rules/patch.rs @@ -104,10 +104,10 @@ fn check_patch_call(call: &ast::ExprCall, index: usize) -> Option { /// PT008 pub(crate) fn patch_with_lambda(call: &ast::ExprCall) -> Option { - let call_path = UnqualifiedName::from_expr(&call.func)?; + let name = UnqualifiedName::from_expr(&call.func)?; if matches!( - call_path.segments(), + name.segments(), [ "mocker" | "class_mocker" @@ -120,7 +120,7 @@ pub(crate) fn patch_with_lambda(call: &ast::ExprCall) -> Option { ) { check_patch_call(call, 1) } else if matches!( - call_path.segments(), + name.segments(), [ "mocker" | "class_mocker" diff --git a/crates/ruff_linter/src/rules/flake8_pytest_style/rules/raises.rs b/crates/ruff_linter/src/rules/flake8_pytest_style/rules/raises.rs index fb37a31188836..ea40496c30b0f 100644 --- a/crates/ruff_linter/src/rules/flake8_pytest_style/rules/raises.rs +++ b/crates/ruff_linter/src/rules/flake8_pytest_style/rules/raises.rs @@ -154,7 +154,7 @@ impl Violation for PytestRaisesWithoutException { fn is_pytest_raises(func: &Expr, semantic: &SemanticModel) -> bool { semantic .resolve_qualified_name(func) - .is_some_and(|call_path| matches!(call_path.segments(), ["pytest", "raises"])) + .is_some_and(|qualified_name| matches!(qualified_name.segments(), ["pytest", "raises"])) } const fn is_non_trivial_with_body(body: &[Stmt]) -> bool { @@ -226,11 +226,11 @@ pub(crate) fn complex_raises( /// PT011 fn exception_needs_match(checker: &mut Checker, exception: &Expr) { - if let Some(call_path) = checker + if let Some(qualified_name) = checker .semantic() .resolve_qualified_name(exception) - .and_then(|call_path| { - let call_path = call_path.to_string(); + .and_then(|qualified_name| { + let qualified_name = qualified_name.to_string(); checker .settings .flake8_pytest_style @@ -242,13 +242,13 @@ fn exception_needs_match(checker: &mut Checker, exception: &Expr) { .flake8_pytest_style .raises_extend_require_match_for, ) - .any(|pattern| pattern.matches(&call_path)) - .then_some(call_path) + .any(|pattern| pattern.matches(&qualified_name)) + .then_some(qualified_name) }) { checker.diagnostics.push(Diagnostic::new( PytestRaisesTooBroad { - exception: call_path, + exception: qualified_name, }, exception.range(), )); diff --git a/crates/ruff_linter/src/rules/flake8_raise/rules/unnecessary_paren_on_raise_exception.rs b/crates/ruff_linter/src/rules/flake8_raise/rules/unnecessary_paren_on_raise_exception.rs index c35eb89e210f6..0c9e2477fce26 100644 --- a/crates/ruff_linter/src/rules/flake8_raise/rules/unnecessary_paren_on_raise_exception.rs +++ b/crates/ruff_linter/src/rules/flake8_raise/rules/unnecessary_paren_on_raise_exception.rs @@ -100,7 +100,9 @@ pub(crate) fn unnecessary_paren_on_raise_exception(checker: &mut Checker, expr: if checker .semantic() .resolve_qualified_name(func) - .is_some_and(|call_path| matches!(call_path.segments(), ["ctypes", "WinError"])) + .is_some_and(|qualified_name| { + matches!(qualified_name.segments(), ["ctypes", "WinError"]) + }) { return; } diff --git a/crates/ruff_linter/src/rules/flake8_return/rules/function.rs b/crates/ruff_linter/src/rules/flake8_return/rules/function.rs index a71c9b94c7c77..615f83b6b18c0 100644 --- a/crates/ruff_linter/src/rules/flake8_return/rules/function.rs +++ b/crates/ruff_linter/src/rules/flake8_return/rules/function.rs @@ -402,15 +402,15 @@ fn is_noreturn_func(func: &Expr, semantic: &SemanticModel) -> bool { // libraries. if semantic .resolve_qualified_name(func) - .is_some_and(|call_path| { + .is_some_and(|qualified_name| { matches!( - call_path.segments(), + qualified_name.segments(), ["" | "builtins" | "sys" | "_thread" | "pytest", "exit"] | ["" | "builtins", "quit"] | ["os" | "posix", "_exit" | "abort"] | ["_winapi", "ExitProcess"] | ["pytest", "fail" | "skip" | "xfail"] - ) || semantic.match_typing_call_path(&call_path, "assert_never") + ) || semantic.match_typing_qualified_name(&qualified_name, "assert_never") }) { return true; @@ -433,11 +433,11 @@ fn is_noreturn_func(func: &Expr, semantic: &SemanticModel) -> bool { return false; }; - let Some(call_path) = semantic.resolve_qualified_name(returns) else { + let Some(qualified_name) = semantic.resolve_qualified_name(returns) else { return false; }; - semantic.match_typing_call_path(&call_path, "NoReturn") + semantic.match_typing_qualified_name(&qualified_name, "NoReturn") } /// RET503 diff --git a/crates/ruff_linter/src/rules/flake8_return/visitor.rs b/crates/ruff_linter/src/rules/flake8_return/visitor.rs index e366d3a998eff..f583c22d6cab0 100644 --- a/crates/ruff_linter/src/rules/flake8_return/visitor.rs +++ b/crates/ruff_linter/src/rules/flake8_return/visitor.rs @@ -198,8 +198,8 @@ fn has_conditional_body(with: &ast::StmtWith, semantic: &SemanticModel) -> bool else { return false; }; - if let Some(call_path) = semantic.resolve_qualified_name(func) { - if call_path.segments() == ["contextlib", "suppress"] { + if let Some(qualified_name) = semantic.resolve_qualified_name(func) { + if qualified_name.segments() == ["contextlib", "suppress"] { return true; } } diff --git a/crates/ruff_linter/src/rules/flake8_self/rules/private_member_access.rs b/crates/ruff_linter/src/rules/flake8_self/rules/private_member_access.rs index 5c99668dcd038..f81024f123f9a 100644 --- a/crates/ruff_linter/src/rules/flake8_self/rules/private_member_access.rs +++ b/crates/ruff_linter/src/rules/flake8_self/rules/private_member_access.rs @@ -141,24 +141,24 @@ pub(crate) fn private_member_access(checker: &mut Checker, expr: &Expr) { } // Allow some documented private methods, like `os._exit()`. - if let Some(call_path) = checker.semantic().resolve_qualified_name(expr) { - if matches!(call_path.segments(), ["os", "_exit"]) { + if let Some(qualified_name) = checker.semantic().resolve_qualified_name(expr) { + if matches!(qualified_name.segments(), ["os", "_exit"]) { return; } } if let Expr::Call(ast::ExprCall { func, .. }) = value.as_ref() { // Ignore `super()` calls. - if let Some(call_path) = UnqualifiedName::from_expr(func) { - if matches!(call_path.segments(), ["super"]) { + if let Some(name) = UnqualifiedName::from_expr(func) { + if matches!(name.segments(), ["super"]) { return; } } } - if let Some(call_path) = UnqualifiedName::from_expr(value) { + if let Some(name) = UnqualifiedName::from_expr(value) { // Ignore `self` and `cls` accesses. - if matches!(call_path.segments(), ["self" | "cls" | "mcs"]) { + if matches!(name.segments(), ["self" | "cls" | "mcs"]) { return; } } diff --git a/crates/ruff_linter/src/rules/flake8_simplify/rules/ast_expr.rs b/crates/ruff_linter/src/rules/flake8_simplify/rules/ast_expr.rs index 348a17a3edf9d..dd6cfa545d0a0 100644 --- a/crates/ruff_linter/src/rules/flake8_simplify/rules/ast_expr.rs +++ b/crates/ruff_linter/src/rules/flake8_simplify/rules/ast_expr.rs @@ -150,9 +150,9 @@ pub(crate) fn use_capital_environment_variables(checker: &mut Checker, expr: &Ex if !checker .semantic() .resolve_qualified_name(func) - .is_some_and(|call_path| { + .is_some_and(|qualified_name| { matches!( - call_path.segments(), + qualified_name.segments(), ["os", "environ", "get"] | ["os", "getenv"] ) }) diff --git a/crates/ruff_linter/src/rules/flake8_simplify/rules/ast_with.rs b/crates/ruff_linter/src/rules/flake8_simplify/rules/ast_with.rs index b44f7a0ef2889..881f4b36914a4 100644 --- a/crates/ruff_linter/src/rules/flake8_simplify/rules/ast_with.rs +++ b/crates/ruff_linter/src/rules/flake8_simplify/rules/ast_with.rs @@ -100,9 +100,9 @@ fn explicit_with_items(checker: &mut Checker, with_items: &[WithItem]) -> bool { checker .semantic() .resolve_qualified_name(&expr_call.func) - .is_some_and(|call_path| { + .is_some_and(|qualified_name| { matches!( - call_path.segments(), + qualified_name.segments(), ["asyncio", "timeout" | "timeout_at"] | ["anyio", "CancelScope" | "fail_after" | "move_on_after"] | [ diff --git a/crates/ruff_linter/src/rules/flake8_simplify/rules/open_file_with_context_handler.rs b/crates/ruff_linter/src/rules/flake8_simplify/rules/open_file_with_context_handler.rs index 15f9c03275fbd..148ed9187bad0 100644 --- a/crates/ruff_linter/src/rules/flake8_simplify/rules/open_file_with_context_handler.rs +++ b/crates/ruff_linter/src/rules/flake8_simplify/rules/open_file_with_context_handler.rs @@ -65,8 +65,8 @@ fn match_async_exit_stack(semantic: &SemanticModel) -> bool { if let Expr::Call(ast::ExprCall { func, .. }) = &item.context_expr { if semantic .resolve_qualified_name(func) - .is_some_and(|call_path| { - matches!(call_path.segments(), ["contextlib", "AsyncExitStack"]) + .is_some_and(|qualified_name| { + matches!(qualified_name.segments(), ["contextlib", "AsyncExitStack"]) }) { return true; @@ -99,8 +99,8 @@ fn match_exit_stack(semantic: &SemanticModel) -> bool { if let Expr::Call(ast::ExprCall { func, .. }) = &item.context_expr { if semantic .resolve_qualified_name(func) - .is_some_and(|call_path| { - matches!(call_path.segments(), ["contextlib", "ExitStack"]) + .is_some_and(|qualified_name| { + matches!(qualified_name.segments(), ["contextlib", "ExitStack"]) }) { return true; @@ -121,7 +121,9 @@ fn is_open(checker: &mut Checker, func: &Expr) -> bool { Expr::Call(ast::ExprCall { func, .. }) => checker .semantic() .resolve_qualified_name(func) - .is_some_and(|call_path| matches!(call_path.segments(), ["pathlib", "Path"])), + .is_some_and(|qualified_name| { + matches!(qualified_name.segments(), ["pathlib", "Path"]) + }), _ => false, } } diff --git a/crates/ruff_linter/src/rules/flake8_simplify/rules/suppressible_exception.rs b/crates/ruff_linter/src/rules/flake8_simplify/rules/suppressible_exception.rs index babbb63f06313..bdc73c62eb119 100644 --- a/crates/ruff_linter/src/rules/flake8_simplify/rules/suppressible_exception.rs +++ b/crates/ruff_linter/src/rules/flake8_simplify/rules/suppressible_exception.rs @@ -1,7 +1,7 @@ use ruff_diagnostics::{Diagnostic, Edit, Fix, FixAvailability, Violation}; use ruff_macros::{derive_message_formats, violation}; use ruff_python_ast::helpers; -use ruff_python_ast::name::compose_call_path; +use ruff_python_ast::name::UnqualifiedName; use ruff_python_ast::{self as ast, ExceptHandler, Stmt}; use ruff_text_size::Ranged; use ruff_text_size::{TextLen, TextRange}; @@ -107,7 +107,7 @@ pub(crate) fn suppressible_exception( let Some(handler_names) = helpers::extract_handled_exceptions(handlers) .into_iter() - .map(compose_call_path) + .map(|expr| UnqualifiedName::from_expr(expr).map(|name| name.to_string())) .collect::>>() else { return; diff --git a/crates/ruff_linter/src/rules/flake8_slots/rules/no_slots_in_namedtuple_subclass.rs b/crates/ruff_linter/src/rules/flake8_slots/rules/no_slots_in_namedtuple_subclass.rs index 0f6b89c867198..187e0bcdf9062 100644 --- a/crates/ruff_linter/src/rules/flake8_slots/rules/no_slots_in_namedtuple_subclass.rs +++ b/crates/ruff_linter/src/rules/flake8_slots/rules/no_slots_in_namedtuple_subclass.rs @@ -74,7 +74,9 @@ pub(crate) fn no_slots_in_namedtuple_subclass( checker .semantic() .resolve_qualified_name(func) - .is_some_and(|call_path| matches!(call_path.segments(), ["collections", "namedtuple"])) + .is_some_and(|qualified_name| { + matches!(qualified_name.segments(), ["collections", "namedtuple"]) + }) }) { if !has_slots(&class.body) { checker.diagnostics.push(Diagnostic::new( diff --git a/crates/ruff_linter/src/rules/flake8_slots/rules/no_slots_in_str_subclass.rs b/crates/ruff_linter/src/rules/flake8_slots/rules/no_slots_in_str_subclass.rs index 07ee994b208fb..4c9006ff03f79 100644 --- a/crates/ruff_linter/src/rules/flake8_slots/rules/no_slots_in_str_subclass.rs +++ b/crates/ruff_linter/src/rules/flake8_slots/rules/no_slots_in_str_subclass.rs @@ -69,8 +69,8 @@ pub(crate) fn no_slots_in_str_subclass(checker: &mut Checker, stmt: &Stmt, class fn is_str_subclass(bases: &[Expr], semantic: &SemanticModel) -> bool { let mut is_str_subclass = false; for base in bases { - if let Some(call_path) = semantic.resolve_qualified_name(base) { - match call_path.segments() { + if let Some(qualified_name) = semantic.resolve_qualified_name(base) { + match qualified_name.segments() { ["" | "builtins", "str"] => { is_str_subclass = true; } diff --git a/crates/ruff_linter/src/rules/flake8_slots/rules/no_slots_in_tuple_subclass.rs b/crates/ruff_linter/src/rules/flake8_slots/rules/no_slots_in_tuple_subclass.rs index d86cce403cbfd..0c0c1462685ae 100644 --- a/crates/ruff_linter/src/rules/flake8_slots/rules/no_slots_in_tuple_subclass.rs +++ b/crates/ruff_linter/src/rules/flake8_slots/rules/no_slots_in_tuple_subclass.rs @@ -59,11 +59,11 @@ pub(crate) fn no_slots_in_tuple_subclass(checker: &mut Checker, stmt: &Stmt, cla checker .semantic() .resolve_qualified_name(map_subscript(base)) - .is_some_and(|call_path| { - matches!(call_path.segments(), ["" | "builtins", "tuple"]) + .is_some_and(|qualified_name| { + matches!(qualified_name.segments(), ["" | "builtins", "tuple"]) || checker .semantic() - .match_typing_call_path(&call_path, "Tuple") + .match_typing_qualified_name(&qualified_name, "Tuple") }) }) { if !has_slots(&class.body) { diff --git a/crates/ruff_linter/src/rules/flake8_tidy_imports/rules/banned_api.rs b/crates/ruff_linter/src/rules/flake8_tidy_imports/rules/banned_api.rs index 3757224dd686a..534d408e16e10 100644 --- a/crates/ruff_linter/src/rules/flake8_tidy_imports/rules/banned_api.rs +++ b/crates/ruff_linter/src/rules/flake8_tidy_imports/rules/banned_api.rs @@ -66,9 +66,9 @@ pub(crate) fn banned_attribute_access(checker: &mut Checker, expr: &Expr) { checker .semantic() .resolve_qualified_name(expr) - .and_then(|call_path| { + .and_then(|qualified_name| { banned_api.iter().find(|(banned_path, ..)| { - call_path == QualifiedName::from_dotted_name(banned_path) + qualified_name == QualifiedName::from_dotted_name(banned_path) }) }) { diff --git a/crates/ruff_linter/src/rules/flake8_trio/rules/sync_call.rs b/crates/ruff_linter/src/rules/flake8_trio/rules/sync_call.rs index ae9b52617aeeb..2f824ab41a26c 100644 --- a/crates/ruff_linter/src/rules/flake8_trio/rules/sync_call.rs +++ b/crates/ruff_linter/src/rules/flake8_trio/rules/sync_call.rs @@ -57,13 +57,13 @@ pub(crate) fn sync_call(checker: &mut Checker, call: &ExprCall) { } let Some(method_name) = ({ - let Some(call_path) = checker + let Some(qualified_name) = checker .semantic() .resolve_qualified_name(call.func.as_ref()) else { return; }; - MethodName::try_from(&call_path) + MethodName::try_from(&qualified_name) }) else { return; }; diff --git a/crates/ruff_linter/src/rules/flake8_trio/rules/timeout_without_await.rs b/crates/ruff_linter/src/rules/flake8_trio/rules/timeout_without_await.rs index cbe80e25d0116..d0707d32bc4a2 100644 --- a/crates/ruff_linter/src/rules/flake8_trio/rules/timeout_without_await.rs +++ b/crates/ruff_linter/src/rules/flake8_trio/rules/timeout_without_await.rs @@ -56,10 +56,10 @@ pub(crate) fn timeout_without_await( let Some(method_name) = with_items.iter().find_map(|item| { let call = item.context_expr.as_call_expr()?; - let call_path = checker + let qualified_name = checker .semantic() .resolve_qualified_name(call.func.as_ref())?; - MethodName::try_from(&call_path) + MethodName::try_from(&qualified_name) }) else { return; }; diff --git a/crates/ruff_linter/src/rules/flake8_trio/rules/zero_sleep_call.rs b/crates/ruff_linter/src/rules/flake8_trio/rules/zero_sleep_call.rs index fb6c902ad24ea..515db518180ff 100644 --- a/crates/ruff_linter/src/rules/flake8_trio/rules/zero_sleep_call.rs +++ b/crates/ruff_linter/src/rules/flake8_trio/rules/zero_sleep_call.rs @@ -63,7 +63,7 @@ pub(crate) fn zero_sleep_call(checker: &mut Checker, call: &ExprCall) { if !checker .semantic() .resolve_qualified_name(call.func.as_ref()) - .is_some_and(|call_path| matches!(call_path.segments(), ["trio", "sleep"])) + .is_some_and(|qualified_name| matches!(qualified_name.segments(), ["trio", "sleep"])) { return; } diff --git a/crates/ruff_linter/src/rules/flake8_type_checking/helpers.rs b/crates/ruff_linter/src/rules/flake8_type_checking/helpers.rs index 28dc4ec700f4b..56c475c9159d9 100644 --- a/crates/ruff_linter/src/rules/flake8_type_checking/helpers.rs +++ b/crates/ruff_linter/src/rules/flake8_type_checking/helpers.rs @@ -77,10 +77,10 @@ fn runtime_required_base_class( base_classes: &[String], semantic: &SemanticModel, ) -> bool { - analyze::class::any_call_path(class_def, semantic, &|call_path| { + analyze::class::any_qualified_name(class_def, semantic, &|qualified_name| { base_classes .iter() - .any(|base_class| QualifiedName::from_dotted_name(base_class) == call_path) + .any(|base_class| QualifiedName::from_dotted_name(base_class) == qualified_name) }) } @@ -96,10 +96,10 @@ fn runtime_required_decorators( decorator_list.iter().any(|decorator| { semantic .resolve_qualified_name(map_callable(&decorator.expression)) - .is_some_and(|call_path| { + .is_some_and(|qualified_name| { decorators .iter() - .any(|base_class| QualifiedName::from_dotted_name(base_class) == call_path) + .any(|base_class| QualifiedName::from_dotted_name(base_class) == qualified_name) }) }) } @@ -120,16 +120,16 @@ pub(crate) fn is_dataclass_meta_annotation(annotation: &Expr, semantic: &Semanti if class_def.decorator_list.iter().any(|decorator| { semantic .resolve_qualified_name(map_callable(&decorator.expression)) - .is_some_and(|call_path| { - matches!(call_path.segments(), ["dataclasses", "dataclass"]) + .is_some_and(|qualified_name| { + matches!(qualified_name.segments(), ["dataclasses", "dataclass"]) }) }) { // Determine whether the annotation is `typing.ClassVar` or `dataclasses.InitVar`. return semantic .resolve_qualified_name(map_subscript(annotation)) - .is_some_and(|call_path| { - matches!(call_path.segments(), ["dataclasses", "InitVar"]) - || semantic.match_typing_call_path(&call_path, "ClassVar") + .is_some_and(|qualified_name| { + matches!(qualified_name.segments(), ["dataclasses", "InitVar"]) + || semantic.match_typing_qualified_name(&qualified_name, "ClassVar") }); } } @@ -155,8 +155,8 @@ pub(crate) fn is_singledispatch_interface( function_def.decorator_list.iter().any(|decorator| { semantic .resolve_qualified_name(&decorator.expression) - .is_some_and(|call_path| { - matches!(call_path.segments(), ["functools", "singledispatch"]) + .is_some_and(|qualified_name| { + matches!(qualified_name.segments(), ["functools", "singledispatch"]) }) }) } diff --git a/crates/ruff_linter/src/rules/flake8_use_pathlib/rules/os_sep_split.rs b/crates/ruff_linter/src/rules/flake8_use_pathlib/rules/os_sep_split.rs index 79bdc253598bb..c6dab7b57ea0d 100644 --- a/crates/ruff_linter/src/rules/flake8_use_pathlib/rules/os_sep_split.rs +++ b/crates/ruff_linter/src/rules/flake8_use_pathlib/rules/os_sep_split.rs @@ -79,7 +79,7 @@ pub(crate) fn os_sep_split(checker: &mut Checker, call: &ast::ExprCall) { if !checker .semantic() .resolve_qualified_name(sep) - .is_some_and(|call_path| matches!(call_path.segments(), ["os", "sep"])) + .is_some_and(|qualified_name| matches!(qualified_name.segments(), ["os", "sep"])) { return; } diff --git a/crates/ruff_linter/src/rules/flake8_use_pathlib/rules/path_constructor_current_directory.rs b/crates/ruff_linter/src/rules/flake8_use_pathlib/rules/path_constructor_current_directory.rs index be77d367fc4d8..542c32dcc55de 100644 --- a/crates/ruff_linter/src/rules/flake8_use_pathlib/rules/path_constructor_current_directory.rs +++ b/crates/ruff_linter/src/rules/flake8_use_pathlib/rules/path_constructor_current_directory.rs @@ -47,7 +47,9 @@ pub(crate) fn path_constructor_current_directory(checker: &mut Checker, expr: &E if !checker .semantic() .resolve_qualified_name(func) - .is_some_and(|call_path| matches!(call_path.segments(), ["pathlib", "Path" | "PurePath"])) + .is_some_and(|qualified_name| { + matches!(qualified_name.segments(), ["pathlib", "Path" | "PurePath"]) + }) { return; } diff --git a/crates/ruff_linter/src/rules/flake8_use_pathlib/rules/replaceable_by_pathlib.rs b/crates/ruff_linter/src/rules/flake8_use_pathlib/rules/replaceable_by_pathlib.rs index b5e90bf2cb41b..fabcdd40e3ad7 100644 --- a/crates/ruff_linter/src/rules/flake8_use_pathlib/rules/replaceable_by_pathlib.rs +++ b/crates/ruff_linter/src/rules/flake8_use_pathlib/rules/replaceable_by_pathlib.rs @@ -19,7 +19,7 @@ pub(crate) fn replaceable_by_pathlib(checker: &mut Checker, call: &ExprCall) { if let Some(diagnostic_kind) = checker .semantic() .resolve_qualified_name(&call.func) - .and_then(|call_path| match call_path.segments() { + .and_then(|qualified_name| match qualified_name.segments() { // PTH100 ["os", "path", "abspath"] => Some(OsPathAbspath.into()), // PTH101 diff --git a/crates/ruff_linter/src/rules/numpy/rules/deprecated_function.rs b/crates/ruff_linter/src/rules/numpy/rules/deprecated_function.rs index db9557c9a9fe8..8a3d943a797ff 100644 --- a/crates/ruff_linter/src/rules/numpy/rules/deprecated_function.rs +++ b/crates/ruff_linter/src/rules/numpy/rules/deprecated_function.rs @@ -64,7 +64,7 @@ pub(crate) fn deprecated_function(checker: &mut Checker, expr: &Expr) { checker .semantic() .resolve_qualified_name(expr) - .and_then(|call_path| match call_path.segments() { + .and_then(|qualified_name| match qualified_name.segments() { ["numpy", "round_"] => Some(("round_", "round")), ["numpy", "product"] => Some(("product", "prod")), ["numpy", "cumproduct"] => Some(("cumproduct", "cumprod")), diff --git a/crates/ruff_linter/src/rules/numpy/rules/deprecated_type_alias.rs b/crates/ruff_linter/src/rules/numpy/rules/deprecated_type_alias.rs index faa84e5009f0a..6f91e68eb128b 100644 --- a/crates/ruff_linter/src/rules/numpy/rules/deprecated_type_alias.rs +++ b/crates/ruff_linter/src/rules/numpy/rules/deprecated_type_alias.rs @@ -54,22 +54,30 @@ pub(crate) fn deprecated_type_alias(checker: &mut Checker, expr: &Expr) { return; } - if let Some(type_name) = checker - .semantic() - .resolve_qualified_name(expr) - .and_then(|call_path| { - if matches!( - call_path.segments(), - [ - "numpy", - "bool" | "int" | "float" | "complex" | "object" | "str" | "long" | "unicode" - ] - ) { - Some(call_path.segments()[1]) - } else { - None - } - }) + if let Some(type_name) = + checker + .semantic() + .resolve_qualified_name(expr) + .and_then(|qualified_name| { + if matches!( + qualified_name.segments(), + [ + "numpy", + "bool" + | "int" + | "float" + | "complex" + | "object" + | "str" + | "long" + | "unicode" + ] + ) { + Some(qualified_name.segments()[1]) + } else { + None + } + }) { let mut diagnostic = Diagnostic::new( NumpyDeprecatedTypeAlias { diff --git a/crates/ruff_linter/src/rules/numpy/rules/legacy_random.rs b/crates/ruff_linter/src/rules/numpy/rules/legacy_random.rs index e295b13cc699b..13050db4abfc7 100644 --- a/crates/ruff_linter/src/rules/numpy/rules/legacy_random.rs +++ b/crates/ruff_linter/src/rules/numpy/rules/legacy_random.rs @@ -68,10 +68,10 @@ pub(crate) fn legacy_random(checker: &mut Checker, expr: &Expr) { checker .semantic() .resolve_qualified_name(expr) - .and_then(|call_path| { + .and_then(|qualified_name| { // seeding state if matches!( - call_path.segments(), + qualified_name.segments(), [ "numpy", "random", @@ -131,7 +131,7 @@ pub(crate) fn legacy_random(checker: &mut Checker, expr: &Expr) { "zipf" ] ) { - Some(call_path.segments()[2]) + Some(qualified_name.segments()[2]) } else { None } diff --git a/crates/ruff_linter/src/rules/numpy/rules/numpy_2_0_deprecation.rs b/crates/ruff_linter/src/rules/numpy/rules/numpy_2_0_deprecation.rs index 2451cde7ebd91..b969ca5e6db07 100644 --- a/crates/ruff_linter/src/rules/numpy/rules/numpy_2_0_deprecation.rs +++ b/crates/ruff_linter/src/rules/numpy/rules/numpy_2_0_deprecation.rs @@ -160,7 +160,7 @@ pub(crate) fn numpy_2_0_deprecation(checker: &mut Checker, expr: &Expr) { let maybe_replacement = checker .semantic() .resolve_qualified_name(expr) - .and_then(|call_path| match call_path.segments() { + .and_then(|qualified_name| match qualified_name.segments() { // NumPy's main namespace np.* members removed in 2.0 ["numpy", "add_docstring"] => Some(Replacement { existing: "add_docstring", diff --git a/crates/ruff_linter/src/rules/pandas_vet/rules/inplace_argument.rs b/crates/ruff_linter/src/rules/pandas_vet/rules/inplace_argument.rs index 442e2eb4e4230..e1766b27c4704 100644 --- a/crates/ruff_linter/src/rules/pandas_vet/rules/inplace_argument.rs +++ b/crates/ruff_linter/src/rules/pandas_vet/rules/inplace_argument.rs @@ -56,7 +56,7 @@ pub(crate) fn inplace_argument(checker: &mut Checker, call: &ast::ExprCall) { if checker .semantic() .resolve_qualified_name(&call.func) - .is_some_and(|call_path| !matches!(call_path.segments(), ["pandas", ..])) + .is_some_and(|qualified_name| !matches!(qualified_name.segments(), ["pandas", ..])) { return; } diff --git a/crates/ruff_linter/src/rules/pandas_vet/rules/read_table.rs b/crates/ruff_linter/src/rules/pandas_vet/rules/read_table.rs index af764e2a5af75..e9ffb89aa962e 100644 --- a/crates/ruff_linter/src/rules/pandas_vet/rules/read_table.rs +++ b/crates/ruff_linter/src/rules/pandas_vet/rules/read_table.rs @@ -54,7 +54,7 @@ pub(crate) fn use_of_read_table(checker: &mut Checker, call: &ast::ExprCall) { if checker .semantic() .resolve_qualified_name(&call.func) - .is_some_and(|call_path| matches!(call_path.segments(), ["pandas", "read_table"])) + .is_some_and(|qualified_name| matches!(qualified_name.segments(), ["pandas", "read_table"])) { if let Some(Expr::StringLiteral(ast::ExprStringLiteral { value, .. })) = call .arguments diff --git a/crates/ruff_linter/src/rules/pep8_naming/helpers.rs b/crates/ruff_linter/src/rules/pep8_naming/helpers.rs index 42bd79d893abc..068c4b143b95e 100644 --- a/crates/ruff_linter/src/rules/pep8_naming/helpers.rs +++ b/crates/ruff_linter/src/rules/pep8_naming/helpers.rs @@ -33,9 +33,9 @@ pub(super) fn is_named_tuple_assignment(stmt: &Stmt, semantic: &SemanticModel) - }; semantic .resolve_qualified_name(func) - .is_some_and(|call_path| { - matches!(call_path.segments(), ["collections", "namedtuple"]) - || semantic.match_typing_call_path(&call_path, "NamedTuple") + .is_some_and(|qualified_name| { + matches!(qualified_name.segments(), ["collections", "namedtuple"]) + || semantic.match_typing_qualified_name(&qualified_name, "NamedTuple") }) } @@ -68,9 +68,9 @@ pub(super) fn is_type_var_assignment(stmt: &Stmt, semantic: &SemanticModel) -> b }; semantic .resolve_qualified_name(func) - .is_some_and(|call_path| { - semantic.match_typing_call_path(&call_path, "TypeVar") - || semantic.match_typing_call_path(&call_path, "NewType") + .is_some_and(|qualified_name| { + semantic.match_typing_qualified_name(&qualified_name, "TypeVar") + || semantic.match_typing_qualified_name(&qualified_name, "NewType") }) } @@ -122,8 +122,8 @@ pub(super) fn is_django_model_import(name: &str, stmt: &Stmt, semantic: &Semanti } // Match against, e.g., `apps.get_model("zerver", "Attachment")`. - if let Some(call_path) = UnqualifiedName::from_expr(func.as_ref()) { - if matches!(call_path.segments(), [.., "get_model"]) { + if let Some(unqualified_name) = UnqualifiedName::from_expr(func.as_ref()) { + if matches!(unqualified_name.segments(), [.., "get_model"]) { if let Some(argument) = arguments.find_argument("model_name", arguments.args.len().saturating_sub(1)) { @@ -139,9 +139,9 @@ pub(super) fn is_django_model_import(name: &str, stmt: &Stmt, semantic: &Semanti } // Match against, e.g., `import_string("zerver.models.Attachment")`. - if let Some(call_path) = semantic.resolve_qualified_name(func.as_ref()) { + if let Some(qualified_name) = semantic.resolve_qualified_name(func.as_ref()) { if matches!( - call_path.segments(), + qualified_name.segments(), ["django", "utils", "module_loading", "import_string"] ) { if let Some(argument) = arguments.find_argument("dotted_path", 0) { diff --git a/crates/ruff_linter/src/rules/pycodestyle/rules/lambda_assignment.rs b/crates/ruff_linter/src/rules/pycodestyle/rules/lambda_assignment.rs index d059e73c250c0..26242791398b0 100644 --- a/crates/ruff_linter/src/rules/pycodestyle/rules/lambda_assignment.rs +++ b/crates/ruff_linter/src/rules/pycodestyle/rules/lambda_assignment.rs @@ -155,9 +155,11 @@ fn extract_types(annotation: &Expr, semantic: &SemanticModel) -> Option<(Vec bool { // Ex) `np.dtype(obj)` Expr::Call(ast::ExprCall { func, .. }) => semantic .resolve_qualified_name(func) - .is_some_and(|call_path| matches!(call_path.segments(), ["numpy", "dtype"])), + .is_some_and(|qualified_name| matches!(qualified_name.segments(), ["numpy", "dtype"])), // Ex) `obj.dtype` Expr::Attribute(ast::ExprAttribute { attr, .. }) => { // Ex) `obj.dtype` diff --git a/crates/ruff_linter/src/rules/pydocstyle/helpers.rs b/crates/ruff_linter/src/rules/pydocstyle/helpers.rs index a0d63c240badd..9ce0a757ac58b 100644 --- a/crates/ruff_linter/src/rules/pydocstyle/helpers.rs +++ b/crates/ruff_linter/src/rules/pydocstyle/helpers.rs @@ -54,10 +54,10 @@ pub(crate) fn should_ignore_definition( function.decorator_list.iter().any(|decorator| { semantic .resolve_qualified_name(map_callable(&decorator.expression)) - .is_some_and(|call_path| { + .is_some_and(|qualified_name| { ignore_decorators .iter() - .any(|decorator| QualifiedName::from_dotted_name(decorator) == call_path) + .any(|decorator| QualifiedName::from_dotted_name(decorator) == qualified_name) }) }) } diff --git a/crates/ruff_linter/src/rules/pylint/rules/bad_open_mode.rs b/crates/ruff_linter/src/rules/pylint/rules/bad_open_mode.rs index 2eb8520f02847..d60bb37deb7a7 100644 --- a/crates/ruff_linter/src/rules/pylint/rules/bad_open_mode.rs +++ b/crates/ruff_linter/src/rules/pylint/rules/bad_open_mode.rs @@ -91,7 +91,9 @@ fn is_open(func: &Expr, semantic: &SemanticModel) -> Option { match value.as_ref() { Expr::Call(ast::ExprCall { func, .. }) => semantic .resolve_qualified_name(func) - .is_some_and(|call_path| matches!(call_path.segments(), ["pathlib", "Path"])) + .is_some_and(|qualified_name| { + matches!(qualified_name.segments(), ["pathlib", "Path"]) + }) .then_some(Kind::Pathlib), _ => None, } diff --git a/crates/ruff_linter/src/rules/pylint/rules/invalid_envvar_default.rs b/crates/ruff_linter/src/rules/pylint/rules/invalid_envvar_default.rs index 5a63aaf155089..1378162b9cf57 100644 --- a/crates/ruff_linter/src/rules/pylint/rules/invalid_envvar_default.rs +++ b/crates/ruff_linter/src/rules/pylint/rules/invalid_envvar_default.rs @@ -51,7 +51,7 @@ pub(crate) fn invalid_envvar_default(checker: &mut Checker, call: &ast::ExprCall if checker .semantic() .resolve_qualified_name(&call.func) - .is_some_and(|call_path| matches!(call_path.segments(), ["os", "getenv"])) + .is_some_and(|qualified_name| matches!(qualified_name.segments(), ["os", "getenv"])) { // Find the `default` argument, if it exists. let Some(expr) = call.arguments.find_argument("default", 1) else { diff --git a/crates/ruff_linter/src/rules/pylint/rules/invalid_envvar_value.rs b/crates/ruff_linter/src/rules/pylint/rules/invalid_envvar_value.rs index 404a41a6b8685..d174137bbc667 100644 --- a/crates/ruff_linter/src/rules/pylint/rules/invalid_envvar_value.rs +++ b/crates/ruff_linter/src/rules/pylint/rules/invalid_envvar_value.rs @@ -44,7 +44,7 @@ pub(crate) fn invalid_envvar_value(checker: &mut Checker, call: &ast::ExprCall) if checker .semantic() .resolve_qualified_name(&call.func) - .is_some_and(|call_path| matches!(call_path.segments(), ["os", "getenv"])) + .is_some_and(|qualified_name| matches!(qualified_name.segments(), ["os", "getenv"])) { // Find the `key` argument, if it exists. let Some(expr) = call.arguments.find_argument("key", 0) else { diff --git a/crates/ruff_linter/src/rules/pylint/rules/logging.rs b/crates/ruff_linter/src/rules/pylint/rules/logging.rs index 1283845fdd588..36ad2af0fd551 100644 --- a/crates/ruff_linter/src/rules/pylint/rules/logging.rs +++ b/crates/ruff_linter/src/rules/pylint/rules/logging.rs @@ -115,13 +115,13 @@ pub(crate) fn logging_call(checker: &mut Checker, call: &ast::ExprCall) { } } Expr::Name(_) => { - let Some(call_path) = checker + let Some(qualified_name) = checker .semantic() .resolve_qualified_name(call.func.as_ref()) else { return; }; - let ["logging", attribute] = call_path.segments() else { + let ["logging", attribute] = qualified_name.segments() else { return; }; if LoggingLevel::from_attribute(attribute).is_none() { diff --git a/crates/ruff_linter/src/rules/pylint/rules/non_slot_assignment.rs b/crates/ruff_linter/src/rules/pylint/rules/non_slot_assignment.rs index 6ac9c611de091..2371a9b30327c 100644 --- a/crates/ruff_linter/src/rules/pylint/rules/non_slot_assignment.rs +++ b/crates/ruff_linter/src/rules/pylint/rules/non_slot_assignment.rs @@ -67,7 +67,7 @@ pub(crate) fn non_slot_assignment(checker: &mut Checker, class_def: &ast::StmtCl checker .semantic() .resolve_qualified_name(base) - .is_some_and(|call_path| matches!(call_path.segments(), ["", "object"])) + .is_some_and(|qualified_name| matches!(qualified_name.segments(), ["", "object"])) }) { return; } diff --git a/crates/ruff_linter/src/rules/pylint/rules/repeated_equality_comparison.rs b/crates/ruff_linter/src/rules/pylint/rules/repeated_equality_comparison.rs index 179123ea18601..2636ad985f67a 100644 --- a/crates/ruff_linter/src/rules/pylint/rules/repeated_equality_comparison.rs +++ b/crates/ruff_linter/src/rules/pylint/rules/repeated_equality_comparison.rs @@ -204,8 +204,11 @@ fn is_allowed_value(bool_op: BoolOp, value: &Expr, semantic: &SemanticModel) -> if any_over_expr(value, &|expr| { semantic .resolve_qualified_name(expr) - .is_some_and(|call_path| { - matches!(call_path.segments(), ["sys", "version_info" | "platform"]) + .is_some_and(|qualified_name| { + matches!( + qualified_name.segments(), + ["sys", "version_info" | "platform"] + ) }) }) { return false; diff --git a/crates/ruff_linter/src/rules/pylint/rules/singledispatch_method.rs b/crates/ruff_linter/src/rules/pylint/rules/singledispatch_method.rs index 28dc841fca8be..ec732781e3b85 100644 --- a/crates/ruff_linter/src/rules/pylint/rules/singledispatch_method.rs +++ b/crates/ruff_linter/src/rules/pylint/rules/singledispatch_method.rs @@ -97,8 +97,8 @@ pub(crate) fn singledispatch_method( if checker .semantic() .resolve_qualified_name(&decorator.expression) - .is_some_and(|call_path| { - matches!(call_path.segments(), ["functools", "singledispatch"]) + .is_some_and(|qualified_name| { + matches!(qualified_name.segments(), ["functools", "singledispatch"]) }) { let mut diagnostic = Diagnostic::new(SingledispatchMethod, decorator.range()); diff --git a/crates/ruff_linter/src/rules/pylint/rules/subprocess_popen_preexec_fn.rs b/crates/ruff_linter/src/rules/pylint/rules/subprocess_popen_preexec_fn.rs index 44729dd355730..1815d8e66b518 100644 --- a/crates/ruff_linter/src/rules/pylint/rules/subprocess_popen_preexec_fn.rs +++ b/crates/ruff_linter/src/rules/pylint/rules/subprocess_popen_preexec_fn.rs @@ -58,7 +58,7 @@ pub(crate) fn subprocess_popen_preexec_fn(checker: &mut Checker, call: &ast::Exp if checker .semantic() .resolve_qualified_name(&call.func) - .is_some_and(|call_path| matches!(call_path.segments(), ["subprocess", "Popen"])) + .is_some_and(|qualified_name| matches!(qualified_name.segments(), ["subprocess", "Popen"])) { if let Some(keyword) = call .arguments diff --git a/crates/ruff_linter/src/rules/pylint/rules/subprocess_run_without_check.rs b/crates/ruff_linter/src/rules/pylint/rules/subprocess_run_without_check.rs index bfc7a3871c3b0..c6ff569fcbd06 100644 --- a/crates/ruff_linter/src/rules/pylint/rules/subprocess_run_without_check.rs +++ b/crates/ruff_linter/src/rules/pylint/rules/subprocess_run_without_check.rs @@ -68,7 +68,7 @@ pub(crate) fn subprocess_run_without_check(checker: &mut Checker, call: &ast::Ex if checker .semantic() .resolve_qualified_name(&call.func) - .is_some_and(|call_path| matches!(call_path.segments(), ["subprocess", "run"])) + .is_some_and(|qualified_name| matches!(qualified_name.segments(), ["subprocess", "run"])) { if call.arguments.find_keyword("check").is_none() { let mut diagnostic = Diagnostic::new(SubprocessRunWithoutCheck, call.func.range()); diff --git a/crates/ruff_linter/src/rules/pylint/rules/type_bivariance.rs b/crates/ruff_linter/src/rules/pylint/rules/type_bivariance.rs index 37e035a177149..2fd82e175d751 100644 --- a/crates/ruff_linter/src/rules/pylint/rules/type_bivariance.rs +++ b/crates/ruff_linter/src/rules/pylint/rules/type_bivariance.rs @@ -101,24 +101,25 @@ pub(crate) fn type_bivariance(checker: &mut Checker, value: &Expr) { }; if is_const_true(covariant) && is_const_true(contravariant) { - let Some(kind) = checker - .semantic() - .resolve_qualified_name(func) - .and_then(|call_path| { - if checker - .semantic() - .match_typing_call_path(&call_path, "ParamSpec") - { - Some(VarKind::ParamSpec) - } else if checker - .semantic() - .match_typing_call_path(&call_path, "TypeVar") - { - Some(VarKind::TypeVar) - } else { - None - } - }) + let Some(kind) = + checker + .semantic() + .resolve_qualified_name(func) + .and_then(|qualified_name| { + if checker + .semantic() + .match_typing_qualified_name(&qualified_name, "ParamSpec") + { + Some(VarKind::ParamSpec) + } else if checker + .semantic() + .match_typing_qualified_name(&qualified_name, "TypeVar") + { + Some(VarKind::TypeVar) + } else { + None + } + }) else { return; }; diff --git a/crates/ruff_linter/src/rules/pylint/rules/type_name_incorrect_variance.rs b/crates/ruff_linter/src/rules/pylint/rules/type_name_incorrect_variance.rs index ce4356d3cc578..75a86035d398d 100644 --- a/crates/ruff_linter/src/rules/pylint/rules/type_name_incorrect_variance.rs +++ b/crates/ruff_linter/src/rules/pylint/rules/type_name_incorrect_variance.rs @@ -96,15 +96,15 @@ pub(crate) fn type_name_incorrect_variance(checker: &mut Checker, value: &Expr) let Some(kind) = checker .semantic() .resolve_qualified_name(func) - .and_then(|call_path| { + .and_then(|qualified_name| { if checker .semantic() - .match_typing_call_path(&call_path, "ParamSpec") + .match_typing_qualified_name(&qualified_name, "ParamSpec") { Some(VarKind::ParamSpec) } else if checker .semantic() - .match_typing_call_path(&call_path, "TypeVar") + .match_typing_qualified_name(&qualified_name, "TypeVar") { Some(VarKind::TypeVar) } else { diff --git a/crates/ruff_linter/src/rules/pylint/rules/type_param_name_mismatch.rs b/crates/ruff_linter/src/rules/pylint/rules/type_param_name_mismatch.rs index 3dc04ab42f464..805f98e97c676 100644 --- a/crates/ruff_linter/src/rules/pylint/rules/type_param_name_mismatch.rs +++ b/crates/ruff_linter/src/rules/pylint/rules/type_param_name_mismatch.rs @@ -90,25 +90,25 @@ pub(crate) fn type_param_name_mismatch(checker: &mut Checker, value: &Expr, targ let Some(kind) = checker .semantic() .resolve_qualified_name(func) - .and_then(|call_path| { + .and_then(|qualified_name| { if checker .semantic() - .match_typing_call_path(&call_path, "ParamSpec") + .match_typing_qualified_name(&qualified_name, "ParamSpec") { Some(VarKind::ParamSpec) } else if checker .semantic() - .match_typing_call_path(&call_path, "TypeVar") + .match_typing_qualified_name(&qualified_name, "TypeVar") { Some(VarKind::TypeVar) } else if checker .semantic() - .match_typing_call_path(&call_path, "TypeVarTuple") + .match_typing_qualified_name(&qualified_name, "TypeVarTuple") { Some(VarKind::TypeVarTuple) } else if checker .semantic() - .match_typing_call_path(&call_path, "NewType") + .match_typing_qualified_name(&qualified_name, "NewType") { Some(VarKind::NewType) } else { diff --git a/crates/ruff_linter/src/rules/pylint/rules/unnecessary_list_index_lookup.rs b/crates/ruff_linter/src/rules/pylint/rules/unnecessary_list_index_lookup.rs index 0999accdfeaaf..67cf9d7469109 100644 --- a/crates/ruff_linter/src/rules/pylint/rules/unnecessary_list_index_lookup.rs +++ b/crates/ruff_linter/src/rules/pylint/rules/unnecessary_list_index_lookup.rs @@ -127,7 +127,9 @@ fn enumerate_items<'a>( // Check that the function is the `enumerate` builtin. if !semantic .resolve_qualified_name(func.as_ref()) - .is_some_and(|call_path| matches!(call_path.segments(), ["builtins" | "", "enumerate"])) + .is_some_and(|qualified_name| { + matches!(qualified_name.segments(), ["builtins" | "", "enumerate"]) + }) { return None; } diff --git a/crates/ruff_linter/src/rules/pylint/rules/unspecified_encoding.rs b/crates/ruff_linter/src/rules/pylint/rules/unspecified_encoding.rs index ef3343bf9eb02..fabd85f18e382 100644 --- a/crates/ruff_linter/src/rules/pylint/rules/unspecified_encoding.rs +++ b/crates/ruff_linter/src/rules/pylint/rules/unspecified_encoding.rs @@ -73,8 +73,8 @@ pub(crate) fn unspecified_encoding(checker: &mut Checker, call: &ast::ExprCall) let Some((function_name, mode)) = checker .semantic() .resolve_qualified_name(&call.func) - .filter(|call_path| is_violation(call, call_path)) - .map(|call_path| (call_path.to_string(), Mode::from(&call_path))) + .filter(|qualified_name| is_violation(call, qualified_name)) + .map(|qualified_name| (qualified_name.to_string(), Mode::from(&qualified_name))) else { return; }; diff --git a/crates/ruff_linter/src/rules/pylint/rules/useless_exception_statement.rs b/crates/ruff_linter/src/rules/pylint/rules/useless_exception_statement.rs index c31feb8acedb9..1df8a4ab25358 100644 --- a/crates/ruff_linter/src/rules/pylint/rules/useless_exception_statement.rs +++ b/crates/ruff_linter/src/rules/pylint/rules/useless_exception_statement.rs @@ -65,9 +65,9 @@ pub(crate) fn useless_exception_statement(checker: &mut Checker, expr: &ast::Stm fn is_builtin_exception(expr: &Expr, semantic: &SemanticModel) -> bool { return semantic .resolve_qualified_name(expr) - .is_some_and(|call_path| { + .is_some_and(|qualified_name| { matches!( - call_path.segments(), + qualified_name.segments(), [ "", "SystemExit" diff --git a/crates/ruff_linter/src/rules/pylint/rules/useless_with_lock.rs b/crates/ruff_linter/src/rules/pylint/rules/useless_with_lock.rs index 14eab7b645bc8..9d949c9ca7762 100644 --- a/crates/ruff_linter/src/rules/pylint/rules/useless_with_lock.rs +++ b/crates/ruff_linter/src/rules/pylint/rules/useless_with_lock.rs @@ -66,9 +66,9 @@ pub(crate) fn useless_with_lock(checker: &mut Checker, with: &ast::StmtWith) { if !checker .semantic() .resolve_qualified_name(call.func.as_ref()) - .is_some_and(|call_path| { + .is_some_and(|qualified_name| { matches!( - call_path.segments(), + qualified_name.segments(), [ "threading", "Lock" | "RLock" | "Condition" | "Semaphore" | "BoundedSemaphore" diff --git a/crates/ruff_linter/src/rules/pyupgrade/rules/datetime_utc_alias.rs b/crates/ruff_linter/src/rules/pyupgrade/rules/datetime_utc_alias.rs index 79ad2727b4a48..8af75bc012f1a 100644 --- a/crates/ruff_linter/src/rules/pyupgrade/rules/datetime_utc_alias.rs +++ b/crates/ruff_linter/src/rules/pyupgrade/rules/datetime_utc_alias.rs @@ -54,7 +54,9 @@ pub(crate) fn datetime_utc_alias(checker: &mut Checker, expr: &Expr) { if checker .semantic() .resolve_qualified_name(expr) - .is_some_and(|call_path| matches!(call_path.segments(), ["datetime", "timezone", "utc"])) + .is_some_and(|qualified_name| { + matches!(qualified_name.segments(), ["datetime", "timezone", "utc"]) + }) { let mut diagnostic = Diagnostic::new(DatetimeTimezoneUTC, expr.range()); diagnostic.try_set_fix(|| { diff --git a/crates/ruff_linter/src/rules/pyupgrade/rules/deprecated_mock_import.rs b/crates/ruff_linter/src/rules/pyupgrade/rules/deprecated_mock_import.rs index fe4979f2a22de..a4156b83e3538 100644 --- a/crates/ruff_linter/src/rules/pyupgrade/rules/deprecated_mock_import.rs +++ b/crates/ruff_linter/src/rules/pyupgrade/rules/deprecated_mock_import.rs @@ -256,7 +256,7 @@ pub(crate) fn deprecated_mock_attribute(checker: &mut Checker, attribute: &ast:: } if UnqualifiedName::from_expr(&attribute.value) - .is_some_and(|call_path| matches!(call_path.segments(), ["mock", "mock"])) + .is_some_and(|qualified_name| matches!(qualified_name.segments(), ["mock", "mock"])) { let mut diagnostic = Diagnostic::new( DeprecatedMockImport { diff --git a/crates/ruff_linter/src/rules/pyupgrade/rules/f_strings.rs b/crates/ruff_linter/src/rules/pyupgrade/rules/f_strings.rs index 9d0be5e36a869..e813979276b2a 100644 --- a/crates/ruff_linter/src/rules/pyupgrade/rules/f_strings.rs +++ b/crates/ruff_linter/src/rules/pyupgrade/rules/f_strings.rs @@ -466,9 +466,9 @@ pub(crate) fn f_strings( checker .semantic() .resolve_qualified_name(call.func.as_ref()) - .map_or(false, |call_path| { + .map_or(false, |qualified_name| { matches!( - call_path.segments(), + qualified_name.segments(), ["django", "utils", "translation", "gettext" | "gettext_lazy"] ) }) diff --git a/crates/ruff_linter/src/rules/pyupgrade/rules/lru_cache_with_maxsize_none.rs b/crates/ruff_linter/src/rules/pyupgrade/rules/lru_cache_with_maxsize_none.rs index 344ea5ebe80ed..58317848dc241 100644 --- a/crates/ruff_linter/src/rules/pyupgrade/rules/lru_cache_with_maxsize_none.rs +++ b/crates/ruff_linter/src/rules/pyupgrade/rules/lru_cache_with_maxsize_none.rs @@ -77,7 +77,9 @@ pub(crate) fn lru_cache_with_maxsize_none(checker: &mut Checker, decorator_list: && checker .semantic() .resolve_qualified_name(func) - .is_some_and(|call_path| matches!(call_path.segments(), ["functools", "lru_cache"])) + .is_some_and(|qualified_name| { + matches!(qualified_name.segments(), ["functools", "lru_cache"]) + }) { let Keyword { arg, diff --git a/crates/ruff_linter/src/rules/pyupgrade/rules/lru_cache_without_parameters.rs b/crates/ruff_linter/src/rules/pyupgrade/rules/lru_cache_without_parameters.rs index 3dcd23bae1ffc..69ac48e29dc3b 100644 --- a/crates/ruff_linter/src/rules/pyupgrade/rules/lru_cache_without_parameters.rs +++ b/crates/ruff_linter/src/rules/pyupgrade/rules/lru_cache_without_parameters.rs @@ -70,7 +70,9 @@ pub(crate) fn lru_cache_without_parameters(checker: &mut Checker, decorator_list && checker .semantic() .resolve_qualified_name(func) - .is_some_and(|call_path| matches!(call_path.segments(), ["functools", "lru_cache"])) + .is_some_and(|qualified_name| { + matches!(qualified_name.segments(), ["functools", "lru_cache"]) + }) { let mut diagnostic = Diagnostic::new( LRUCacheWithoutParameters, diff --git a/crates/ruff_linter/src/rules/pyupgrade/rules/open_alias.rs b/crates/ruff_linter/src/rules/pyupgrade/rules/open_alias.rs index c97e881455114..685ddfcaaa22e 100644 --- a/crates/ruff_linter/src/rules/pyupgrade/rules/open_alias.rs +++ b/crates/ruff_linter/src/rules/pyupgrade/rules/open_alias.rs @@ -50,7 +50,7 @@ pub(crate) fn open_alias(checker: &mut Checker, expr: &Expr, func: &Expr) { if checker .semantic() .resolve_qualified_name(func) - .is_some_and(|call_path| matches!(call_path.segments(), ["io", "open"])) + .is_some_and(|qualified_name| matches!(qualified_name.segments(), ["io", "open"])) { let mut diagnostic = Diagnostic::new(OpenAlias, expr.range()); if checker.semantic().is_builtin("open") { diff --git a/crates/ruff_linter/src/rules/pyupgrade/rules/os_error_alias.rs b/crates/ruff_linter/src/rules/pyupgrade/rules/os_error_alias.rs index 56869b52e6cd4..c7a0567f06ac4 100644 --- a/crates/ruff_linter/src/rules/pyupgrade/rules/os_error_alias.rs +++ b/crates/ruff_linter/src/rules/pyupgrade/rules/os_error_alias.rs @@ -4,7 +4,7 @@ use ruff_text_size::{Ranged, TextRange}; use crate::fix::edits::pad; use ruff_diagnostics::{AlwaysFixableViolation, Diagnostic, Edit, Fix}; use ruff_macros::{derive_message_formats, violation}; -use ruff_python_ast::name::compose_call_path; +use ruff_python_ast::name::UnqualifiedName; use ruff_python_semantic::SemanticModel; use crate::checkers::ast::Checker; @@ -58,9 +58,9 @@ impl AlwaysFixableViolation for OSErrorAlias { fn is_alias(expr: &Expr, semantic: &SemanticModel) -> bool { semantic .resolve_qualified_name(expr) - .is_some_and(|call_path| { + .is_some_and(|qualified_name| { matches!( - call_path.segments(), + qualified_name.segments(), ["", "EnvironmentError" | "IOError" | "WindowsError"] | ["mmap" | "select" | "socket" | "os", "error"] ) @@ -71,14 +71,14 @@ fn is_alias(expr: &Expr, semantic: &SemanticModel) -> bool { fn is_os_error(expr: &Expr, semantic: &SemanticModel) -> bool { semantic .resolve_qualified_name(expr) - .is_some_and(|call_path| matches!(call_path.segments(), ["", "OSError"])) + .is_some_and(|qualified_name| matches!(qualified_name.segments(), ["", "OSError"])) } /// Create a [`Diagnostic`] for a single target, like an [`Expr::Name`]. fn atom_diagnostic(checker: &mut Checker, target: &Expr) { let mut diagnostic = Diagnostic::new( OSErrorAlias { - name: compose_call_path(target), + name: UnqualifiedName::from_expr(target).map(|name| name.to_string()), }, target.range(), ); diff --git a/crates/ruff_linter/src/rules/pyupgrade/rules/outdated_version_block.rs b/crates/ruff_linter/src/rules/pyupgrade/rules/outdated_version_block.rs index 2357ec5bf121d..04391303d2caa 100644 --- a/crates/ruff_linter/src/rules/pyupgrade/rules/outdated_version_block.rs +++ b/crates/ruff_linter/src/rules/pyupgrade/rules/outdated_version_block.rs @@ -98,7 +98,9 @@ pub(crate) fn outdated_version_block(checker: &mut Checker, stmt_if: &StmtIf) { if !checker .semantic() .resolve_qualified_name(map_subscript(left)) - .is_some_and(|call_path| matches!(call_path.segments(), ["sys", "version_info"])) + .is_some_and(|qualified_name| { + matches!(qualified_name.segments(), ["sys", "version_info"]) + }) { continue; } diff --git a/crates/ruff_linter/src/rules/pyupgrade/rules/replace_stdout_stderr.rs b/crates/ruff_linter/src/rules/pyupgrade/rules/replace_stdout_stderr.rs index 8e1856597743d..ec5ee0d2d5823 100644 --- a/crates/ruff_linter/src/rules/pyupgrade/rules/replace_stdout_stderr.rs +++ b/crates/ruff_linter/src/rules/pyupgrade/rules/replace_stdout_stderr.rs @@ -61,7 +61,7 @@ pub(crate) fn replace_stdout_stderr(checker: &mut Checker, call: &ast::ExprCall) if checker .semantic() .resolve_qualified_name(&call.func) - .is_some_and(|call_path| matches!(call_path.segments(), ["subprocess", "run"])) + .is_some_and(|qualified_name| matches!(qualified_name.segments(), ["subprocess", "run"])) { // Find `stdout` and `stderr` kwargs. let Some(stdout) = call.arguments.find_keyword("stdout") else { @@ -75,11 +75,15 @@ pub(crate) fn replace_stdout_stderr(checker: &mut Checker, call: &ast::ExprCall) if !checker .semantic() .resolve_qualified_name(&stdout.value) - .is_some_and(|call_path| matches!(call_path.segments(), ["subprocess", "PIPE"])) + .is_some_and(|qualified_name| { + matches!(qualified_name.segments(), ["subprocess", "PIPE"]) + }) || !checker .semantic() .resolve_qualified_name(&stderr.value) - .is_some_and(|call_path| matches!(call_path.segments(), ["subprocess", "PIPE"])) + .is_some_and(|qualified_name| { + matches!(qualified_name.segments(), ["subprocess", "PIPE"]) + }) { return; } diff --git a/crates/ruff_linter/src/rules/pyupgrade/rules/replace_universal_newlines.rs b/crates/ruff_linter/src/rules/pyupgrade/rules/replace_universal_newlines.rs index 4791dc88aad69..6638ae1086dae 100644 --- a/crates/ruff_linter/src/rules/pyupgrade/rules/replace_universal_newlines.rs +++ b/crates/ruff_linter/src/rules/pyupgrade/rules/replace_universal_newlines.rs @@ -57,7 +57,7 @@ pub(crate) fn replace_universal_newlines(checker: &mut Checker, call: &ast::Expr if checker .semantic() .resolve_qualified_name(&call.func) - .is_some_and(|call_path| matches!(call_path.segments(), ["subprocess", "run"])) + .is_some_and(|qualified_name| matches!(qualified_name.segments(), ["subprocess", "run"])) { let Some(kwarg) = call.arguments.find_keyword("universal_newlines") else { return; diff --git a/crates/ruff_linter/src/rules/pyupgrade/rules/timeout_error_alias.rs b/crates/ruff_linter/src/rules/pyupgrade/rules/timeout_error_alias.rs index 7979970ede65f..b3591f26d78a6 100644 --- a/crates/ruff_linter/src/rules/pyupgrade/rules/timeout_error_alias.rs +++ b/crates/ruff_linter/src/rules/pyupgrade/rules/timeout_error_alias.rs @@ -4,7 +4,7 @@ use ruff_text_size::{Ranged, TextRange}; use crate::fix::edits::pad; use ruff_diagnostics::{AlwaysFixableViolation, Diagnostic, Edit, Fix}; use ruff_macros::{derive_message_formats, violation}; -use ruff_python_ast::name::compose_call_path; +use ruff_python_ast::name::UnqualifiedName; use ruff_python_semantic::SemanticModel; use crate::checkers::ast::Checker; @@ -61,10 +61,10 @@ impl AlwaysFixableViolation for TimeoutErrorAlias { fn is_alias(expr: &Expr, semantic: &SemanticModel, target_version: PythonVersion) -> bool { semantic .resolve_qualified_name(expr) - .is_some_and(|call_path| { + .is_some_and(|qualified_name| { if target_version >= PythonVersion::Py311 { matches!( - call_path.segments(), + qualified_name.segments(), ["socket", "timeout"] | ["asyncio", "TimeoutError"] ) } else { @@ -76,7 +76,7 @@ fn is_alias(expr: &Expr, semantic: &SemanticModel, target_version: PythonVersion target_version >= PythonVersion::Py310, "lint should only be used for Python 3.10+", ); - matches!(call_path.segments(), ["socket", "timeout"]) + matches!(qualified_name.segments(), ["socket", "timeout"]) } }) } @@ -85,14 +85,14 @@ fn is_alias(expr: &Expr, semantic: &SemanticModel, target_version: PythonVersion fn is_timeout_error(expr: &Expr, semantic: &SemanticModel) -> bool { semantic .resolve_qualified_name(expr) - .is_some_and(|call_path| matches!(call_path.segments(), ["", "TimeoutError"])) + .is_some_and(|qualified_name| matches!(qualified_name.segments(), ["", "TimeoutError"])) } /// Create a [`Diagnostic`] for a single target, like an [`Expr::Name`]. fn atom_diagnostic(checker: &mut Checker, target: &Expr) { let mut diagnostic = Diagnostic::new( TimeoutErrorAlias { - name: compose_call_path(target), + name: UnqualifiedName::from_expr(target).map(|name| name.to_string()), }, target.range(), ); diff --git a/crates/ruff_linter/src/rules/pyupgrade/rules/type_of_primitive.rs b/crates/ruff_linter/src/rules/pyupgrade/rules/type_of_primitive.rs index 9dd725d41dc60..c2fc297eaf9ca 100644 --- a/crates/ruff_linter/src/rules/pyupgrade/rules/type_of_primitive.rs +++ b/crates/ruff_linter/src/rules/pyupgrade/rules/type_of_primitive.rs @@ -61,7 +61,7 @@ pub(crate) fn type_of_primitive(checker: &mut Checker, expr: &Expr, func: &Expr, if !checker .semantic() .resolve_qualified_name(func) - .is_some_and(|call_path| matches!(call_path.segments(), ["", "type"])) + .is_some_and(|qualified_name| matches!(qualified_name.segments(), ["", "type"])) { return; } diff --git a/crates/ruff_linter/src/rules/pyupgrade/rules/typing_text_str_alias.rs b/crates/ruff_linter/src/rules/pyupgrade/rules/typing_text_str_alias.rs index 9fb6746ac6b63..a186e8b3d0e23 100644 --- a/crates/ruff_linter/src/rules/pyupgrade/rules/typing_text_str_alias.rs +++ b/crates/ruff_linter/src/rules/pyupgrade/rules/typing_text_str_alias.rs @@ -54,7 +54,7 @@ pub(crate) fn typing_text_str_alias(checker: &mut Checker, expr: &Expr) { if checker .semantic() .resolve_qualified_name(expr) - .is_some_and(|call_path| matches!(call_path.segments(), ["typing", "Text"])) + .is_some_and(|qualified_name| matches!(qualified_name.segments(), ["typing", "Text"])) { let mut diagnostic = Diagnostic::new(TypingTextStrAlias, expr.range()); if checker.semantic().is_builtin("str") { diff --git a/crates/ruff_linter/src/rules/pyupgrade/rules/use_pep585_annotation.rs b/crates/ruff_linter/src/rules/pyupgrade/rules/use_pep585_annotation.rs index aa15696cf615e..d47a6fc2e3e2e 100644 --- a/crates/ruff_linter/src/rules/pyupgrade/rules/use_pep585_annotation.rs +++ b/crates/ruff_linter/src/rules/pyupgrade/rules/use_pep585_annotation.rs @@ -2,7 +2,7 @@ use ruff_python_ast::Expr; use ruff_diagnostics::{Applicability, Diagnostic, Edit, Fix, FixAvailability, Violation}; use ruff_macros::{derive_message_formats, violation}; -use ruff_python_ast::name::compose_call_path; +use ruff_python_ast::name::UnqualifiedName; use ruff_python_semantic::analyze::typing::ModuleMember; use ruff_text_size::Ranged; @@ -81,12 +81,12 @@ pub(crate) fn use_pep585_annotation( expr: &Expr, replacement: &ModuleMember, ) { - let Some(from) = compose_call_path(expr) else { + let Some(from) = UnqualifiedName::from_expr(expr) else { return; }; let mut diagnostic = Diagnostic::new( NonPEP585Annotation { - from, + from: from.to_string(), to: replacement.to_string(), }, expr.range(), diff --git a/crates/ruff_linter/src/rules/refurb/rules/bit_count.rs b/crates/ruff_linter/src/rules/refurb/rules/bit_count.rs index 8fe6a2e347a1f..671cca0f0bd71 100644 --- a/crates/ruff_linter/src/rules/refurb/rules/bit_count.rs +++ b/crates/ruff_linter/src/rules/refurb/rules/bit_count.rs @@ -101,7 +101,7 @@ pub(crate) fn bit_count(checker: &mut Checker, call: &ExprCall) { if !checker .semantic() .resolve_qualified_name(func) - .is_some_and(|call_path| matches!(call_path.segments(), ["" | "builtins", "bin"])) + .is_some_and(|qualified_name| matches!(qualified_name.segments(), ["" | "builtins", "bin"])) { return; } diff --git a/crates/ruff_linter/src/rules/refurb/rules/hashlib_digest_hex.rs b/crates/ruff_linter/src/rules/refurb/rules/hashlib_digest_hex.rs index 77177d9382525..6f210e60827c7 100644 --- a/crates/ruff_linter/src/rules/refurb/rules/hashlib_digest_hex.rs +++ b/crates/ruff_linter/src/rules/refurb/rules/hashlib_digest_hex.rs @@ -86,9 +86,9 @@ pub(crate) fn hashlib_digest_hex(checker: &mut Checker, call: &ExprCall) { if checker .semantic() .resolve_qualified_name(func) - .is_some_and(|call_path| { + .is_some_and(|qualified_name| { matches!( - call_path.segments(), + qualified_name.segments(), [ "hashlib", "md5" diff --git a/crates/ruff_linter/src/rules/refurb/rules/implicit_cwd.rs b/crates/ruff_linter/src/rules/refurb/rules/implicit_cwd.rs index b14a2f0e998b8..7274e49795a61 100644 --- a/crates/ruff_linter/src/rules/refurb/rules/implicit_cwd.rs +++ b/crates/ruff_linter/src/rules/refurb/rules/implicit_cwd.rs @@ -77,7 +77,7 @@ pub(crate) fn no_implicit_cwd(checker: &mut Checker, call: &ExprCall) { if !checker .semantic() .resolve_qualified_name(func) - .is_some_and(|call_path| matches!(call_path.segments(), ["pathlib", "Path"])) + .is_some_and(|qualified_name| matches!(qualified_name.segments(), ["pathlib", "Path"])) { return; } diff --git a/crates/ruff_linter/src/rules/refurb/rules/metaclass_abcmeta.rs b/crates/ruff_linter/src/rules/refurb/rules/metaclass_abcmeta.rs index c38680d8faf82..5982cdca7fd6d 100644 --- a/crates/ruff_linter/src/rules/refurb/rules/metaclass_abcmeta.rs +++ b/crates/ruff_linter/src/rules/refurb/rules/metaclass_abcmeta.rs @@ -64,7 +64,7 @@ pub(crate) fn metaclass_abcmeta(checker: &mut Checker, class_def: &StmtClassDef) if !checker .semantic() .resolve_qualified_name(&keyword.value) - .is_some_and(|call_path| matches!(call_path.segments(), ["abc", "ABCMeta"])) + .is_some_and(|qualified_name| matches!(qualified_name.segments(), ["abc", "ABCMeta"])) { return; } diff --git a/crates/ruff_linter/src/rules/refurb/rules/print_empty_string.rs b/crates/ruff_linter/src/rules/refurb/rules/print_empty_string.rs index e77d53112bf25..6bc52c14e5484 100644 --- a/crates/ruff_linter/src/rules/refurb/rules/print_empty_string.rs +++ b/crates/ruff_linter/src/rules/refurb/rules/print_empty_string.rs @@ -74,7 +74,7 @@ pub(crate) fn print_empty_string(checker: &mut Checker, call: &ast::ExprCall) { .semantic() .resolve_qualified_name(&call.func) .as_ref() - .is_some_and(|call_path| matches!(call_path.segments(), ["", "print"])) + .is_some_and(|qualified_name| matches!(qualified_name.segments(), ["", "print"])) { return; } diff --git a/crates/ruff_linter/src/rules/refurb/rules/redundant_log_base.rs b/crates/ruff_linter/src/rules/refurb/rules/redundant_log_base.rs index 884938316fcc5..4fbc2774cf450 100644 --- a/crates/ruff_linter/src/rules/refurb/rules/redundant_log_base.rs +++ b/crates/ruff_linter/src/rules/refurb/rules/redundant_log_base.rs @@ -78,7 +78,7 @@ pub(crate) fn redundant_log_base(checker: &mut Checker, call: &ast::ExprCall) { .semantic() .resolve_qualified_name(&call.func) .as_ref() - .is_some_and(|call_path| matches!(call_path.segments(), ["math", "log"])) + .is_some_and(|qualified_name| matches!(qualified_name.segments(), ["math", "log"])) { return; } @@ -91,7 +91,7 @@ pub(crate) fn redundant_log_base(checker: &mut Checker, call: &ast::ExprCall) { .semantic() .resolve_qualified_name(base) .as_ref() - .is_some_and(|call_path| matches!(call_path.segments(), ["math", "e"])) + .is_some_and(|qualified_name| matches!(qualified_name.segments(), ["math", "e"])) { Base::E } else { diff --git a/crates/ruff_linter/src/rules/refurb/rules/regex_flag_alias.rs b/crates/ruff_linter/src/rules/refurb/rules/regex_flag_alias.rs index 068ed359aef5d..81facd1417b2e 100644 --- a/crates/ruff_linter/src/rules/refurb/rules/regex_flag_alias.rs +++ b/crates/ruff_linter/src/rules/refurb/rules/regex_flag_alias.rs @@ -60,7 +60,7 @@ pub(crate) fn regex_flag_alias(checker: &mut Checker, expr: &Expr) { let Some(flag) = checker .semantic() .resolve_qualified_name(expr) - .and_then(|call_path| match call_path.segments() { + .and_then(|qualified_name| match qualified_name.segments() { ["re", "A"] => Some(RegexFlag::Ascii), ["re", "I"] => Some(RegexFlag::IgnoreCase), ["re", "L"] => Some(RegexFlag::Locale), diff --git a/crates/ruff_linter/src/rules/ruff/rules/asyncio_dangling_task.rs b/crates/ruff_linter/src/rules/ruff/rules/asyncio_dangling_task.rs index 6b12afa6a1d9e..4c04fc0d43af1 100644 --- a/crates/ruff_linter/src/rules/ruff/rules/asyncio_dangling_task.rs +++ b/crates/ruff_linter/src/rules/ruff/rules/asyncio_dangling_task.rs @@ -71,14 +71,13 @@ pub(crate) fn asyncio_dangling_task(expr: &Expr, semantic: &SemanticModel) -> Op }; // Ex) `asyncio.create_task(...)` - if let Some(method) = - semantic - .resolve_qualified_name(func) - .and_then(|call_path| match call_path.segments() { - ["asyncio", "create_task"] => Some(Method::CreateTask), - ["asyncio", "ensure_future"] => Some(Method::EnsureFuture), - _ => None, - }) + if let Some(method) = semantic + .resolve_qualified_name(func) + .and_then(|qualified_name| match qualified_name.segments() { + ["asyncio", "create_task"] => Some(Method::CreateTask), + ["asyncio", "ensure_future"] => Some(Method::EnsureFuture), + _ => None, + }) { return Some(Diagnostic::new( AsyncioDanglingTask { @@ -93,9 +92,9 @@ pub(crate) fn asyncio_dangling_task(expr: &Expr, semantic: &SemanticModel) -> Op if let Expr::Attribute(ast::ExprAttribute { attr, value, .. }) = func.as_ref() { if attr == "create_task" { if let Expr::Name(name) = value.as_ref() { - if typing::resolve_assignment(value, semantic).is_some_and(|call_path| { + if typing::resolve_assignment(value, semantic).is_some_and(|qualified_name| { matches!( - call_path.segments(), + qualified_name.segments(), [ "asyncio", "get_event_loop" | "get_running_loop" | "new_event_loop" diff --git a/crates/ruff_linter/src/rules/ruff/rules/default_factory_kwarg.rs b/crates/ruff_linter/src/rules/ruff/rules/default_factory_kwarg.rs index 8cecaca683b1c..61adfd48dd469 100644 --- a/crates/ruff_linter/src/rules/ruff/rules/default_factory_kwarg.rs +++ b/crates/ruff_linter/src/rules/ruff/rules/default_factory_kwarg.rs @@ -78,7 +78,9 @@ pub(crate) fn default_factory_kwarg(checker: &mut Checker, call: &ast::ExprCall) if !checker .semantic() .resolve_qualified_name(call.func.as_ref()) - .is_some_and(|call_path| matches!(call_path.segments(), ["collections", "defaultdict"])) + .is_some_and(|qualified_name| { + matches!(qualified_name.segments(), ["collections", "defaultdict"]) + }) { return; } diff --git a/crates/ruff_linter/src/rules/ruff/rules/function_call_in_dataclass_default.rs b/crates/ruff_linter/src/rules/ruff/rules/function_call_in_dataclass_default.rs index e0df6a82e2839..017e243da93f1 100644 --- a/crates/ruff_linter/src/rules/ruff/rules/function_call_in_dataclass_default.rs +++ b/crates/ruff_linter/src/rules/ruff/rules/function_call_in_dataclass_default.rs @@ -2,8 +2,7 @@ use ruff_python_ast::{self as ast, Expr, Stmt}; use ruff_diagnostics::{Diagnostic, Violation}; use ruff_macros::{derive_message_formats, violation}; -use ruff_python_ast::name::compose_call_path; -use ruff_python_ast::name::QualifiedName; +use ruff_python_ast::name::{QualifiedName, UnqualifiedName}; use ruff_python_semantic::analyze::typing::is_immutable_func; use ruff_text_size::Ranged; @@ -103,7 +102,7 @@ pub(crate) fn function_call_in_dataclass_default( { checker.diagnostics.push(Diagnostic::new( FunctionCallInDataclassDefaultArgument { - name: compose_call_path(func), + name: UnqualifiedName::from_expr(func).map(|name| name.to_string()), }, expr.range(), )); diff --git a/crates/ruff_linter/src/rules/ruff/rules/helpers.rs b/crates/ruff_linter/src/rules/ruff/rules/helpers.rs index 9b48765c38a27..e151a5bb16013 100644 --- a/crates/ruff_linter/src/rules/ruff/rules/helpers.rs +++ b/crates/ruff_linter/src/rules/ruff/rules/helpers.rs @@ -26,7 +26,7 @@ pub(super) fn is_dataclass_field(func: &Expr, semantic: &SemanticModel) -> bool semantic .resolve_qualified_name(func) - .is_some_and(|call_path| matches!(call_path.segments(), ["dataclasses", "field"])) + .is_some_and(|qualified_name| matches!(qualified_name.segments(), ["dataclasses", "field"])) } /// Returns `true` if the given [`Expr`] is a `typing.ClassVar` annotation. @@ -60,7 +60,9 @@ pub(super) fn is_dataclass(class_def: &ast::StmtClassDef, semantic: &SemanticMod class_def.decorator_list.iter().any(|decorator| { semantic .resolve_qualified_name(map_callable(&decorator.expression)) - .is_some_and(|call_path| matches!(call_path.segments(), ["dataclasses", "dataclass"])) + .is_some_and(|qualified_name| { + matches!(qualified_name.segments(), ["dataclasses", "dataclass"]) + }) }) } @@ -72,9 +74,9 @@ pub(super) fn has_default_copy_semantics( class_def: &ast::StmtClassDef, semantic: &SemanticModel, ) -> bool { - analyze::class::any_call_path(class_def, semantic, &|call_path| { + analyze::class::any_qualified_name(class_def, semantic, &|qualified_name| { matches!( - call_path.segments(), + qualified_name.segments(), ["pydantic", "BaseModel" | "BaseSettings" | "BaseConfig"] | ["pydantic_settings", "BaseSettings"] | ["msgspec", "Struct"] diff --git a/crates/ruff_linter/src/rules/ruff/rules/never_union.rs b/crates/ruff_linter/src/rules/ruff/rules/never_union.rs index e9df8aebd06f4..f68983834e252 100644 --- a/crates/ruff_linter/src/rules/ruff/rules/never_union.rs +++ b/crates/ruff_linter/src/rules/ruff/rules/never_union.rs @@ -193,10 +193,10 @@ enum NeverLike { impl NeverLike { fn from_expr(expr: &Expr, semantic: &ruff_python_semantic::SemanticModel) -> Option { - let call_path = semantic.resolve_qualified_name(expr)?; - if semantic.match_typing_call_path(&call_path, "NoReturn") { + let qualified_name = semantic.resolve_qualified_name(expr)?; + if semantic.match_typing_qualified_name(&qualified_name, "NoReturn") { Some(NeverLike::NoReturn) - } else if semantic.match_typing_call_path(&call_path, "Never") { + } else if semantic.match_typing_qualified_name(&qualified_name, "Never") { Some(NeverLike::Never) } else { None diff --git a/crates/ruff_linter/src/rules/ruff/typing.rs b/crates/ruff_linter/src/rules/ruff/typing.rs index 9ab1398f19a25..286f162b4c893 100644 --- a/crates/ruff_linter/src/rules/ruff/typing.rs +++ b/crates/ruff_linter/src/rules/ruff/typing.rs @@ -83,19 +83,20 @@ impl<'a> TypingTarget<'a> { // If we can't resolve the call path, it must be defined // in the same file and could be a type alias. Some(TypingTarget::Unknown), - |call_path| { - if semantic.match_typing_call_path(&call_path, "Optional") { + |qualified_name| { + if semantic.match_typing_qualified_name(&qualified_name, "Optional") { Some(TypingTarget::Optional(slice.as_ref())) - } else if semantic.match_typing_call_path(&call_path, "Literal") { + } else if semantic.match_typing_qualified_name(&qualified_name, "Literal") { Some(TypingTarget::Literal(slice.as_ref())) - } else if semantic.match_typing_call_path(&call_path, "Union") { + } else if semantic.match_typing_qualified_name(&qualified_name, "Union") { Some(TypingTarget::Union(slice.as_ref())) - } else if semantic.match_typing_call_path(&call_path, "Annotated") { + } else if semantic.match_typing_qualified_name(&qualified_name, "Annotated") + { resolve_slice_value(slice.as_ref()) .next() .map(TypingTarget::Annotated) } else { - if is_known_type(&call_path, minor_version) { + if is_known_type(&qualified_name, minor_version) { Some(TypingTarget::Known) } else { Some(TypingTarget::Unknown) @@ -119,16 +120,19 @@ impl<'a> TypingTarget<'a> { // If we can't resolve the call path, it must be defined in the // same file, so we assume it's `Any` as it could be a type alias. Some(TypingTarget::Unknown), - |call_path| { - if semantic.match_typing_call_path(&call_path, "Any") { + |qualified_name| { + if semantic.match_typing_qualified_name(&qualified_name, "Any") { Some(TypingTarget::Any) - } else if matches!(call_path.segments(), ["" | "builtins", "object"]) { + } else if matches!(qualified_name.segments(), ["" | "builtins", "object"]) { Some(TypingTarget::Object) - } else if semantic.match_typing_call_path(&call_path, "Hashable") - || matches!(call_path.segments(), ["collections", "abc", "Hashable"]) + } else if semantic.match_typing_qualified_name(&qualified_name, "Hashable") + || matches!( + qualified_name.segments(), + ["collections", "abc", "Hashable"] + ) { Some(TypingTarget::Hashable) - } else if !is_known_type(&call_path, minor_version) { + } else if !is_known_type(&qualified_name, minor_version) { // If it's not a known type, we assume it's `Any`. Some(TypingTarget::Unknown) } else { diff --git a/crates/ruff_linter/src/rules/tryceratops/helpers.rs b/crates/ruff_linter/src/rules/tryceratops/helpers.rs index 28bd75975ca1c..7b07240c20a39 100644 --- a/crates/ruff_linter/src/rules/tryceratops/helpers.rs +++ b/crates/ruff_linter/src/rules/tryceratops/helpers.rs @@ -35,10 +35,10 @@ impl<'a, 'b> Visitor<'b> for LoggerCandidateVisitor<'a, 'b> { } } Expr::Name(_) => { - if let Some(call_path) = + if let Some(qualified_name) = self.semantic.resolve_qualified_name(call.func.as_ref()) { - if let ["logging", attribute] = call_path.segments() { + if let ["logging", attribute] = qualified_name.segments() { if let Some(logging_level) = LoggingLevel::from_attribute(attribute) { { self.calls.push((call, logging_level)); diff --git a/crates/ruff_linter/src/rules/tryceratops/rules/error_instead_of_exception.rs b/crates/ruff_linter/src/rules/tryceratops/rules/error_instead_of_exception.rs index 5e329b498d61b..b98c71452a9f8 100644 --- a/crates/ruff_linter/src/rules/tryceratops/rules/error_instead_of_exception.rs +++ b/crates/ruff_linter/src/rules/tryceratops/rules/error_instead_of_exception.rs @@ -91,8 +91,8 @@ pub(crate) fn error_instead_of_exception(checker: &mut Checker, handlers: &[Exce if checker .semantic() .resolve_qualified_name(expr.func.as_ref()) - .is_some_and(|call_path| { - matches!(call_path.segments(), ["logging", "error"]) + .is_some_and(|qualified_name| { + matches!(qualified_name.segments(), ["logging", "error"]) }) { Applicability::Safe @@ -118,8 +118,11 @@ pub(crate) fn error_instead_of_exception(checker: &mut Checker, handlers: &[Exce if checker .semantic() .resolve_qualified_name(expr.func.as_ref()) - .is_some_and(|call_path| { - matches!(call_path.segments(), ["logging", "error"]) + .is_some_and(|qualified_name| { + matches!( + qualified_name.segments(), + ["logging", "error"] + ) }) { Applicability::Safe diff --git a/crates/ruff_linter/src/rules/tryceratops/rules/raise_vanilla_args.rs b/crates/ruff_linter/src/rules/tryceratops/rules/raise_vanilla_args.rs index 3f6ed6aa31551..561f2f9577599 100644 --- a/crates/ruff_linter/src/rules/tryceratops/rules/raise_vanilla_args.rs +++ b/crates/ruff_linter/src/rules/tryceratops/rules/raise_vanilla_args.rs @@ -73,7 +73,9 @@ pub(crate) fn raise_vanilla_args(checker: &mut Checker, expr: &Expr) { if checker .semantic() .resolve_qualified_name(func) - .is_some_and(|call_path| matches!(call_path.segments(), ["", "NotImplementedError"])) + .is_some_and(|qualified_name| { + matches!(qualified_name.segments(), ["", "NotImplementedError"]) + }) { return; } diff --git a/crates/ruff_linter/src/rules/tryceratops/rules/raise_vanilla_class.rs b/crates/ruff_linter/src/rules/tryceratops/rules/raise_vanilla_class.rs index 527760c8b23dd..bacdeeb12ef49 100644 --- a/crates/ruff_linter/src/rules/tryceratops/rules/raise_vanilla_class.rs +++ b/crates/ruff_linter/src/rules/tryceratops/rules/raise_vanilla_class.rs @@ -70,7 +70,7 @@ pub(crate) fn raise_vanilla_class(checker: &mut Checker, expr: &Expr) { } else { expr }) - .is_some_and(|call_path| matches!(call_path.segments(), ["", "Exception"])) + .is_some_and(|qualified_name| matches!(qualified_name.segments(), ["", "Exception"])) { checker .diagnostics diff --git a/crates/ruff_linter/src/rules/tryceratops/rules/raise_within_try.rs b/crates/ruff_linter/src/rules/tryceratops/rules/raise_within_try.rs index 2c82c88f7b89e..ca769add40ad1 100644 --- a/crates/ruff_linter/src/rules/tryceratops/rules/raise_within_try.rs +++ b/crates/ruff_linter/src/rules/tryceratops/rules/raise_within_try.rs @@ -112,8 +112,11 @@ pub(crate) fn raise_within_try(checker: &mut Checker, body: &[Stmt], handlers: & checker .semantic() .resolve_qualified_name(expr) - .is_some_and(|call_path| { - matches!(call_path.segments(), ["", "Exception" | "BaseException"]) + .is_some_and(|qualified_name| { + matches!( + qualified_name.segments(), + ["", "Exception" | "BaseException"] + ) }) }) { diff --git a/crates/ruff_linter/src/rules/tryceratops/rules/type_check_without_type_error.rs b/crates/ruff_linter/src/rules/tryceratops/rules/type_check_without_type_error.rs index e968e2704f1b3..9c326349138a4 100644 --- a/crates/ruff_linter/src/rules/tryceratops/rules/type_check_without_type_error.rs +++ b/crates/ruff_linter/src/rules/tryceratops/rules/type_check_without_type_error.rs @@ -76,9 +76,9 @@ fn check_type_check_call(checker: &mut Checker, call: &Expr) -> bool { checker .semantic() .resolve_qualified_name(call) - .is_some_and(|call_path| { + .is_some_and(|qualified_name| { matches!( - call_path.segments(), + qualified_name.segments(), ["", "isinstance" | "issubclass" | "callable"] ) }) @@ -101,9 +101,9 @@ fn is_builtin_exception(checker: &mut Checker, exc: &Expr) -> bool { return checker .semantic() .resolve_qualified_name(exc) - .is_some_and(|call_path| { + .is_some_and(|qualified_name| { matches!( - call_path.segments(), + qualified_name.segments(), [ "", "ArithmeticError" diff --git a/crates/ruff_python_ast/src/helpers.rs b/crates/ruff_python_ast/src/helpers.rs index b1ace67f5bddd..18b3e5cc9676a 100644 --- a/crates/ruff_python_ast/src/helpers.rs +++ b/crates/ruff_python_ast/src/helpers.rs @@ -801,7 +801,7 @@ pub fn collect_import_from_member<'a>( module: Option<&'a str>, member: &'a str, ) -> QualifiedName<'a> { - let mut call_path_builder = QualifiedNameBuilder::with_capacity( + let mut qualified_name_builder = QualifiedNameBuilder::with_capacity( level.unwrap_or_default() as usize + module .map(|module| module.split('.').count()) @@ -813,20 +813,20 @@ pub fn collect_import_from_member<'a>( if let Some(level) = level { if level > 0 { for _ in 0..level { - call_path_builder.push("."); + qualified_name_builder.push("."); } } } // Add the remaining segments. if let Some(module) = module { - call_path_builder.extend(module.split('.')); + qualified_name_builder.extend(module.split('.')); } // Add the member. - call_path_builder.push(member); + qualified_name_builder.push(member); - call_path_builder.build() + qualified_name_builder.build() } /// Format the call path for a relative import, or `None` if the relative import extends beyond @@ -839,28 +839,28 @@ pub fn from_relative_import<'a>( // The remaining segments to the call path (e.g., given `bar.baz`, `["baz"]`). tail: &[&'a str], ) -> Option> { - let mut call_path_builder = + let mut qualified_name_builder = QualifiedNameBuilder::with_capacity(module.len() + import.len() + tail.len()); // Start with the module path. - call_path_builder.extend(module.iter().map(String::as_str)); + qualified_name_builder.extend(module.iter().map(String::as_str)); // Remove segments based on the number of dots. for segment in import { if *segment == "." { - if call_path_builder.is_empty() { + if qualified_name_builder.is_empty() { return None; } - call_path_builder.pop(); + qualified_name_builder.pop(); } else { - call_path_builder.push(segment); + qualified_name_builder.push(segment); } } // Add the remaining segments. - call_path_builder.extend_from_slice(tail); + qualified_name_builder.extend_from_slice(tail); - Some(call_path_builder.build()) + Some(qualified_name_builder.build()) } /// Given an imported module (based on its relative import level and module name), return the diff --git a/crates/ruff_python_ast/src/name.rs b/crates/ruff_python_ast/src/name.rs index 49a4f4f3bc456..0a1ff2837f0ac 100644 --- a/crates/ruff_python_ast/src/name.rs +++ b/crates/ruff_python_ast/src/name.rs @@ -95,9 +95,9 @@ impl<'a> QualifiedNameBuilder<'a> { } } - pub fn from_qualified_name(call_path: QualifiedName<'a>) -> Self { + pub fn from_qualified_name(qualified_name: QualifiedName<'a>) -> Self { Self { - segments: call_path.segments, + segments: qualified_name.segments, } } @@ -133,16 +133,11 @@ impl<'a> QualifiedNameBuilder<'a> { impl Display for QualifiedName<'_> { fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { - format_call_path_segments(self.segments(), f) + format_qualified_name_segments(self.segments(), f) } } -/// Convert an `Expr` to its call path (like `List`, or `typing.List`). -pub fn compose_call_path(expr: &Expr) -> Option { - UnqualifiedName::from_expr(expr).map(|call_path| call_path.to_string()) -} - -pub fn format_call_path_segments(segments: &[&str], w: &mut dyn Write) -> std::fmt::Result { +pub fn format_qualified_name_segments(segments: &[&str], w: &mut dyn Write) -> std::fmt::Result { if segments.first().is_some_and(|first| first.is_empty()) { // If the first segment is empty, the `CallPath` is that of a builtin. // Ex) `["", "bool"]` -> `"bool"` @@ -194,21 +189,6 @@ pub struct UnqualifiedName<'a> { } impl<'a> UnqualifiedName<'a> { - /// Create a [`UnqualifiedName`] from a dotted name. - /// - /// ```rust - /// # use smallvec::smallvec; - /// # use ruff_python_ast::name::{UnqualifiedName}; - /// - /// assert_eq!(UnqualifiedName::from_dotted("typing.List").segments(), ["typing", "List"]); - /// assert_eq!(UnqualifiedName::from_dotted("list").segments(), ["list"]); - /// ``` - pub fn from_dotted(name: &'a str) -> Self { - Self { - segments: name.split('.').collect(), - } - } - pub fn from_expr(expr: &'a Expr) -> Option { let segments = collect_segments(expr)?; Some(Self { segments }) @@ -242,6 +222,15 @@ impl Display for UnqualifiedName<'_> { } } +impl<'a> FromIterator<&'a str> for UnqualifiedName<'a> { + #[inline] + fn from_iter>(iter: T) -> Self { + Self { + segments: iter.into_iter().collect(), + } + } +} + /// Convert an `Expr` to its [`QualifiedName`] segments (like `["typing", "List"]`). fn collect_segments(expr: &Expr) -> Option> { // Unroll the loop up to eight times, to match the maximum number of expected attributes. diff --git a/crates/ruff_python_semantic/src/analyze/class.rs b/crates/ruff_python_semantic/src/analyze/class.rs index 78dd51db1d91f..5aaeebc9de8ad 100644 --- a/crates/ruff_python_semantic/src/analyze/class.rs +++ b/crates/ruff_python_semantic/src/analyze/class.rs @@ -7,7 +7,7 @@ use ruff_python_ast::name::QualifiedName; use crate::{BindingId, SemanticModel}; /// Return `true` if any base class matches a [`QualifiedName`] predicate. -pub fn any_call_path( +pub fn any_qualified_name( class_def: &ast::StmtClassDef, semantic: &SemanticModel, func: &dyn Fn(QualifiedName) -> bool, diff --git a/crates/ruff_python_semantic/src/analyze/function_type.rs b/crates/ruff_python_semantic/src/analyze/function_type.rs index 8a281c05e3a31..ff6d3ea560c57 100644 --- a/crates/ruff_python_semantic/src/analyze/function_type.rs +++ b/crates/ruff_python_semantic/src/analyze/function_type.rs @@ -36,8 +36,8 @@ pub fn classify( // The class itself extends a known metaclass, so all methods are class methods. semantic .resolve_qualified_name(map_callable(expr)) - .is_some_and( |call_path| { - matches!(call_path.segments(), ["", "type"] | ["abc", "ABCMeta"]) + .is_some_and( |qualified_name| { + matches!(qualified_name.segments(), ["", "type"] | ["abc", "ABCMeta"]) }) }) || decorator_list.iter().any(|decorator| is_class_method(decorator, semantic, classmethod_decorators)) @@ -60,13 +60,13 @@ fn is_static_method( // The decorator is an import, so should match against a qualified path. if semantic .resolve_qualified_name(decorator) - .is_some_and(|call_path| { + .is_some_and(|qualified_name| { matches!( - call_path.segments(), + qualified_name.segments(), ["", "staticmethod"] | ["abc", "abstractstaticmethod"] ) || staticmethod_decorators .iter() - .any(|decorator| call_path == QualifiedName::from_dotted_name(decorator)) + .any(|decorator| qualified_name == QualifiedName::from_dotted_name(decorator)) }) { return true; @@ -75,8 +75,8 @@ fn is_static_method( // We do not have a resolvable call path, most likely from a decorator like // `@someproperty.setter`. Instead, match on the last element. if !staticmethod_decorators.is_empty() { - if UnqualifiedName::from_expr(decorator).is_some_and(|call_path| { - call_path.segments().last().is_some_and(|tail| { + if UnqualifiedName::from_expr(decorator).is_some_and(|name| { + name.segments().last().is_some_and(|tail| { staticmethod_decorators .iter() .any(|decorator| tail == decorator) @@ -100,13 +100,13 @@ fn is_class_method( // The decorator is an import, so should match against a qualified path. if semantic .resolve_qualified_name(decorator) - .is_some_and(|call_path| { + .is_some_and(|qualified_name| { matches!( - call_path.segments(), + qualified_name.segments(), ["", "classmethod"] | ["abc", "abstractclassmethod"] ) || classmethod_decorators .iter() - .any(|decorator| call_path == QualifiedName::from_dotted_name(decorator)) + .any(|decorator| qualified_name == QualifiedName::from_dotted_name(decorator)) }) { return true; @@ -115,8 +115,8 @@ fn is_class_method( // We do not have a resolvable call path, most likely from a decorator like // `@someproperty.setter`. Instead, match on the last element. if !classmethod_decorators.is_empty() { - if UnqualifiedName::from_expr(decorator).is_some_and(|call_path| { - call_path.segments().last().is_some_and(|tail| { + if UnqualifiedName::from_expr(decorator).is_some_and(|name| { + name.segments().last().is_some_and(|tail| { classmethod_decorators .iter() .any(|decorator| tail == decorator) diff --git a/crates/ruff_python_semantic/src/analyze/imports.rs b/crates/ruff_python_semantic/src/analyze/imports.rs index 23693418d7e4f..162cb6e653a94 100644 --- a/crates/ruff_python_semantic/src/analyze/imports.rs +++ b/crates/ruff_python_semantic/src/analyze/imports.rs @@ -18,9 +18,9 @@ pub fn is_sys_path_modification(stmt: &Stmt, semantic: &SemanticModel) -> bool { }; semantic .resolve_qualified_name(func.as_ref()) - .is_some_and(|call_path| { + .is_some_and(|qualified_name| { matches!( - call_path.segments(), + qualified_name.segments(), [ "sys", "path", @@ -48,9 +48,9 @@ pub fn is_os_environ_modification(stmt: &Stmt, semantic: &SemanticModel) -> bool Stmt::Expr(ast::StmtExpr { value, .. }) => match value.as_ref() { Expr::Call(ast::ExprCall { func, .. }) => semantic .resolve_qualified_name(func.as_ref()) - .is_some_and(|call_path| { + .is_some_and(|qualified_name| { matches!( - call_path.segments(), + qualified_name.segments(), ["os", "putenv" | "unsetenv"] | [ "os", @@ -64,19 +64,23 @@ pub fn is_os_environ_modification(stmt: &Stmt, semantic: &SemanticModel) -> bool Stmt::Delete(ast::StmtDelete { targets, .. }) => targets.iter().any(|target| { semantic .resolve_qualified_name(map_subscript(target)) - .is_some_and(|call_path| matches!(call_path.segments(), ["os", "environ"])) + .is_some_and(|qualified_name| { + matches!(qualified_name.segments(), ["os", "environ"]) + }) }), Stmt::Assign(ast::StmtAssign { targets, .. }) => targets.iter().any(|target| { semantic .resolve_qualified_name(map_subscript(target)) - .is_some_and(|call_path| matches!(call_path.segments(), ["os", "environ"])) + .is_some_and(|qualified_name| { + matches!(qualified_name.segments(), ["os", "environ"]) + }) }), Stmt::AnnAssign(ast::StmtAnnAssign { target, .. }) => semantic .resolve_qualified_name(map_subscript(target)) - .is_some_and(|call_path| matches!(call_path.segments(), ["os", "environ"])), + .is_some_and(|qualified_name| matches!(qualified_name.segments(), ["os", "environ"])), Stmt::AugAssign(ast::StmtAugAssign { target, .. }) => semantic .resolve_qualified_name(map_subscript(target)) - .is_some_and(|call_path| matches!(call_path.segments(), ["os", "environ"])), + .is_some_and(|qualified_name| matches!(qualified_name.segments(), ["os", "environ"])), _ => false, } } @@ -96,5 +100,5 @@ pub fn is_matplotlib_activation(stmt: &Stmt, semantic: &SemanticModel) -> bool { }; semantic .resolve_qualified_name(func.as_ref()) - .is_some_and(|call_path| matches!(call_path.segments(), ["matplotlib", "use"])) + .is_some_and(|qualified_name| matches!(qualified_name.segments(), ["matplotlib", "use"])) } diff --git a/crates/ruff_python_semantic/src/analyze/logging.rs b/crates/ruff_python_semantic/src/analyze/logging.rs index 922fdd8b6234b..d72b46d4e9ec8 100644 --- a/crates/ruff_python_semantic/src/analyze/logging.rs +++ b/crates/ruff_python_semantic/src/analyze/logging.rs @@ -27,9 +27,9 @@ pub fn is_logger_candidate( // If the symbol was imported from another module, ensure that it's either a user-specified // logger object, the `logging` module itself, or `flask.current_app.logger`. - if let Some(call_path) = semantic.resolve_qualified_name(value) { + if let Some(qualified_name) = semantic.resolve_qualified_name(value) { if matches!( - call_path.segments(), + qualified_name.segments(), ["logging"] | ["flask", "current_app", "logger"] ) { return true; @@ -37,7 +37,7 @@ pub fn is_logger_candidate( if logger_objects .iter() - .any(|logger| QualifiedName::from_dotted_name(logger) == call_path) + .any(|logger| QualifiedName::from_dotted_name(logger) == qualified_name) { return true; } @@ -47,8 +47,8 @@ pub fn is_logger_candidate( // Otherwise, if the symbol was defined in the current module, match against some common // logger names. - if let Some(call_path) = UnqualifiedName::from_expr(value) { - if let Some(tail) = call_path.segments().last() { + if let Some(name) = UnqualifiedName::from_expr(value) { + if let Some(tail) = name.segments().last() { if tail.starts_with("log") || tail.ends_with("logger") || tail.ends_with("logging") @@ -79,7 +79,7 @@ pub fn exc_info<'a>(arguments: &'a Arguments, semantic: &SemanticModel) -> Optio .value .as_call_expr() .and_then(|call| semantic.resolve_qualified_name(&call.func)) - .is_some_and(|call_path| matches!(call_path.segments(), ["sys", "exc_info"])) + .is_some_and(|qualified_name| matches!(qualified_name.segments(), ["sys", "exc_info"])) { return Some(exc_info); } diff --git a/crates/ruff_python_semantic/src/analyze/typing.rs b/crates/ruff_python_semantic/src/analyze/typing.rs index 409fc8f0e06fa..59ba701b800fb 100644 --- a/crates/ruff_python_semantic/src/analyze/typing.rs +++ b/crates/ruff_python_semantic/src/analyze/typing.rs @@ -42,43 +42,45 @@ pub fn match_annotated_subscript<'a>( typing_modules: impl Iterator, extend_generics: &[String], ) -> Option { - semantic.resolve_qualified_name(expr).and_then(|call_path| { - if is_standard_library_literal(call_path.segments()) { - return Some(SubscriptKind::Literal); - } + semantic + .resolve_qualified_name(expr) + .and_then(|qualified_name| { + if is_standard_library_literal(qualified_name.segments()) { + return Some(SubscriptKind::Literal); + } - if is_standard_library_generic(call_path.segments()) - || extend_generics - .iter() - .map(|target| QualifiedName::from_dotted_name(target)) - .any(|target| call_path == target) - { - return Some(SubscriptKind::Generic); - } + if is_standard_library_generic(qualified_name.segments()) + || extend_generics + .iter() + .map(|target| QualifiedName::from_dotted_name(target)) + .any(|target| qualified_name == target) + { + return Some(SubscriptKind::Generic); + } - if is_pep_593_generic_type(call_path.segments()) { - return Some(SubscriptKind::PEP593Annotation); - } + if is_pep_593_generic_type(qualified_name.segments()) { + return Some(SubscriptKind::PEP593Annotation); + } - for module in typing_modules { - let module_call_path = QualifiedName::imported(module); - if call_path.starts_with(&module_call_path) { - if let Some(member) = call_path.segments().last() { - if is_literal_member(member) { - return Some(SubscriptKind::Literal); - } - if is_standard_library_generic_member(member) { - return Some(SubscriptKind::Generic); - } - if is_pep_593_generic_member(member) { - return Some(SubscriptKind::PEP593Annotation); + for module in typing_modules { + let module_qualified_name = QualifiedName::imported(module); + if qualified_name.starts_with(&module_qualified_name) { + if let Some(member) = qualified_name.segments().last() { + if is_literal_member(member) { + return Some(SubscriptKind::Literal); + } + if is_standard_library_generic_member(member) { + return Some(SubscriptKind::Generic); + } + if is_pep_593_generic_member(member) { + return Some(SubscriptKind::PEP593Annotation); + } } } } - } - None - }) + None + }) } #[derive(Debug, Clone, Eq, PartialEq)] @@ -105,8 +107,8 @@ pub fn to_pep585_generic(expr: &Expr, semantic: &SemanticModel) -> Option Option bool { semantic .resolve_qualified_name(expr) - .is_some_and(|call_path| { - let [module, name] = call_path.segments() else { + .is_some_and(|qualified_name| { + let [module, name] = qualified_name.segments() else { return false; }; has_pep_585_generic(module, name) @@ -199,10 +201,10 @@ pub fn to_pep604_operator( semantic .resolve_qualified_name(value) .as_ref() - .and_then(|call_path| { - if semantic.match_typing_call_path(call_path, "Optional") { + .and_then(|qualified_name| { + if semantic.match_typing_qualified_name(qualified_name, "Optional") { Some(Pep604Operator::Optional) - } else if semantic.match_typing_call_path(call_path, "Union") { + } else if semantic.match_typing_qualified_name(qualified_name, "Union") { Some(Pep604Operator::Union) } else { None @@ -221,20 +223,20 @@ pub fn is_immutable_annotation( Expr::Name(_) | Expr::Attribute(_) => { semantic .resolve_qualified_name(expr) - .is_some_and(|call_path| { - is_immutable_non_generic_type(call_path.segments()) - || is_immutable_generic_type(call_path.segments()) + .is_some_and(|qualified_name| { + is_immutable_non_generic_type(qualified_name.segments()) + || is_immutable_generic_type(qualified_name.segments()) || extend_immutable_calls .iter() - .any(|target| call_path == *target) + .any(|target| qualified_name == *target) }) } Expr::Subscript(ast::ExprSubscript { value, slice, .. }) => semantic .resolve_qualified_name(value) - .is_some_and(|call_path| { - if is_immutable_generic_type(call_path.segments()) { + .is_some_and(|qualified_name| { + if is_immutable_generic_type(qualified_name.segments()) { true - } else if matches!(call_path.segments(), ["typing", "Union"]) { + } else if matches!(qualified_name.segments(), ["typing", "Union"]) { if let Expr::Tuple(ast::ExprTuple { elts, .. }) = slice.as_ref() { elts.iter().all(|elt| { is_immutable_annotation(elt, semantic, extend_immutable_calls) @@ -242,9 +244,9 @@ pub fn is_immutable_annotation( } else { false } - } else if matches!(call_path.segments(), ["typing", "Optional"]) { + } else if matches!(qualified_name.segments(), ["typing", "Optional"]) { is_immutable_annotation(slice, semantic, extend_immutable_calls) - } else if is_pep_593_generic_type(call_path.segments()) { + } else if is_pep_593_generic_type(qualified_name.segments()) { if let Expr::Tuple(ast::ExprTuple { elts, .. }) = slice.as_ref() { elts.first().is_some_and(|elt| { is_immutable_annotation(elt, semantic, extend_immutable_calls) @@ -278,11 +280,11 @@ pub fn is_immutable_func( ) -> bool { semantic .resolve_qualified_name(func) - .is_some_and(|call_path| { - is_immutable_return_type(call_path.segments()) + .is_some_and(|qualified_name| { + is_immutable_return_type(qualified_name.segments()) || extend_immutable_calls .iter() - .any(|target| call_path == *target) + .any(|target| qualified_name == *target) }) } @@ -344,8 +346,11 @@ pub fn is_sys_version_block(stmt: &ast::StmtIf, semantic: &SemanticModel) -> boo any_over_expr(test, &|expr| { semantic .resolve_qualified_name(expr) - .is_some_and(|call_path| { - matches!(call_path.segments(), ["sys", "version_info" | "platform"]) + .is_some_and(|qualified_name| { + matches!( + qualified_name.segments(), + ["sys", "version_info" | "platform"] + ) }) }) } @@ -617,18 +622,18 @@ impl TypeChecker for IoBaseChecker { fn match_annotation(annotation: &Expr, semantic: &SemanticModel) -> bool { semantic .resolve_qualified_name(annotation) - .is_some_and(|call_path| { - if semantic.match_typing_call_path(&call_path, "IO") { + .is_some_and(|qualified_name| { + if semantic.match_typing_qualified_name(&qualified_name, "IO") { return true; } - if semantic.match_typing_call_path(&call_path, "BinaryIO") { + if semantic.match_typing_qualified_name(&qualified_name, "BinaryIO") { return true; } - if semantic.match_typing_call_path(&call_path, "TextIO") { + if semantic.match_typing_qualified_name(&qualified_name, "TextIO") { return true; } matches!( - call_path.segments(), + qualified_name.segments(), [ "io", "IOBase" @@ -662,9 +667,9 @@ impl TypeChecker for IoBaseChecker { if let Expr::Call(ast::ExprCall { func, .. }) = value.as_ref() { return semantic .resolve_qualified_name(func) - .is_some_and(|call_path| { + .is_some_and(|qualified_name| { matches!( - call_path.segments(), + qualified_name.segments(), [ "pathlib", "Path" | "PurePath" | "PurePosixPath" | "PureWindowsPath" @@ -678,9 +683,9 @@ impl TypeChecker for IoBaseChecker { // Ex) `open("file.txt")` semantic .resolve_qualified_name(func.as_ref()) - .is_some_and(|call_path| { + .is_some_and(|qualified_name| { matches!( - call_path.segments(), + qualified_name.segments(), ["io", "open" | "open_code"] | ["os" | "", "open"] ) }) diff --git a/crates/ruff_python_semantic/src/analyze/visibility.rs b/crates/ruff_python_semantic/src/analyze/visibility.rs index b5edc74c1e8d3..99e107f3d8a3b 100644 --- a/crates/ruff_python_semantic/src/analyze/visibility.rs +++ b/crates/ruff_python_semantic/src/analyze/visibility.rs @@ -18,7 +18,7 @@ pub fn is_staticmethod(decorator_list: &[Decorator], semantic: &SemanticModel) - decorator_list.iter().any(|decorator| { semantic .resolve_qualified_name(map_callable(&decorator.expression)) - .is_some_and(|call_path| matches!(call_path.segments(), ["", "staticmethod"])) + .is_some_and(|qualified_name| matches!(qualified_name.segments(), ["", "staticmethod"])) }) } @@ -27,7 +27,7 @@ pub fn is_classmethod(decorator_list: &[Decorator], semantic: &SemanticModel) -> decorator_list.iter().any(|decorator| { semantic .resolve_qualified_name(map_callable(&decorator.expression)) - .is_some_and(|call_path| matches!(call_path.segments(), ["", "classmethod"])) + .is_some_and(|qualified_name| matches!(qualified_name.segments(), ["", "classmethod"])) }) } @@ -50,9 +50,9 @@ pub fn is_abstract(decorator_list: &[Decorator], semantic: &SemanticModel) -> bo decorator_list.iter().any(|decorator| { semantic .resolve_qualified_name(map_callable(&decorator.expression)) - .is_some_and(|call_path| { + .is_some_and(|qualified_name| { matches!( - call_path.segments(), + qualified_name.segments(), [ "abc", "abstractmethod" @@ -76,13 +76,13 @@ pub fn is_property( decorator_list.iter().any(|decorator| { semantic .resolve_qualified_name(map_callable(&decorator.expression)) - .is_some_and(|call_path| { + .is_some_and(|qualified_name| { matches!( - call_path.segments(), + qualified_name.segments(), ["", "property"] | ["functools", "cached_property"] ) || extra_properties .iter() - .any(|extra_property| extra_property.segments() == call_path.segments()) + .any(|extra_property| extra_property.segments() == qualified_name.segments()) }) }) } @@ -187,9 +187,9 @@ pub(crate) fn function_visibility(function: &ast::StmtFunctionDef) -> Visibility pub fn method_visibility(function: &ast::StmtFunctionDef) -> Visibility { // Is this a setter or deleter? if function.decorator_list.iter().any(|decorator| { - UnqualifiedName::from_expr(&decorator.expression).is_some_and(|call_path| { - call_path.segments() == [function.name.as_str(), "setter"] - || call_path.segments() == [function.name.as_str(), "deleter"] + UnqualifiedName::from_expr(&decorator.expression).is_some_and(|name| { + name.segments() == [function.name.as_str(), "setter"] + || name.segments() == [function.name.as_str(), "deleter"] }) }) { return Visibility::Private; diff --git a/crates/ruff_python_semantic/src/binding.rs b/crates/ruff_python_semantic/src/binding.rs index 28bd77fa28c28..10b4d13439246 100644 --- a/crates/ruff_python_semantic/src/binding.rs +++ b/crates/ruff_python_semantic/src/binding.rs @@ -4,7 +4,7 @@ use std::ops::{Deref, DerefMut}; use bitflags::bitflags; use ruff_index::{newtype_index, IndexSlice, IndexVec}; -use ruff_python_ast::name::format_call_path_segments; +use ruff_python_ast::name::format_qualified_name_segments; use ruff_python_ast::Stmt; use ruff_source_file::Locator; use ruff_text_size::{Ranged, TextRange}; @@ -561,7 +561,7 @@ pub trait Imported<'a> { /// Returns the fully-qualified name of the imported symbol. fn qualified_name(&self) -> String { let mut output = String::new(); - format_call_path_segments(self.call_path(), &mut output).unwrap(); + format_qualified_name_segments(self.call_path(), &mut output).unwrap(); output } } diff --git a/crates/ruff_python_semantic/src/model.rs b/crates/ruff_python_semantic/src/model.rs index 67a1d886ecec9..3556fb28a9b76 100644 --- a/crates/ruff_python_semantic/src/model.rs +++ b/crates/ruff_python_semantic/src/model.rs @@ -174,13 +174,19 @@ impl<'a> SemanticModel<'a> { self.seen_typing() && self .resolve_qualified_name(expr) - .is_some_and(|call_path| self.match_typing_call_path(&call_path, target)) + .is_some_and(|qualified_name| { + self.match_typing_qualified_name(&qualified_name, target) + }) } /// Return `true` if the call path is a reference to `typing.${target}`. - pub fn match_typing_call_path(&self, call_path: &QualifiedName, target: &str) -> bool { + pub fn match_typing_qualified_name( + &self, + qualified_name: &QualifiedName, + target: &str, + ) -> bool { if matches!( - call_path.segments(), + qualified_name.segments(), ["typing" | "_typeshed" | "typing_extensions", member] if *member == target ) { return true; @@ -191,7 +197,7 @@ impl<'a> SemanticModel<'a> { let mut builder = QualifiedNameBuilder::from_qualified_name(module); builder.push(target); let target_path = builder.build(); - call_path == &target_path + qualified_name == &target_path }) { return true; } @@ -568,10 +574,10 @@ impl<'a> SemanticModel<'a> { /// associated with `Class`, then the `BindingKind::FunctionDefinition` associated with /// `Class.method`. pub fn lookup_attribute(&self, value: &Expr) -> Option { - let call_path = UnqualifiedName::from_expr(value)?; + let unqualified_name = UnqualifiedName::from_expr(value)?; // Find the symbol in the current scope. - let (symbol, attribute) = call_path.segments().split_first()?; + let (symbol, attribute) = unqualified_name.segments().split_first()?; let mut binding_id = self.lookup_symbol(symbol)?; // Recursively resolve class attributes, e.g., `foo.bar.baz` in. @@ -683,23 +689,19 @@ impl<'a> SemanticModel<'a> { .map(|id| self.binding(id))?; match &binding.kind { - BindingKind::Import(Import { - qualified_name: call_path, - }) => { + BindingKind::Import(Import { qualified_name }) => { let unqualified_name = UnqualifiedName::from_expr(value)?; let (_, tail) = unqualified_name.segments().split_first()?; let resolved: QualifiedName = - call_path.iter().chain(tail.iter()).copied().collect(); + qualified_name.iter().chain(tail.iter()).copied().collect(); Some(resolved) } - BindingKind::SubmoduleImport(SubmoduleImport { - qualified_name: call_path, - }) => { - let value_path = UnqualifiedName::from_expr(value)?; - let (_, tail) = value_path.segments().split_first()?; + BindingKind::SubmoduleImport(SubmoduleImport { qualified_name }) => { + let value_name = UnqualifiedName::from_expr(value)?; + let (_, tail) = value_name.segments().split_first()?; Some( - call_path + qualified_name .iter() .take(1) .chain(tail.iter()) @@ -707,18 +709,18 @@ impl<'a> SemanticModel<'a> { .collect(), ) } - BindingKind::FromImport(FromImport { - qualified_name: call_path, - }) => { - let value_path = UnqualifiedName::from_expr(value)?; - let (_, tail) = value_path.segments().split_first()?; + BindingKind::FromImport(FromImport { qualified_name }) => { + let value_name = UnqualifiedName::from_expr(value)?; + let (_, tail) = value_name.segments().split_first()?; - let resolved: QualifiedName = - if call_path.first().map_or(false, |segment| *segment == ".") { - from_relative_import(self.module_path?, call_path, tail)? - } else { - call_path.iter().chain(tail.iter()).copied().collect() - }; + let resolved: QualifiedName = if qualified_name + .first() + .map_or(false, |segment| *segment == ".") + { + from_relative_import(self.module_path?, qualified_name, tail)? + } else { + qualified_name.iter().chain(tail.iter()).copied().collect() + }; Some(resolved) } BindingKind::Builtin => { @@ -727,21 +729,21 @@ impl<'a> SemanticModel<'a> { Some(QualifiedName::from_slice(&["", head.id.as_str()])) } else { // Ex) `dict.__dict__` - let value_path = UnqualifiedName::from_expr(value)?; + let value_name = UnqualifiedName::from_expr(value)?; Some( std::iter::once("") - .chain(value_path.segments().iter().copied()) + .chain(value_name.segments().iter().copied()) .collect(), ) } } BindingKind::ClassDefinition(_) | BindingKind::FunctionDefinition(_) => { - let value_path = UnqualifiedName::from_expr(value)?; + let value_name = UnqualifiedName::from_expr(value)?; let resolved: QualifiedName = self .module_path? .iter() .map(String::as_str) - .chain(value_path.segments().iter().copied()) + .chain(value_name.segments().iter().copied()) .collect(); Some(resolved) } @@ -777,10 +779,8 @@ impl<'a> SemanticModel<'a> { // Ex) Given `module="sys"` and `object="exit"`: // `import sys` -> `sys.exit` // `import sys as sys2` -> `sys2.exit` - BindingKind::Import(Import { - qualified_name: call_path, - }) => { - if call_path.as_ref() == module_path.as_slice() { + BindingKind::Import(Import { qualified_name }) => { + if qualified_name.as_ref() == module_path.as_slice() { if let Some(source) = binding.source { // Verify that `sys` isn't bound in an inner scope. if self @@ -801,10 +801,10 @@ impl<'a> SemanticModel<'a> { // Ex) Given `module="os.path"` and `object="join"`: // `from os.path import join` -> `join` // `from os.path import join as join2` -> `join2` - BindingKind::FromImport(FromImport { - qualified_name: call_path, - }) => { - if let Some((target_member, target_module)) = call_path.split_last() { + BindingKind::FromImport(FromImport { qualified_name }) => { + if let Some((target_member, target_module)) = + qualified_name.split_last() + { if target_module == module_path.as_slice() && target_member == &member { @@ -830,10 +830,8 @@ impl<'a> SemanticModel<'a> { // `import os.path ` -> `os.name` // Ex) Given `module="os.path"` and `object="join"`: // `import os.path ` -> `os.path.join` - BindingKind::SubmoduleImport(SubmoduleImport { - qualified_name: call_path, - }) => { - if call_path.starts_with(&module_path) { + BindingKind::SubmoduleImport(SubmoduleImport { qualified_name }) => { + if qualified_name.starts_with(&module_path) { if let Some(source) = binding.source { // Verify that `os` isn't bound in an inner scope. if self diff --git a/crates/ruff_python_stdlib/src/typing.rs b/crates/ruff_python_stdlib/src/typing.rs index 45b472ce707d6..a917dfa1ca2ca 100644 --- a/crates/ruff_python_stdlib/src/typing.rs +++ b/crates/ruff_python_stdlib/src/typing.rs @@ -2,9 +2,9 @@ /// can be used as `list[int]`). /// /// See: -pub fn is_standard_library_generic(call_path: &[&str]) -> bool { +pub fn is_standard_library_generic(qualified_name: &[&str]) -> bool { matches!( - call_path, + qualified_name, ["", "dict" | "frozenset" | "list" | "set" | "tuple" | "type"] | [ "collections" | "typing" | "typing_extensions", @@ -118,13 +118,16 @@ pub fn is_standard_library_generic(call_path: &[&str]) -> bool { /// See: /// /// [PEP 593]: https://peps.python.org/pep-0593/ -pub fn is_pep_593_generic_type(call_path: &[&str]) -> bool { - matches!(call_path, ["typing" | "typing_extensions", "Annotated"]) +pub fn is_pep_593_generic_type(qualified_name: &[&str]) -> bool { + matches!( + qualified_name, + ["typing" | "typing_extensions", "Annotated"] + ) } /// Returns `true` if a call path is `Literal`. -pub fn is_standard_library_literal(call_path: &[&str]) -> bool { - matches!(call_path, ["typing" | "typing_extensions", "Literal"]) +pub fn is_standard_library_literal(qualified_name: &[&str]) -> bool { + matches!(qualified_name, ["typing" | "typing_extensions", "Literal"]) } /// Returns `true` if a name matches that of a generic from the Python standard library (e.g. @@ -219,9 +222,9 @@ pub fn is_literal_member(member: &str) -> bool { /// Returns `true` if a call path represents that of an immutable, non-generic type from the Python /// standard library (e.g. `int` or `str`). -pub fn is_immutable_non_generic_type(call_path: &[&str]) -> bool { +pub fn is_immutable_non_generic_type(qualified_name: &[&str]) -> bool { matches!( - call_path, + qualified_name, ["collections", "abc", "Sized"] | ["typing", "LiteralString" | "Sized"] | [ @@ -241,9 +244,9 @@ pub fn is_immutable_non_generic_type(call_path: &[&str]) -> bool { /// Returns `true` if a call path represents that of an immutable, generic type from the Python /// standard library (e.g. `tuple`). -pub fn is_immutable_generic_type(call_path: &[&str]) -> bool { +pub fn is_immutable_generic_type(qualified_name: &[&str]) -> bool { matches!( - call_path, + qualified_name, ["", "tuple"] | [ "collections", @@ -279,9 +282,9 @@ pub fn is_immutable_generic_type(call_path: &[&str]) -> bool { /// Returns `true` if a call path represents a function from the Python standard library that /// returns a mutable value (e.g., `dict`). -pub fn is_mutable_return_type(call_path: &[&str]) -> bool { +pub fn is_mutable_return_type(qualified_name: &[&str]) -> bool { matches!( - call_path, + qualified_name, ["", "dict" | "list" | "set"] | [ "collections", @@ -292,9 +295,9 @@ pub fn is_mutable_return_type(call_path: &[&str]) -> bool { /// Returns `true` if a call path represents a function from the Python standard library that /// returns a immutable value (e.g., `bool`). -pub fn is_immutable_return_type(call_path: &[&str]) -> bool { +pub fn is_immutable_return_type(qualified_name: &[&str]) -> bool { matches!( - call_path, + qualified_name, ["datetime", "date" | "datetime" | "timedelta"] | ["decimal", "Decimal"] | ["fractions", "Fraction"]