mirror of https://github.com/astral-sh/ruff
Store the call path
This commit is contained in:
parent
de898c52eb
commit
15273c6d95
|
|
@ -26,6 +26,7 @@
|
||||||
//! represents the lint-rule analysis phase. In the future, these steps may be separated into
|
//! represents the lint-rule analysis phase. In the future, these steps may be separated into
|
||||||
//! distinct passes over the AST.
|
//! distinct passes over the AST.
|
||||||
|
|
||||||
|
use std::iter::once;
|
||||||
use std::path::Path;
|
use std::path::Path;
|
||||||
|
|
||||||
use itertools::Itertools;
|
use itertools::Itertools;
|
||||||
|
|
@ -320,10 +321,14 @@ where
|
||||||
// "foo.bar".
|
// "foo.bar".
|
||||||
let name = alias.name.split('.').next().unwrap();
|
let name = alias.name.split('.').next().unwrap();
|
||||||
let qualified_name = &alias.name;
|
let qualified_name = &alias.name;
|
||||||
|
let call_path: Box<[&str]> = qualified_name.split('.').collect();
|
||||||
self.add_binding(
|
self.add_binding(
|
||||||
name,
|
name,
|
||||||
alias.identifier(),
|
alias.identifier(),
|
||||||
BindingKind::SubmoduleImport(SubmoduleImport { qualified_name }),
|
BindingKind::SubmoduleImport(SubmoduleImport {
|
||||||
|
qualified_name,
|
||||||
|
call_path,
|
||||||
|
}),
|
||||||
BindingFlags::EXTERNAL,
|
BindingFlags::EXTERNAL,
|
||||||
);
|
);
|
||||||
} else {
|
} else {
|
||||||
|
|
@ -341,10 +346,14 @@ where
|
||||||
|
|
||||||
let name = alias.asname.as_ref().unwrap_or(&alias.name);
|
let name = alias.asname.as_ref().unwrap_or(&alias.name);
|
||||||
let qualified_name = &alias.name;
|
let qualified_name = &alias.name;
|
||||||
|
let call_path: Box<[&str]> = qualified_name.split('.').collect();
|
||||||
self.add_binding(
|
self.add_binding(
|
||||||
name,
|
name,
|
||||||
alias.identifier(),
|
alias.identifier(),
|
||||||
BindingKind::Import(Import { qualified_name }),
|
BindingKind::Import(Import {
|
||||||
|
qualified_name,
|
||||||
|
call_path,
|
||||||
|
}),
|
||||||
flags,
|
flags,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
@ -390,10 +399,16 @@ where
|
||||||
let name = alias.asname.as_ref().unwrap_or(&alias.name);
|
let name = alias.asname.as_ref().unwrap_or(&alias.name);
|
||||||
let qualified_name =
|
let qualified_name =
|
||||||
helpers::format_import_from_member(level, module, &alias.name);
|
helpers::format_import_from_member(level, module, &alias.name);
|
||||||
|
let module = module.unwrap();
|
||||||
|
let call_path: Box<[&str]> =
|
||||||
|
module.split('.').chain(once(alias.name.as_str())).collect();
|
||||||
self.add_binding(
|
self.add_binding(
|
||||||
name,
|
name,
|
||||||
alias.identifier(),
|
alias.identifier(),
|
||||||
BindingKind::FromImport(FromImport { qualified_name }),
|
BindingKind::FromImport(FromImport {
|
||||||
|
qualified_name,
|
||||||
|
call_path,
|
||||||
|
}),
|
||||||
flags,
|
flags,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -50,7 +50,7 @@ pub(crate) fn unaliased_collections_abc_set_import(
|
||||||
checker: &Checker,
|
checker: &Checker,
|
||||||
binding: &Binding,
|
binding: &Binding,
|
||||||
) -> Option<Diagnostic> {
|
) -> Option<Diagnostic> {
|
||||||
let BindingKind::FromImport(FromImport { qualified_name }) = &binding.kind else {
|
let BindingKind::FromImport(FromImport { qualified_name, .. }) = &binding.kind else {
|
||||||
return None;
|
return None;
|
||||||
};
|
};
|
||||||
if qualified_name.as_str() != "collections.abc.Set" {
|
if qualified_name.as_str() != "collections.abc.Set" {
|
||||||
|
|
|
||||||
|
|
@ -51,7 +51,7 @@ impl Violation for RuntimeImportInTypeCheckingBlock {
|
||||||
|
|
||||||
#[derive_message_formats]
|
#[derive_message_formats]
|
||||||
fn message(&self) -> String {
|
fn message(&self) -> String {
|
||||||
let RuntimeImportInTypeCheckingBlock { qualified_name } = self;
|
let RuntimeImportInTypeCheckingBlock { qualified_name, .. } = self;
|
||||||
format!(
|
format!(
|
||||||
"Move import `{qualified_name}` out of type-checking block. Import is used for more than type hinting."
|
"Move import `{qualified_name}` out of type-checking block. Import is used for more than type hinting."
|
||||||
)
|
)
|
||||||
|
|
|
||||||
|
|
@ -39,9 +39,11 @@ pub(super) fn test_expression(expr: &Expr, semantic: &SemanticModel) -> Resoluti
|
||||||
| BindingKind::LoopVar
|
| BindingKind::LoopVar
|
||||||
| BindingKind::Global
|
| BindingKind::Global
|
||||||
| BindingKind::Nonlocal(_) => Resolution::RelevantLocal,
|
| BindingKind::Nonlocal(_) => Resolution::RelevantLocal,
|
||||||
BindingKind::Import(Import {
|
BindingKind::Import(Import { qualified_name, .. })
|
||||||
qualified_name: module,
|
if qualified_name == "pandas" =>
|
||||||
}) if module == "pandas" => Resolution::PandasModule,
|
{
|
||||||
|
Resolution::PandasModule
|
||||||
|
}
|
||||||
_ => Resolution::IrrelevantBinding,
|
_ => Resolution::IrrelevantBinding,
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
|
||||||
|
|
@ -68,7 +68,8 @@ pub(crate) fn inplace_argument(
|
||||||
matches!(
|
matches!(
|
||||||
binding.kind,
|
binding.kind,
|
||||||
BindingKind::Import(Import {
|
BindingKind::Import(Import {
|
||||||
qualified_name: "pandas"
|
qualified_name: "pandas",
|
||||||
|
..
|
||||||
})
|
})
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
|
|
|
||||||
|
|
@ -5,6 +5,7 @@ use ruff_python_ast::Ranged;
|
||||||
use ruff_text_size::TextRange;
|
use ruff_text_size::TextRange;
|
||||||
|
|
||||||
use ruff_index::{newtype_index, IndexSlice, IndexVec};
|
use ruff_index::{newtype_index, IndexSlice, IndexVec};
|
||||||
|
use ruff_python_ast::source_code::Locator;
|
||||||
use ruff_source_file::Locator;
|
use ruff_source_file::Locator;
|
||||||
|
|
||||||
use crate::context::ExecutionContext;
|
use crate::context::ExecutionContext;
|
||||||
|
|
@ -117,38 +118,38 @@ impl<'a> Binding<'a> {
|
||||||
// import foo.baz
|
// import foo.baz
|
||||||
// ```
|
// ```
|
||||||
BindingKind::Import(Import {
|
BindingKind::Import(Import {
|
||||||
qualified_name: redefinition,
|
call_path: redefinition,
|
||||||
}) => {
|
}) => {
|
||||||
if let BindingKind::SubmoduleImport(SubmoduleImport {
|
if let BindingKind::SubmoduleImport(SubmoduleImport {
|
||||||
qualified_name: definition,
|
call_path: definition,
|
||||||
}) = &existing.kind
|
}) = &existing.kind
|
||||||
{
|
{
|
||||||
return redefinition == definition;
|
return redefinition == definition;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
BindingKind::FromImport(FromImport {
|
BindingKind::FromImport(FromImport {
|
||||||
qualified_name: redefinition,
|
call_path: redefinition,
|
||||||
}) => {
|
}) => {
|
||||||
if let BindingKind::SubmoduleImport(SubmoduleImport {
|
if let BindingKind::SubmoduleImport(SubmoduleImport {
|
||||||
qualified_name: definition,
|
call_path: definition,
|
||||||
}) = &existing.kind
|
}) = &existing.kind
|
||||||
{
|
{
|
||||||
return redefinition == definition;
|
return redefinition == definition;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
BindingKind::SubmoduleImport(SubmoduleImport {
|
BindingKind::SubmoduleImport(SubmoduleImport {
|
||||||
qualified_name: redefinition,
|
call_path: redefinition,
|
||||||
}) => match &existing.kind {
|
}) => match &existing.kind {
|
||||||
BindingKind::Import(Import {
|
BindingKind::Import(Import {
|
||||||
qualified_name: definition,
|
call_path: definition,
|
||||||
})
|
})
|
||||||
| BindingKind::SubmoduleImport(SubmoduleImport {
|
| BindingKind::SubmoduleImport(SubmoduleImport {
|
||||||
qualified_name: definition,
|
call_path: definition,
|
||||||
}) => {
|
}) => {
|
||||||
return redefinition == definition;
|
return redefinition == definition;
|
||||||
}
|
}
|
||||||
BindingKind::FromImport(FromImport {
|
BindingKind::FromImport(FromImport {
|
||||||
qualified_name: definition,
|
call_path: definition,
|
||||||
}) => {
|
}) => {
|
||||||
return redefinition == definition;
|
return redefinition == definition;
|
||||||
}
|
}
|
||||||
|
|
@ -178,9 +179,9 @@ impl<'a> Binding<'a> {
|
||||||
/// Returns the fully-qualified symbol name, if this symbol was imported from another module.
|
/// Returns the fully-qualified symbol name, if this symbol was imported from another module.
|
||||||
pub fn qualified_name(&self) -> Option<&str> {
|
pub fn qualified_name(&self) -> Option<&str> {
|
||||||
match &self.kind {
|
match &self.kind {
|
||||||
BindingKind::Import(Import { qualified_name }) => Some(qualified_name),
|
BindingKind::Import(Import { qualified_name, .. }) => Some(qualified_name),
|
||||||
BindingKind::FromImport(FromImport { qualified_name }) => Some(qualified_name),
|
BindingKind::FromImport(FromImport { qualified_name, .. }) => Some(qualified_name),
|
||||||
BindingKind::SubmoduleImport(SubmoduleImport { qualified_name }) => {
|
BindingKind::SubmoduleImport(SubmoduleImport { qualified_name, .. }) => {
|
||||||
Some(qualified_name)
|
Some(qualified_name)
|
||||||
}
|
}
|
||||||
_ => None,
|
_ => None,
|
||||||
|
|
@ -191,11 +192,11 @@ impl<'a> Binding<'a> {
|
||||||
/// symbol was imported from another module.
|
/// symbol was imported from another module.
|
||||||
pub fn module_name(&self) -> Option<&str> {
|
pub fn module_name(&self) -> Option<&str> {
|
||||||
match &self.kind {
|
match &self.kind {
|
||||||
BindingKind::Import(Import { qualified_name })
|
BindingKind::Import(Import { qualified_name, .. })
|
||||||
| BindingKind::SubmoduleImport(SubmoduleImport { qualified_name }) => {
|
| BindingKind::SubmoduleImport(SubmoduleImport { qualified_name, .. }) => {
|
||||||
Some(qualified_name.split('.').next().unwrap_or(qualified_name))
|
Some(qualified_name.split('.').next().unwrap_or(qualified_name))
|
||||||
}
|
}
|
||||||
BindingKind::FromImport(FromImport { qualified_name }) => Some(
|
BindingKind::FromImport(FromImport { qualified_name, .. }) => Some(
|
||||||
qualified_name
|
qualified_name
|
||||||
.rsplit_once('.')
|
.rsplit_once('.')
|
||||||
.map_or(qualified_name, |(module, _)| module),
|
.map_or(qualified_name, |(module, _)| module),
|
||||||
|
|
@ -357,17 +358,19 @@ pub struct Import<'a> {
|
||||||
/// Ex) Given `import foo`, `qualified_name` would be "foo".
|
/// Ex) Given `import foo`, `qualified_name` would be "foo".
|
||||||
/// Ex) Given `import foo as bar`, `qualified_name` would be "foo".
|
/// Ex) Given `import foo as bar`, `qualified_name` would be "foo".
|
||||||
pub qualified_name: &'a str,
|
pub qualified_name: &'a str,
|
||||||
|
pub call_path: Box<[&'a str]>,
|
||||||
}
|
}
|
||||||
|
|
||||||
/// A binding for a member imported from a module, keyed on the name to which the member is bound.
|
/// A binding for a member imported from a module, keyed on the name to which the member is bound.
|
||||||
/// Ex) `from foo import bar` would be keyed on "bar".
|
/// Ex) `from foo import bar` would be keyed on "bar".
|
||||||
/// Ex) `from foo import bar as baz` would be keyed on "baz".
|
/// Ex) `from foo import bar as baz` would be keyed on "baz".
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
pub struct FromImport {
|
pub struct FromImport<'a> {
|
||||||
/// The full name of the member being imported.
|
/// The full name of the member being imported.
|
||||||
/// Ex) Given `from foo import bar`, `qualified_name` would be "foo.bar".
|
/// Ex) Given `from foo import bar`, `qualified_name` would be "foo.bar".
|
||||||
/// Ex) Given `from foo import bar as baz`, `qualified_name` would be "foo.bar".
|
/// Ex) Given `from foo import bar as baz`, `qualified_name` would be "foo.bar".
|
||||||
pub qualified_name: String,
|
pub qualified_name: String,
|
||||||
|
pub call_path: Box<[&'a str]>,
|
||||||
}
|
}
|
||||||
|
|
||||||
/// A binding for a submodule imported from a module, keyed on the name of the parent module.
|
/// A binding for a submodule imported from a module, keyed on the name of the parent module.
|
||||||
|
|
@ -377,6 +380,7 @@ pub struct SubmoduleImport<'a> {
|
||||||
/// The full name of the submodule being imported.
|
/// The full name of the submodule being imported.
|
||||||
/// Ex) Given `import foo.bar`, `qualified_name` would be "foo.bar".
|
/// Ex) Given `import foo.bar`, `qualified_name` would be "foo.bar".
|
||||||
pub qualified_name: &'a str,
|
pub qualified_name: &'a str,
|
||||||
|
pub call_path: Box<[&'a str]>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone, is_macro::Is)]
|
#[derive(Debug, Clone, is_macro::Is)]
|
||||||
|
|
@ -485,7 +489,7 @@ pub enum BindingKind<'a> {
|
||||||
/// ```python
|
/// ```python
|
||||||
/// from foo import bar
|
/// from foo import bar
|
||||||
/// ```
|
/// ```
|
||||||
FromImport(FromImport),
|
FromImport(FromImport<'a>),
|
||||||
|
|
||||||
/// A binding for a submodule imported from a module, like `bar` in:
|
/// A binding for a submodule imported from a module, like `bar` in:
|
||||||
/// ```python
|
/// ```python
|
||||||
|
|
|
||||||
|
|
@ -1,15 +1,14 @@
|
||||||
use std::path::Path;
|
use std::path::Path;
|
||||||
|
|
||||||
use bitflags::bitflags;
|
use bitflags::bitflags;
|
||||||
use ruff_python_ast::{self as ast, Expr, Ranged, Stmt};
|
|
||||||
use ruff_text_size::{TextRange, TextSize};
|
|
||||||
use rustc_hash::FxHashMap;
|
use rustc_hash::FxHashMap;
|
||||||
use smallvec::smallvec;
|
use smallvec::{smallvec, SmallVec};
|
||||||
|
|
||||||
use ruff_python_ast::call_path::{collect_call_path, from_unqualified_name, CallPath};
|
use ruff_python_ast::call_path::{collect_call_path, from_unqualified_name, CallPath};
|
||||||
use ruff_python_ast::helpers::from_relative_import;
|
use ruff_python_ast::{self as ast, Expr, Ranged, Stmt};
|
||||||
use ruff_python_stdlib::path::is_python_stub_file;
|
use ruff_python_stdlib::path::is_python_stub_file;
|
||||||
use ruff_python_stdlib::typing::is_typing_extension;
|
use ruff_python_stdlib::typing::is_typing_extension;
|
||||||
|
use ruff_text_size::{TextRange, TextSize};
|
||||||
|
|
||||||
use crate::binding::{
|
use crate::binding::{
|
||||||
Binding, BindingFlags, BindingId, BindingKind, Bindings, Exceptions, FromImport, Import,
|
Binding, BindingFlags, BindingId, BindingKind, Bindings, Exceptions, FromImport, Import,
|
||||||
|
|
@ -633,46 +632,32 @@ impl<'a> SemanticModel<'a> {
|
||||||
.or_else(|| self.find_binding(&head.id))?;
|
.or_else(|| self.find_binding(&head.id))?;
|
||||||
|
|
||||||
match &binding.kind {
|
match &binding.kind {
|
||||||
BindingKind::Import(Import {
|
BindingKind::Import(Import { call_path, .. }) => {
|
||||||
qualified_name: name,
|
let x = collect_call_path(value)?;
|
||||||
}) => {
|
let (_, tail) = x.split_first()?;
|
||||||
let call_path = collect_call_path(value)?;
|
|
||||||
let (_, tail) = call_path.split_first()?;
|
|
||||||
|
|
||||||
|
let mut resolved = SmallVec::with_capacity(call_path.len() + tail.len());
|
||||||
|
resolved.extend_from_slice(call_path);
|
||||||
|
resolved.extend_from_slice(tail);
|
||||||
|
Some(resolved)
|
||||||
|
}
|
||||||
|
BindingKind::SubmoduleImport(SubmoduleImport { qualified_name, .. }) => {
|
||||||
|
let x = collect_call_path(value)?;
|
||||||
|
let (_, tail) = x.split_first()?;
|
||||||
|
|
||||||
|
let name = qualified_name.split('.').next().unwrap_or(qualified_name);
|
||||||
let mut source_path: CallPath = from_unqualified_name(name);
|
let mut source_path: CallPath = from_unqualified_name(name);
|
||||||
source_path.extend_from_slice(tail);
|
source_path.extend_from_slice(tail);
|
||||||
Some(source_path)
|
Some(source_path)
|
||||||
}
|
}
|
||||||
BindingKind::SubmoduleImport(SubmoduleImport {
|
BindingKind::FromImport(FromImport { call_path, .. }) => {
|
||||||
qualified_name: name,
|
let x = collect_call_path(value)?;
|
||||||
}) => {
|
let (_, tail) = x.split_first()?;
|
||||||
let call_path = collect_call_path(value)?;
|
|
||||||
let (_, tail) = call_path.split_first()?;
|
|
||||||
|
|
||||||
let name = name.split('.').next().unwrap_or(name);
|
let mut resolved = SmallVec::with_capacity(call_path.len() + tail.len());
|
||||||
let mut source_path: CallPath = from_unqualified_name(name);
|
resolved.extend_from_slice(call_path);
|
||||||
source_path.extend_from_slice(tail);
|
resolved.extend_from_slice(tail);
|
||||||
Some(source_path)
|
Some(resolved)
|
||||||
}
|
|
||||||
BindingKind::FromImport(FromImport {
|
|
||||||
qualified_name: name,
|
|
||||||
}) => {
|
|
||||||
let call_path = collect_call_path(value)?;
|
|
||||||
let (_, tail) = call_path.split_first()?;
|
|
||||||
|
|
||||||
if name.starts_with('.') {
|
|
||||||
let mut source_path = from_relative_import(self.module_path?, name);
|
|
||||||
if source_path.is_empty() {
|
|
||||||
None
|
|
||||||
} else {
|
|
||||||
source_path.extend_from_slice(tail);
|
|
||||||
Some(source_path)
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
let mut source_path: CallPath = from_unqualified_name(name);
|
|
||||||
source_path.extend_from_slice(tail);
|
|
||||||
Some(source_path)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
BindingKind::Builtin => Some(smallvec!["", head.id.as_str()]),
|
BindingKind::Builtin => Some(smallvec!["", head.id.as_str()]),
|
||||||
_ => None,
|
_ => None,
|
||||||
|
|
@ -703,7 +688,7 @@ impl<'a> SemanticModel<'a> {
|
||||||
// Ex) Given `module="sys"` and `object="exit"`:
|
// Ex) Given `module="sys"` and `object="exit"`:
|
||||||
// `import sys` -> `sys.exit`
|
// `import sys` -> `sys.exit`
|
||||||
// `import sys as sys2` -> `sys2.exit`
|
// `import sys as sys2` -> `sys2.exit`
|
||||||
BindingKind::Import(Import { qualified_name }) => {
|
BindingKind::Import(Import { qualified_name, .. }) => {
|
||||||
if qualified_name == &module {
|
if qualified_name == &module {
|
||||||
if let Some(source) = binding.source {
|
if let Some(source) = binding.source {
|
||||||
// Verify that `sys` isn't bound in an inner scope.
|
// Verify that `sys` isn't bound in an inner scope.
|
||||||
|
|
@ -724,7 +709,7 @@ impl<'a> SemanticModel<'a> {
|
||||||
// Ex) Given `module="os.path"` and `object="join"`:
|
// Ex) Given `module="os.path"` and `object="join"`:
|
||||||
// `from os.path import join` -> `join`
|
// `from os.path import join` -> `join`
|
||||||
// `from os.path import join as join2` -> `join2`
|
// `from os.path import join as join2` -> `join2`
|
||||||
BindingKind::FromImport(FromImport { qualified_name }) => {
|
BindingKind::FromImport(FromImport { qualified_name, .. }) => {
|
||||||
if let Some((target_module, target_member)) = qualified_name.split_once('.')
|
if let Some((target_module, target_member)) = qualified_name.split_once('.')
|
||||||
{
|
{
|
||||||
if target_module == module && target_member == member {
|
if target_module == module && target_member == member {
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue