Fix false-positive in submodule resolution (#6435)

Closes https://github.com/astral-sh/ruff/issues/6433.
This commit is contained in:
Charlie Marsh 2023-08-08 22:36:39 -04:00 committed by GitHub
parent 1b9fed8397
commit a2758513de
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
6 changed files with 67 additions and 12 deletions

View file

@ -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"))

View file

@ -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

View file

@ -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"))

View file

@ -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 {

View file

@ -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>),

View file

@ -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;
}