mirror of
https://github.com/astral-sh/ruff.git
synced 2025-08-04 02:38:25 +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,:
|
||||
import x
|
||||
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):
|
||||
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,:
|
||||
93 |- import x
|
||||
94 93 | import y
|
||||
95 94 |
|
||||
96 95 |
|
||||
|
||||
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,:
|
||||
93 93 | import x
|
||||
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) {
|
||||
match expr {
|
||||
Expr::Name(name) if name.ctx.is_load() => {
|
||||
let Some(Stmt::Assign(StmtAssign { value, .. })) =
|
||||
self.semantic.lookup_symbol(name.id.as_str())
|
||||
.and_then(|binding_id| {
|
||||
self.semantic
|
||||
.binding(binding_id)
|
||||
.source
|
||||
.map(|node_id| self.semantic.statement(node_id))
|
||||
}) else {
|
||||
return;
|
||||
};
|
||||
let Some(Stmt::Assign(StmtAssign { value, .. })) = self
|
||||
.semantic
|
||||
.lookup_symbol(name.id.as_str())
|
||||
.and_then(|binding_id| {
|
||||
self.semantic
|
||||
.binding(binding_id)
|
||||
.source
|
||||
.map(|node_id| self.semantic.statement(node_id))
|
||||
})
|
||||
else {
|
||||
return;
|
||||
};
|
||||
|
||||
match value.as_ref() {
|
||||
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.
|
||||
#[derive(Debug, Clone)]
|
||||
#[derive(Debug, Clone, is_macro::Is)]
|
||||
pub enum AnyImport<'a> {
|
||||
Import(&'a Import<'a>),
|
||||
SubmoduleImport(&'a SubmoduleImport<'a>),
|
||||
|
|
|
@ -590,14 +590,26 @@ impl<'a> SemanticModel<'a> {
|
|||
// print(pa.csv.read_csv("test.csv"))
|
||||
// ```
|
||||
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 segment = call_path.last()?;
|
||||
if *segment == symbol {
|
||||
return None;
|
||||
}
|
||||
|
||||
// Locate the submodule import (e.g., `pyarrow.csv`) that `pa` aliases.
|
||||
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;
|
||||
}
|
||||
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue