mirror of
https://github.com/astral-sh/ruff.git
synced 2025-08-09 13:18:52 +00:00
Fix false-positive in submodule resolution (#6435)
Closes https://github.com/astral-sh/ruff/issues/6433.
This commit is contained in:
parent
1b9fed8397
commit
a2758513de
6 changed files with 67 additions and 12 deletions
|
@ -92,3 +92,10 @@ match *0, 1, *2:
|
||||||
case 0,:
|
case 0,:
|
||||||
import x
|
import x
|
||||||
import y
|
import y
|
||||||
|
|
||||||
|
|
||||||
|
# Test: access a sub-importation via an alias.
|
||||||
|
import foo.bar as bop
|
||||||
|
import foo.bar.baz
|
||||||
|
|
||||||
|
print(bop.baz.read_csv("test.csv"))
|
||||||
|
|
|
@ -70,3 +70,13 @@ import requests_mock as rm
|
||||||
|
|
||||||
def requests_mock(requests_mock: rm.Mocker):
|
def requests_mock(requests_mock: rm.Mocker):
|
||||||
print(rm.ANY)
|
print(rm.ANY)
|
||||||
|
|
||||||
|
|
||||||
|
import sklearn.base
|
||||||
|
import mlflow.sklearn
|
||||||
|
|
||||||
|
|
||||||
|
def f():
|
||||||
|
import sklearn
|
||||||
|
|
||||||
|
mlflow
|
||||||
|
|
|
@ -151,6 +151,8 @@ F401_0.py:93:16: F401 [*] `x` imported but unused
|
||||||
92 92 | case 0,:
|
92 92 | case 0,:
|
||||||
93 |- import x
|
93 |- import x
|
||||||
94 93 | import y
|
94 93 | import y
|
||||||
|
95 94 |
|
||||||
|
96 95 |
|
||||||
|
|
||||||
F401_0.py:94:16: F401 [*] `y` imported but unused
|
F401_0.py:94:16: F401 [*] `y` imported but unused
|
||||||
|
|
|
|
||||||
|
@ -166,5 +168,27 @@ F401_0.py:94:16: F401 [*] `y` imported but unused
|
||||||
92 92 | case 0,:
|
92 92 | case 0,:
|
||||||
93 93 | import x
|
93 93 | import x
|
||||||
94 |- import y
|
94 |- import y
|
||||||
|
95 94 |
|
||||||
|
96 95 |
|
||||||
|
97 96 | # Test: access a sub-importation via an alias.
|
||||||
|
|
||||||
|
F401_0.py:99:8: F401 [*] `foo.bar.baz` imported but unused
|
||||||
|
|
|
||||||
|
97 | # Test: access a sub-importation via an alias.
|
||||||
|
98 | import foo.bar as bop
|
||||||
|
99 | import foo.bar.baz
|
||||||
|
| ^^^^^^^^^^^ F401
|
||||||
|
100 |
|
||||||
|
101 | print(bop.baz.read_csv("test.csv"))
|
||||||
|
|
|
||||||
|
= help: Remove unused import: `foo.bar.baz`
|
||||||
|
|
||||||
|
ℹ Fix
|
||||||
|
96 96 |
|
||||||
|
97 97 | # Test: access a sub-importation via an alias.
|
||||||
|
98 98 | import foo.bar as bop
|
||||||
|
99 |-import foo.bar.baz
|
||||||
|
100 99 |
|
||||||
|
101 100 | print(bop.baz.read_csv("test.csv"))
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -130,16 +130,18 @@ impl<'a> Visitor<'a> for TypeVarReferenceVisitor<'a> {
|
||||||
fn visit_expr(&mut self, expr: &'a Expr) {
|
fn visit_expr(&mut self, expr: &'a Expr) {
|
||||||
match expr {
|
match expr {
|
||||||
Expr::Name(name) if name.ctx.is_load() => {
|
Expr::Name(name) if name.ctx.is_load() => {
|
||||||
let Some(Stmt::Assign(StmtAssign { value, .. })) =
|
let Some(Stmt::Assign(StmtAssign { value, .. })) = self
|
||||||
self.semantic.lookup_symbol(name.id.as_str())
|
.semantic
|
||||||
.and_then(|binding_id| {
|
.lookup_symbol(name.id.as_str())
|
||||||
self.semantic
|
.and_then(|binding_id| {
|
||||||
.binding(binding_id)
|
self.semantic
|
||||||
.source
|
.binding(binding_id)
|
||||||
.map(|node_id| self.semantic.statement(node_id))
|
.source
|
||||||
}) else {
|
.map(|node_id| self.semantic.statement(node_id))
|
||||||
return;
|
})
|
||||||
};
|
else {
|
||||||
|
return;
|
||||||
|
};
|
||||||
|
|
||||||
match value.as_ref() {
|
match value.as_ref() {
|
||||||
Expr::Subscript(ExprSubscript {
|
Expr::Subscript(ExprSubscript {
|
||||||
|
|
|
@ -585,7 +585,7 @@ impl<'a> Imported<'a> for FromImport<'a> {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// A wrapper around an import [`BindingKind`] that can be any of the three types of imports.
|
/// A wrapper around an import [`BindingKind`] that can be any of the three types of imports.
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone, is_macro::Is)]
|
||||||
pub enum AnyImport<'a> {
|
pub enum AnyImport<'a> {
|
||||||
Import(&'a Import<'a>),
|
Import(&'a Import<'a>),
|
||||||
SubmoduleImport(&'a SubmoduleImport<'a>),
|
SubmoduleImport(&'a SubmoduleImport<'a>),
|
||||||
|
|
|
@ -590,14 +590,26 @@ impl<'a> SemanticModel<'a> {
|
||||||
// print(pa.csv.read_csv("test.csv"))
|
// print(pa.csv.read_csv("test.csv"))
|
||||||
// ```
|
// ```
|
||||||
let import = self.bindings[binding_id].as_any_import()?;
|
let import = self.bindings[binding_id].as_any_import()?;
|
||||||
|
if !import.is_import() {
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Grab, e.g., `pyarrow` from `import pyarrow as pa`.
|
||||||
let call_path = import.call_path();
|
let call_path = import.call_path();
|
||||||
let segment = call_path.last()?;
|
let segment = call_path.last()?;
|
||||||
if *segment == symbol {
|
if *segment == symbol {
|
||||||
return None;
|
return None;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Locate the submodule import (e.g., `pyarrow.csv`) that `pa` aliases.
|
||||||
let binding_id = self.scopes[scope_id].get(segment)?;
|
let binding_id = self.scopes[scope_id].get(segment)?;
|
||||||
if !self.bindings[binding_id].kind.is_submodule_import() {
|
let submodule = &self.bindings[binding_id].as_any_import()?;
|
||||||
|
if !submodule.is_submodule_import() {
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Ensure that the submodule import and the aliased import are from the same module.
|
||||||
|
if import.module_name() != submodule.module_name() {
|
||||||
return None;
|
return None;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue