mirror of
https://github.com/astral-sh/ruff.git
synced 2025-09-26 20:09:22 +00:00
Resolve classes and functions relative to script name (#10965)
## Summary If the user is analyzing a script (i.e., we have no module path), it seems reasonable to use the script name when trying to identify paths to objects defined _within_ the script. Closes https://github.com/astral-sh/ruff/issues/10960. ## Test Plan Ran: ```shell check --isolated --select=B008 \ --config 'lint.flake8-bugbear.extend-immutable-calls=["test.A"]' \ test.py ``` On: ```python class A: pass def f(a=A()): pass ```
This commit is contained in:
parent
1480d72643
commit
b23414e3cc
8 changed files with 127 additions and 74 deletions
|
@ -28,7 +28,7 @@ use crate::Imported;
|
|||
/// A semantic model for a Python module, to enable querying the module's semantic information.
|
||||
pub struct SemanticModel<'a> {
|
||||
typing_modules: &'a [String],
|
||||
module_path: Option<&'a [String]>,
|
||||
module: Module<'a>,
|
||||
|
||||
/// Stack of all AST nodes in the program.
|
||||
nodes: Nodes<'a>,
|
||||
|
@ -134,7 +134,7 @@ impl<'a> SemanticModel<'a> {
|
|||
pub fn new(typing_modules: &'a [String], path: &Path, module: Module<'a>) -> Self {
|
||||
Self {
|
||||
typing_modules,
|
||||
module_path: module.path(),
|
||||
module,
|
||||
nodes: Nodes::default(),
|
||||
node_id: None,
|
||||
branches: Branches::default(),
|
||||
|
@ -791,7 +791,11 @@ impl<'a> SemanticModel<'a> {
|
|||
.first()
|
||||
.map_or(false, |segment| *segment == ".")
|
||||
{
|
||||
from_relative_import(self.module_path?, qualified_name.segments(), tail)?
|
||||
from_relative_import(
|
||||
self.module.qualified_name()?,
|
||||
qualified_name.segments(),
|
||||
tail,
|
||||
)?
|
||||
} else {
|
||||
qualified_name
|
||||
.segments()
|
||||
|
@ -817,14 +821,32 @@ impl<'a> SemanticModel<'a> {
|
|||
}
|
||||
}
|
||||
BindingKind::ClassDefinition(_) | BindingKind::FunctionDefinition(_) => {
|
||||
let value_name = UnqualifiedName::from_expr(value)?;
|
||||
let resolved: QualifiedName = self
|
||||
.module_path?
|
||||
.iter()
|
||||
.map(String::as_str)
|
||||
.chain(value_name.segments().iter().copied())
|
||||
.collect();
|
||||
Some(resolved)
|
||||
// If we have a fully-qualified path for the module, use it.
|
||||
if let Some(path) = self.module.qualified_name() {
|
||||
Some(
|
||||
path.iter()
|
||||
.map(String::as_str)
|
||||
.chain(
|
||||
UnqualifiedName::from_expr(value)?
|
||||
.segments()
|
||||
.iter()
|
||||
.copied(),
|
||||
)
|
||||
.collect(),
|
||||
)
|
||||
} else {
|
||||
// Otherwise, if we're in (e.g.) a script, use the module name.
|
||||
Some(
|
||||
std::iter::once(self.module.name()?)
|
||||
.chain(
|
||||
UnqualifiedName::from_expr(value)?
|
||||
.segments()
|
||||
.iter()
|
||||
.copied(),
|
||||
)
|
||||
.collect(),
|
||||
)
|
||||
}
|
||||
}
|
||||
_ => None,
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue