mirror of
https://github.com/astral-sh/ruff.git
synced 2025-10-02 22:55:08 +00:00
Move import-name matching into methods on BindingKind
(#4818)
This commit is contained in:
parent
2c41c54e0c
commit
935094c2ff
8 changed files with 96 additions and 134 deletions
|
@ -2,7 +2,7 @@ from __future__ import annotations
|
||||||
|
|
||||||
|
|
||||||
def f():
|
def f():
|
||||||
# Even in strict mode, this shouldn't rase an error, since `pkg` is used at runtime,
|
# Even in strict mode, this shouldn't raise an error, since `pkg` is used at runtime,
|
||||||
# and implicitly imports `pkg.bar`.
|
# and implicitly imports `pkg.bar`.
|
||||||
import pkg
|
import pkg
|
||||||
import pkg.bar
|
import pkg.bar
|
||||||
|
@ -12,7 +12,7 @@ def f():
|
||||||
|
|
||||||
|
|
||||||
def f():
|
def f():
|
||||||
# Even in strict mode, this shouldn't rase an error, since `pkg.bar` is used at
|
# Even in strict mode, this shouldn't raise an error, since `pkg.bar` is used at
|
||||||
# runtime, and implicitly imports `pkg`.
|
# runtime, and implicitly imports `pkg`.
|
||||||
import pkg
|
import pkg
|
||||||
import pkg.bar
|
import pkg.bar
|
||||||
|
@ -22,7 +22,7 @@ def f():
|
||||||
|
|
||||||
|
|
||||||
def f():
|
def f():
|
||||||
# In un-strict mode, this shouldn't rase an error, since `pkg` is used at runtime.
|
# In un-strict mode, this shouldn't raise an error, since `pkg` is used at runtime.
|
||||||
import pkg
|
import pkg
|
||||||
from pkg import A
|
from pkg import A
|
||||||
|
|
||||||
|
@ -31,7 +31,7 @@ def f():
|
||||||
|
|
||||||
|
|
||||||
def f():
|
def f():
|
||||||
# In un-strict mode, this shouldn't rase an error, since `pkg` is used at runtime.
|
# In un-strict mode, this shouldn't raise an error, since `pkg` is used at runtime.
|
||||||
from pkg import A, B
|
from pkg import A, B
|
||||||
|
|
||||||
def test(value: A):
|
def test(value: A):
|
||||||
|
@ -39,7 +39,7 @@ def f():
|
||||||
|
|
||||||
|
|
||||||
def f():
|
def f():
|
||||||
# Even in strict mode, this shouldn't rase an error, since `pkg.baz` is used at
|
# Even in strict mode, this shouldn't raise an error, since `pkg.baz` is used at
|
||||||
# runtime, and implicitly imports `pkg.bar`.
|
# runtime, and implicitly imports `pkg.bar`.
|
||||||
import pkg.bar
|
import pkg.bar
|
||||||
import pkg.baz
|
import pkg.baz
|
||||||
|
@ -49,7 +49,7 @@ def f():
|
||||||
|
|
||||||
|
|
||||||
def f():
|
def f():
|
||||||
# In un-strict mode, this _should_ rase an error, since `pkg` is used at runtime.
|
# In un-strict mode, this _should_ raise an error, since `pkg.bar` isn't used at runtime
|
||||||
import pkg
|
import pkg
|
||||||
from pkg.bar import A
|
from pkg.bar import A
|
||||||
|
|
||||||
|
@ -58,7 +58,7 @@ def f():
|
||||||
|
|
||||||
|
|
||||||
def f():
|
def f():
|
||||||
# In un-strict mode, this shouldn't rase an error, since `pkg.bar` is used at runtime.
|
# In un-strict mode, this shouldn't raise an error, since `pkg.bar` is used at runtime.
|
||||||
import pkg
|
import pkg
|
||||||
import pkg.bar as B
|
import pkg.bar as B
|
||||||
|
|
||||||
|
@ -67,7 +67,7 @@ def f():
|
||||||
|
|
||||||
|
|
||||||
def f():
|
def f():
|
||||||
# In un-strict mode, this shouldn't rase an error, since `pkg.foo.bar` is used at runtime.
|
# In un-strict mode, this shouldn't raise an error, since `pkg.foo.bar` is used at runtime.
|
||||||
import pkg.foo as F
|
import pkg.foo as F
|
||||||
import pkg.foo.bar as B
|
import pkg.foo.bar as B
|
||||||
|
|
||||||
|
@ -76,7 +76,7 @@ def f():
|
||||||
|
|
||||||
|
|
||||||
def f():
|
def f():
|
||||||
# In un-strict mode, this shouldn't rase an error, since `pkg.foo.bar` is used at runtime.
|
# In un-strict mode, this shouldn't raise an error, since `pkg.foo.bar` is used at runtime.
|
||||||
import pkg
|
import pkg
|
||||||
import pkg.foo.bar as B
|
import pkg.foo.bar as B
|
||||||
|
|
||||||
|
@ -85,7 +85,7 @@ def f():
|
||||||
|
|
||||||
|
|
||||||
def f():
|
def f():
|
||||||
# In un-strict mode, this _should_ rase an error, since `pkgfoo.bar` is used at runtime.
|
# In un-strict mode, this _should_ raise an error, since `pkg` isn't used at runtime.
|
||||||
# Note that `pkg` is a prefix of `pkgfoo` which are both different modules. This is
|
# Note that `pkg` is a prefix of `pkgfoo` which are both different modules. This is
|
||||||
# testing the implementation.
|
# testing the implementation.
|
||||||
import pkg
|
import pkg
|
||||||
|
@ -96,7 +96,7 @@ def f():
|
||||||
|
|
||||||
|
|
||||||
def f():
|
def f():
|
||||||
# In un-strict mode, this shouldn't raise an error, since `pkg.bar` is used at runtime.
|
# In un-strict mode, this shouldn't raise an error, since `pkg` is used at runtime.
|
||||||
import pkg.bar as B
|
import pkg.bar as B
|
||||||
import pkg.foo as F
|
import pkg.foo as F
|
||||||
|
|
||||||
|
|
|
@ -1,8 +1,6 @@
|
||||||
use ruff_diagnostics::{AutofixKind, Diagnostic, Fix, Violation};
|
use ruff_diagnostics::{AutofixKind, Diagnostic, Fix, Violation};
|
||||||
use ruff_macros::{derive_message_formats, violation};
|
use ruff_macros::{derive_message_formats, violation};
|
||||||
use ruff_python_semantic::binding::{
|
use ruff_python_semantic::binding::Binding;
|
||||||
Binding, BindingKind, FromImportation, Importation, SubmoduleImportation,
|
|
||||||
};
|
|
||||||
|
|
||||||
use crate::autofix;
|
use crate::autofix;
|
||||||
use crate::checkers::ast::Checker;
|
use crate::checkers::ast::Checker;
|
||||||
|
@ -66,11 +64,8 @@ pub(crate) fn runtime_import_in_type_checking_block(
|
||||||
binding: &Binding,
|
binding: &Binding,
|
||||||
diagnostics: &mut Vec<Diagnostic>,
|
diagnostics: &mut Vec<Diagnostic>,
|
||||||
) {
|
) {
|
||||||
let full_name = match &binding.kind {
|
let Some(qualified_name) = binding.qualified_name() else {
|
||||||
BindingKind::Importation(Importation { full_name }) => full_name,
|
return;
|
||||||
BindingKind::FromImportation(FromImportation { full_name }) => full_name.as_str(),
|
|
||||||
BindingKind::SubmoduleImportation(SubmoduleImportation { full_name }) => full_name,
|
|
||||||
_ => return,
|
|
||||||
};
|
};
|
||||||
|
|
||||||
let Some(reference_id) = binding.references.first() else {
|
let Some(reference_id) = binding.references.first() else {
|
||||||
|
@ -89,7 +84,7 @@ pub(crate) fn runtime_import_in_type_checking_block(
|
||||||
{
|
{
|
||||||
let mut diagnostic = Diagnostic::new(
|
let mut diagnostic = Diagnostic::new(
|
||||||
RuntimeImportInTypeCheckingBlock {
|
RuntimeImportInTypeCheckingBlock {
|
||||||
full_name: full_name.to_string(),
|
full_name: qualified_name.to_string(),
|
||||||
},
|
},
|
||||||
binding.range,
|
binding.range,
|
||||||
);
|
);
|
||||||
|
@ -102,7 +97,7 @@ pub(crate) fn runtime_import_in_type_checking_block(
|
||||||
let stmt = checker.semantic_model().stmts[source];
|
let stmt = checker.semantic_model().stmts[source];
|
||||||
let parent = checker.semantic_model().stmts.parent(stmt);
|
let parent = checker.semantic_model().stmts.parent(stmt);
|
||||||
let remove_import_edit = autofix::edits::remove_unused_imports(
|
let remove_import_edit = autofix::edits::remove_unused_imports(
|
||||||
std::iter::once(full_name),
|
std::iter::once(qualified_name),
|
||||||
stmt,
|
stmt,
|
||||||
parent,
|
parent,
|
||||||
checker.locator,
|
checker.locator,
|
||||||
|
@ -113,7 +108,10 @@ pub(crate) fn runtime_import_in_type_checking_block(
|
||||||
// Step 2) Add the import to the top-level.
|
// Step 2) Add the import to the top-level.
|
||||||
let reference = checker.semantic_model().references.resolve(*reference_id);
|
let reference = checker.semantic_model().references.resolve(*reference_id);
|
||||||
let add_import_edit = checker.importer.runtime_import_edit(
|
let add_import_edit = checker.importer.runtime_import_edit(
|
||||||
&StmtImport { stmt, full_name },
|
&StmtImport {
|
||||||
|
stmt,
|
||||||
|
full_name: qualified_name,
|
||||||
|
},
|
||||||
reference.range().start(),
|
reference.range().start(),
|
||||||
)?;
|
)?;
|
||||||
|
|
||||||
|
|
|
@ -1,8 +1,6 @@
|
||||||
use ruff_diagnostics::{AutofixKind, Diagnostic, Fix, Violation};
|
use ruff_diagnostics::{AutofixKind, Diagnostic, Fix, Violation};
|
||||||
use ruff_macros::{derive_message_formats, violation};
|
use ruff_macros::{derive_message_formats, violation};
|
||||||
use ruff_python_semantic::binding::{
|
use ruff_python_semantic::binding::Binding;
|
||||||
Binding, BindingKind, FromImportation, Importation, SubmoduleImportation,
|
|
||||||
};
|
|
||||||
|
|
||||||
use crate::autofix;
|
use crate::autofix;
|
||||||
use crate::checkers::ast::Checker;
|
use crate::checkers::ast::Checker;
|
||||||
|
@ -180,65 +178,13 @@ impl Violation for TypingOnlyStandardLibraryImport {
|
||||||
|
|
||||||
/// Return `true` if `this` is implicitly loaded via importing `that`.
|
/// Return `true` if `this` is implicitly loaded via importing `that`.
|
||||||
fn is_implicit_import(this: &Binding, that: &Binding) -> bool {
|
fn is_implicit_import(this: &Binding, that: &Binding) -> bool {
|
||||||
match &this.kind {
|
let Some(this_module) = this.module_name() else {
|
||||||
BindingKind::Importation(Importation {
|
return false;
|
||||||
full_name: this_name,
|
};
|
||||||
})
|
let Some(that_module) = that.module_name() else {
|
||||||
| BindingKind::SubmoduleImportation(SubmoduleImportation {
|
return false;
|
||||||
full_name: this_name,
|
};
|
||||||
}) => match &that.kind {
|
this_module == that_module
|
||||||
BindingKind::FromImportation(FromImportation {
|
|
||||||
full_name: that_name,
|
|
||||||
}) => {
|
|
||||||
// Ex) `pkg.A` vs. `pkg`
|
|
||||||
let this_name = this_name.split('.').next().unwrap_or(this_name);
|
|
||||||
that_name
|
|
||||||
.rfind('.')
|
|
||||||
.map_or(false, |i| that_name[..i] == *this_name)
|
|
||||||
}
|
|
||||||
BindingKind::Importation(Importation {
|
|
||||||
full_name: that_name,
|
|
||||||
})
|
|
||||||
| BindingKind::SubmoduleImportation(SubmoduleImportation {
|
|
||||||
full_name: that_name,
|
|
||||||
}) => {
|
|
||||||
// Submodule importation with an alias (`import pkg.A as B`)
|
|
||||||
// are represented as `Importation`.
|
|
||||||
let this_name = this_name.split('.').next().unwrap_or(this_name);
|
|
||||||
let that_name = that_name.split('.').next().unwrap_or(that_name);
|
|
||||||
this_name == that_name
|
|
||||||
}
|
|
||||||
_ => false,
|
|
||||||
},
|
|
||||||
BindingKind::FromImportation(FromImportation {
|
|
||||||
full_name: this_name,
|
|
||||||
}) => match &that.kind {
|
|
||||||
BindingKind::Importation(Importation {
|
|
||||||
full_name: that_name,
|
|
||||||
})
|
|
||||||
| BindingKind::SubmoduleImportation(SubmoduleImportation {
|
|
||||||
full_name: that_name,
|
|
||||||
}) => {
|
|
||||||
// Ex) `pkg.A` vs. `pkg`
|
|
||||||
let that_name = that_name.split('.').next().unwrap_or(that_name);
|
|
||||||
this_name
|
|
||||||
.rfind('.')
|
|
||||||
.map_or(false, |i| &this_name[..i] == that_name)
|
|
||||||
}
|
|
||||||
BindingKind::FromImportation(FromImportation {
|
|
||||||
full_name: that_name,
|
|
||||||
}) => {
|
|
||||||
// Ex) `pkg.A` vs. `pkg.B`
|
|
||||||
this_name.rfind('.').map_or(false, |i| {
|
|
||||||
that_name
|
|
||||||
.rfind('.')
|
|
||||||
.map_or(false, |j| this_name[..i] == that_name[..j])
|
|
||||||
})
|
|
||||||
}
|
|
||||||
_ => false,
|
|
||||||
},
|
|
||||||
_ => false,
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Return `true` if `name` is exempt from typing-only enforcement.
|
/// Return `true` if `name` is exempt from typing-only enforcement.
|
||||||
|
@ -274,15 +220,12 @@ pub(crate) fn typing_only_runtime_import(
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
let full_name = match &binding.kind {
|
let Some(qualified_name) = binding.qualified_name() else {
|
||||||
BindingKind::Importation(Importation { full_name }) => full_name,
|
return;
|
||||||
BindingKind::FromImportation(FromImportation { full_name }) => full_name.as_str(),
|
|
||||||
BindingKind::SubmoduleImportation(SubmoduleImportation { full_name }) => full_name,
|
|
||||||
_ => return,
|
|
||||||
};
|
};
|
||||||
|
|
||||||
if is_exempt(
|
if is_exempt(
|
||||||
full_name,
|
qualified_name,
|
||||||
&checker
|
&checker
|
||||||
.settings
|
.settings
|
||||||
.flake8_type_checking
|
.flake8_type_checking
|
||||||
|
@ -312,7 +255,7 @@ pub(crate) fn typing_only_runtime_import(
|
||||||
// Extract the module base and level from the full name.
|
// Extract the module base and level from the full name.
|
||||||
// Ex) `foo.bar.baz` -> `foo`, `0`
|
// Ex) `foo.bar.baz` -> `foo`, `0`
|
||||||
// Ex) `.foo.bar.baz` -> `foo`, `1`
|
// Ex) `.foo.bar.baz` -> `foo`, `1`
|
||||||
let level = full_name
|
let level = qualified_name
|
||||||
.chars()
|
.chars()
|
||||||
.take_while(|c| *c == '.')
|
.take_while(|c| *c == '.')
|
||||||
.count()
|
.count()
|
||||||
|
@ -321,7 +264,7 @@ pub(crate) fn typing_only_runtime_import(
|
||||||
|
|
||||||
// Categorize the import.
|
// Categorize the import.
|
||||||
let mut diagnostic = match categorize(
|
let mut diagnostic = match categorize(
|
||||||
full_name,
|
qualified_name,
|
||||||
Some(level),
|
Some(level),
|
||||||
&checker.settings.src,
|
&checker.settings.src,
|
||||||
checker.package(),
|
checker.package(),
|
||||||
|
@ -331,7 +274,7 @@ pub(crate) fn typing_only_runtime_import(
|
||||||
ImportSection::Known(ImportType::LocalFolder | ImportType::FirstParty) => {
|
ImportSection::Known(ImportType::LocalFolder | ImportType::FirstParty) => {
|
||||||
Diagnostic::new(
|
Diagnostic::new(
|
||||||
TypingOnlyFirstPartyImport {
|
TypingOnlyFirstPartyImport {
|
||||||
full_name: full_name.to_string(),
|
full_name: qualified_name.to_string(),
|
||||||
},
|
},
|
||||||
binding.range,
|
binding.range,
|
||||||
)
|
)
|
||||||
|
@ -339,14 +282,14 @@ pub(crate) fn typing_only_runtime_import(
|
||||||
ImportSection::Known(ImportType::ThirdParty) | ImportSection::UserDefined(_) => {
|
ImportSection::Known(ImportType::ThirdParty) | ImportSection::UserDefined(_) => {
|
||||||
Diagnostic::new(
|
Diagnostic::new(
|
||||||
TypingOnlyThirdPartyImport {
|
TypingOnlyThirdPartyImport {
|
||||||
full_name: full_name.to_string(),
|
full_name: qualified_name.to_string(),
|
||||||
},
|
},
|
||||||
binding.range,
|
binding.range,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
ImportSection::Known(ImportType::StandardLibrary) => Diagnostic::new(
|
ImportSection::Known(ImportType::StandardLibrary) => Diagnostic::new(
|
||||||
TypingOnlyStandardLibraryImport {
|
TypingOnlyStandardLibraryImport {
|
||||||
full_name: full_name.to_string(),
|
full_name: qualified_name.to_string(),
|
||||||
},
|
},
|
||||||
binding.range,
|
binding.range,
|
||||||
),
|
),
|
||||||
|
@ -363,7 +306,7 @@ pub(crate) fn typing_only_runtime_import(
|
||||||
let stmt = checker.semantic_model().stmts[source];
|
let stmt = checker.semantic_model().stmts[source];
|
||||||
let parent = checker.semantic_model().stmts.parent(stmt);
|
let parent = checker.semantic_model().stmts.parent(stmt);
|
||||||
let remove_import_edit = autofix::edits::remove_unused_imports(
|
let remove_import_edit = autofix::edits::remove_unused_imports(
|
||||||
std::iter::once(full_name),
|
std::iter::once(qualified_name),
|
||||||
stmt,
|
stmt,
|
||||||
parent,
|
parent,
|
||||||
checker.locator,
|
checker.locator,
|
||||||
|
@ -374,7 +317,10 @@ pub(crate) fn typing_only_runtime_import(
|
||||||
// Step 2) Add the import to a `TYPE_CHECKING` block.
|
// Step 2) Add the import to a `TYPE_CHECKING` block.
|
||||||
let reference = checker.semantic_model().references.resolve(*reference_id);
|
let reference = checker.semantic_model().references.resolve(*reference_id);
|
||||||
let add_import_edit = checker.importer.typing_import_edit(
|
let add_import_edit = checker.importer.typing_import_edit(
|
||||||
&StmtImport { stmt, full_name },
|
&StmtImport {
|
||||||
|
stmt,
|
||||||
|
full_name: qualified_name,
|
||||||
|
},
|
||||||
reference.range().start(),
|
reference.range().start(),
|
||||||
checker.semantic_model(),
|
checker.semantic_model(),
|
||||||
)?;
|
)?;
|
||||||
|
|
|
@ -3,7 +3,7 @@ source: crates/ruff/src/rules/flake8_type_checking/mod.rs
|
||||||
---
|
---
|
||||||
strict.py:27:21: TCH002 [*] Move third-party import `pkg.A` into a type-checking block
|
strict.py:27:21: TCH002 [*] Move third-party import `pkg.A` into a type-checking block
|
||||||
|
|
|
|
||||||
27 | # In un-strict mode, this shouldn't rase an error, since `pkg` is used at runtime.
|
27 | # In un-strict mode, this shouldn't raise an error, since `pkg` is used at runtime.
|
||||||
28 | import pkg
|
28 | import pkg
|
||||||
29 | from pkg import A
|
29 | from pkg import A
|
||||||
| ^ TCH002
|
| ^ TCH002
|
||||||
|
@ -23,7 +23,7 @@ strict.py:27:21: TCH002 [*] Move third-party import `pkg.A` into a type-checking
|
||||||
4 8 | def f():
|
4 8 | def f():
|
||||||
--------------------------------------------------------------------------------
|
--------------------------------------------------------------------------------
|
||||||
24 28 | def f():
|
24 28 | def f():
|
||||||
25 29 | # In un-strict mode, this shouldn't rase an error, since `pkg` is used at runtime.
|
25 29 | # In un-strict mode, this shouldn't raise an error, since `pkg` is used at runtime.
|
||||||
26 30 | import pkg
|
26 30 | import pkg
|
||||||
27 |- from pkg import A
|
27 |- from pkg import A
|
||||||
28 31 |
|
28 31 |
|
||||||
|
@ -33,7 +33,7 @@ strict.py:27:21: TCH002 [*] Move third-party import `pkg.A` into a type-checking
|
||||||
strict.py:35:21: TCH002 [*] Move third-party import `pkg.A` into a type-checking block
|
strict.py:35:21: TCH002 [*] Move third-party import `pkg.A` into a type-checking block
|
||||||
|
|
|
|
||||||
35 | def f():
|
35 | def f():
|
||||||
36 | # In un-strict mode, this shouldn't rase an error, since `pkg` is used at runtime.
|
36 | # In un-strict mode, this shouldn't raise an error, since `pkg` is used at runtime.
|
||||||
37 | from pkg import A, B
|
37 | from pkg import A, B
|
||||||
| ^ TCH002
|
| ^ TCH002
|
||||||
38 |
|
38 |
|
||||||
|
@ -53,7 +53,7 @@ strict.py:35:21: TCH002 [*] Move third-party import `pkg.A` into a type-checking
|
||||||
--------------------------------------------------------------------------------
|
--------------------------------------------------------------------------------
|
||||||
32 36 |
|
32 36 |
|
||||||
33 37 | def f():
|
33 37 | def f():
|
||||||
34 38 | # In un-strict mode, this shouldn't rase an error, since `pkg` is used at runtime.
|
34 38 | # In un-strict mode, this shouldn't raise an error, since `pkg` is used at runtime.
|
||||||
35 |- from pkg import A, B
|
35 |- from pkg import A, B
|
||||||
39 |+ from pkg import B
|
39 |+ from pkg import B
|
||||||
36 40 |
|
36 40 |
|
||||||
|
@ -62,7 +62,7 @@ strict.py:35:21: TCH002 [*] Move third-party import `pkg.A` into a type-checking
|
||||||
|
|
||||||
strict.py:54:25: TCH002 [*] Move third-party import `pkg.bar.A` into a type-checking block
|
strict.py:54:25: TCH002 [*] Move third-party import `pkg.bar.A` into a type-checking block
|
||||||
|
|
|
|
||||||
54 | # In un-strict mode, this _should_ rase an error, since `pkg` is used at runtime.
|
54 | # In un-strict mode, this _should_ raise an error, since `pkg.bar` isn't used at runtime
|
||||||
55 | import pkg
|
55 | import pkg
|
||||||
56 | from pkg.bar import A
|
56 | from pkg.bar import A
|
||||||
| ^ TCH002
|
| ^ TCH002
|
||||||
|
@ -82,7 +82,7 @@ strict.py:54:25: TCH002 [*] Move third-party import `pkg.bar.A` into a type-chec
|
||||||
4 8 | def f():
|
4 8 | def f():
|
||||||
--------------------------------------------------------------------------------
|
--------------------------------------------------------------------------------
|
||||||
51 55 | def f():
|
51 55 | def f():
|
||||||
52 56 | # In un-strict mode, this _should_ rase an error, since `pkg` is used at runtime.
|
52 56 | # In un-strict mode, this _should_ raise an error, since `pkg.bar` isn't used at runtime
|
||||||
53 57 | import pkg
|
53 57 | import pkg
|
||||||
54 |- from pkg.bar import A
|
54 |- from pkg.bar import A
|
||||||
55 58 |
|
55 58 |
|
||||||
|
@ -92,7 +92,7 @@ strict.py:54:25: TCH002 [*] Move third-party import `pkg.bar.A` into a type-chec
|
||||||
strict.py:62:12: TCH002 [*] Move third-party import `pkg` into a type-checking block
|
strict.py:62:12: TCH002 [*] Move third-party import `pkg` into a type-checking block
|
||||||
|
|
|
|
||||||
62 | def f():
|
62 | def f():
|
||||||
63 | # In un-strict mode, this shouldn't rase an error, since `pkg.bar` is used at runtime.
|
63 | # In un-strict mode, this shouldn't raise an error, since `pkg.bar` is used at runtime.
|
||||||
64 | import pkg
|
64 | import pkg
|
||||||
| ^^^ TCH002
|
| ^^^ TCH002
|
||||||
65 | import pkg.bar as B
|
65 | import pkg.bar as B
|
||||||
|
@ -111,7 +111,7 @@ strict.py:62:12: TCH002 [*] Move third-party import `pkg` into a type-checking b
|
||||||
--------------------------------------------------------------------------------
|
--------------------------------------------------------------------------------
|
||||||
59 63 |
|
59 63 |
|
||||||
60 64 | def f():
|
60 64 | def f():
|
||||||
61 65 | # In un-strict mode, this shouldn't rase an error, since `pkg.bar` is used at runtime.
|
61 65 | # In un-strict mode, this shouldn't raise an error, since `pkg.bar` is used at runtime.
|
||||||
62 |- import pkg
|
62 |- import pkg
|
||||||
63 66 | import pkg.bar as B
|
63 66 | import pkg.bar as B
|
||||||
64 67 |
|
64 67 |
|
||||||
|
@ -120,7 +120,7 @@ strict.py:62:12: TCH002 [*] Move third-party import `pkg` into a type-checking b
|
||||||
strict.py:71:12: TCH002 [*] Move third-party import `pkg.foo` into a type-checking block
|
strict.py:71:12: TCH002 [*] Move third-party import `pkg.foo` into a type-checking block
|
||||||
|
|
|
|
||||||
71 | def f():
|
71 | def f():
|
||||||
72 | # In un-strict mode, this shouldn't rase an error, since `pkg.foo.bar` is used at runtime.
|
72 | # In un-strict mode, this shouldn't raise an error, since `pkg.foo.bar` is used at runtime.
|
||||||
73 | import pkg.foo as F
|
73 | import pkg.foo as F
|
||||||
| ^^^^^^^^^^^^ TCH002
|
| ^^^^^^^^^^^^ TCH002
|
||||||
74 | import pkg.foo.bar as B
|
74 | import pkg.foo.bar as B
|
||||||
|
@ -139,7 +139,7 @@ strict.py:71:12: TCH002 [*] Move third-party import `pkg.foo` into a type-checki
|
||||||
--------------------------------------------------------------------------------
|
--------------------------------------------------------------------------------
|
||||||
68 72 |
|
68 72 |
|
||||||
69 73 | def f():
|
69 73 | def f():
|
||||||
70 74 | # In un-strict mode, this shouldn't rase an error, since `pkg.foo.bar` is used at runtime.
|
70 74 | # In un-strict mode, this shouldn't raise an error, since `pkg.foo.bar` is used at runtime.
|
||||||
71 |- import pkg.foo as F
|
71 |- import pkg.foo as F
|
||||||
72 75 | import pkg.foo.bar as B
|
72 75 | import pkg.foo.bar as B
|
||||||
73 76 |
|
73 76 |
|
||||||
|
@ -148,7 +148,7 @@ strict.py:71:12: TCH002 [*] Move third-party import `pkg.foo` into a type-checki
|
||||||
strict.py:80:12: TCH002 [*] Move third-party import `pkg` into a type-checking block
|
strict.py:80:12: TCH002 [*] Move third-party import `pkg` into a type-checking block
|
||||||
|
|
|
|
||||||
80 | def f():
|
80 | def f():
|
||||||
81 | # In un-strict mode, this shouldn't rase an error, since `pkg.foo.bar` is used at runtime.
|
81 | # In un-strict mode, this shouldn't raise an error, since `pkg.foo.bar` is used at runtime.
|
||||||
82 | import pkg
|
82 | import pkg
|
||||||
| ^^^ TCH002
|
| ^^^ TCH002
|
||||||
83 | import pkg.foo.bar as B
|
83 | import pkg.foo.bar as B
|
||||||
|
@ -167,7 +167,7 @@ strict.py:80:12: TCH002 [*] Move third-party import `pkg` into a type-checking b
|
||||||
--------------------------------------------------------------------------------
|
--------------------------------------------------------------------------------
|
||||||
77 81 |
|
77 81 |
|
||||||
78 82 | def f():
|
78 82 | def f():
|
||||||
79 83 | # In un-strict mode, this shouldn't rase an error, since `pkg.foo.bar` is used at runtime.
|
79 83 | # In un-strict mode, this shouldn't raise an error, since `pkg.foo.bar` is used at runtime.
|
||||||
80 |- import pkg
|
80 |- import pkg
|
||||||
81 84 | import pkg.foo.bar as B
|
81 84 | import pkg.foo.bar as B
|
||||||
82 85 |
|
82 85 |
|
||||||
|
@ -193,7 +193,7 @@ strict.py:91:12: TCH002 [*] Move third-party import `pkg` into a type-checking b
|
||||||
3 7 |
|
3 7 |
|
||||||
4 8 | def f():
|
4 8 | def f():
|
||||||
--------------------------------------------------------------------------------
|
--------------------------------------------------------------------------------
|
||||||
88 92 | # In un-strict mode, this _should_ rase an error, since `pkgfoo.bar` is used at runtime.
|
88 92 | # In un-strict mode, this _should_ raise an error, since `pkg` isn't used at runtime.
|
||||||
89 93 | # Note that `pkg` is a prefix of `pkgfoo` which are both different modules. This is
|
89 93 | # Note that `pkg` is a prefix of `pkgfoo` which are both different modules. This is
|
||||||
90 94 | # testing the implementation.
|
90 94 | # testing the implementation.
|
||||||
91 |- import pkg
|
91 |- import pkg
|
||||||
|
@ -203,7 +203,7 @@ strict.py:91:12: TCH002 [*] Move third-party import `pkg` into a type-checking b
|
||||||
|
|
||||||
strict.py:101:12: TCH002 [*] Move third-party import `pkg.foo` into a type-checking block
|
strict.py:101:12: TCH002 [*] Move third-party import `pkg.foo` into a type-checking block
|
||||||
|
|
|
|
||||||
101 | # In un-strict mode, this shouldn't raise an error, since `pkg.bar` is used at runtime.
|
101 | # In un-strict mode, this shouldn't raise an error, since `pkg` is used at runtime.
|
||||||
102 | import pkg.bar as B
|
102 | import pkg.bar as B
|
||||||
103 | import pkg.foo as F
|
103 | import pkg.foo as F
|
||||||
| ^^^^^^^^^^^^ TCH002
|
| ^^^^^^^^^^^^ TCH002
|
||||||
|
@ -223,7 +223,7 @@ strict.py:101:12: TCH002 [*] Move third-party import `pkg.foo` into a type-check
|
||||||
4 8 | def f():
|
4 8 | def f():
|
||||||
--------------------------------------------------------------------------------
|
--------------------------------------------------------------------------------
|
||||||
98 102 | def f():
|
98 102 | def f():
|
||||||
99 103 | # In un-strict mode, this shouldn't raise an error, since `pkg.bar` is used at runtime.
|
99 103 | # In un-strict mode, this shouldn't raise an error, since `pkg` is used at runtime.
|
||||||
100 104 | import pkg.bar as B
|
100 104 | import pkg.bar as B
|
||||||
101 |- import pkg.foo as F
|
101 |- import pkg.foo as F
|
||||||
102 105 |
|
102 105 |
|
||||||
|
|
|
@ -3,7 +3,7 @@ source: crates/ruff/src/rules/flake8_type_checking/mod.rs
|
||||||
---
|
---
|
||||||
strict.py:54:25: TCH002 [*] Move third-party import `pkg.bar.A` into a type-checking block
|
strict.py:54:25: TCH002 [*] Move third-party import `pkg.bar.A` into a type-checking block
|
||||||
|
|
|
|
||||||
54 | # In un-strict mode, this _should_ rase an error, since `pkg` is used at runtime.
|
54 | # In un-strict mode, this _should_ raise an error, since `pkg.bar` isn't used at runtime
|
||||||
55 | import pkg
|
55 | import pkg
|
||||||
56 | from pkg.bar import A
|
56 | from pkg.bar import A
|
||||||
| ^ TCH002
|
| ^ TCH002
|
||||||
|
@ -23,7 +23,7 @@ strict.py:54:25: TCH002 [*] Move third-party import `pkg.bar.A` into a type-chec
|
||||||
4 8 | def f():
|
4 8 | def f():
|
||||||
--------------------------------------------------------------------------------
|
--------------------------------------------------------------------------------
|
||||||
51 55 | def f():
|
51 55 | def f():
|
||||||
52 56 | # In un-strict mode, this _should_ rase an error, since `pkg` is used at runtime.
|
52 56 | # In un-strict mode, this _should_ raise an error, since `pkg.bar` isn't used at runtime
|
||||||
53 57 | import pkg
|
53 57 | import pkg
|
||||||
54 |- from pkg.bar import A
|
54 |- from pkg.bar import A
|
||||||
55 58 |
|
55 58 |
|
||||||
|
@ -50,7 +50,7 @@ strict.py:91:12: TCH002 [*] Move third-party import `pkg` into a type-checking b
|
||||||
3 7 |
|
3 7 |
|
||||||
4 8 | def f():
|
4 8 | def f():
|
||||||
--------------------------------------------------------------------------------
|
--------------------------------------------------------------------------------
|
||||||
88 92 | # In un-strict mode, this _should_ rase an error, since `pkgfoo.bar` is used at runtime.
|
88 92 | # In un-strict mode, this _should_ raise an error, since `pkg` isn't used at runtime.
|
||||||
89 93 | # Note that `pkg` is a prefix of `pkgfoo` which are both different modules. This is
|
89 93 | # Note that `pkg` is a prefix of `pkgfoo` which are both different modules. This is
|
||||||
90 94 | # testing the implementation.
|
90 94 | # testing the implementation.
|
||||||
91 |- import pkg
|
91 |- import pkg
|
||||||
|
|
|
@ -5,9 +5,7 @@ use rustpython_parser::ast::Ranged;
|
||||||
|
|
||||||
use ruff_diagnostics::{AutofixKind, Diagnostic, Fix, IsolationLevel, Violation};
|
use ruff_diagnostics::{AutofixKind, Diagnostic, Fix, IsolationLevel, Violation};
|
||||||
use ruff_macros::{derive_message_formats, violation};
|
use ruff_macros::{derive_message_formats, violation};
|
||||||
use ruff_python_semantic::binding::{
|
use ruff_python_semantic::binding::Exceptions;
|
||||||
BindingKind, Exceptions, FromImportation, Importation, SubmoduleImportation,
|
|
||||||
};
|
|
||||||
use ruff_python_semantic::node::NodeId;
|
use ruff_python_semantic::node::NodeId;
|
||||||
use ruff_python_semantic::scope::Scope;
|
use ruff_python_semantic::scope::Scope;
|
||||||
|
|
||||||
|
@ -117,11 +115,8 @@ pub(crate) fn unused_import(checker: &Checker, scope: &Scope, diagnostics: &mut
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
let full_name = match &binding.kind {
|
let Some(qualified_name) = binding.qualified_name() else {
|
||||||
BindingKind::Importation(Importation { full_name }) => full_name,
|
continue;
|
||||||
BindingKind::FromImportation(FromImportation { full_name }) => full_name.as_str(),
|
|
||||||
BindingKind::SubmoduleImportation(SubmoduleImportation { full_name }) => full_name,
|
|
||||||
_ => continue,
|
|
||||||
};
|
};
|
||||||
|
|
||||||
let stmt_id = binding.source.unwrap();
|
let stmt_id = binding.source.unwrap();
|
||||||
|
@ -144,12 +139,12 @@ pub(crate) fn unused_import(checker: &Checker, scope: &Scope, diagnostics: &mut
|
||||||
ignored
|
ignored
|
||||||
.entry((stmt_id, parent_id, exceptions))
|
.entry((stmt_id, parent_id, exceptions))
|
||||||
.or_default()
|
.or_default()
|
||||||
.push((full_name, &binding.range));
|
.push((qualified_name, &binding.range));
|
||||||
} else {
|
} else {
|
||||||
unused
|
unused
|
||||||
.entry((stmt_id, parent_id, exceptions))
|
.entry((stmt_id, parent_id, exceptions))
|
||||||
.or_default()
|
.or_default()
|
||||||
.push((full_name, &binding.range));
|
.push((qualified_name, &binding.range));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -101,6 +101,35 @@ impl<'a> Binding<'a> {
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Returns the fully-qualified symbol name, if this symbol was imported from another module.
|
||||||
|
pub fn qualified_name(&self) -> Option<&str> {
|
||||||
|
match &self.kind {
|
||||||
|
BindingKind::Importation(Importation { full_name }) => Some(full_name),
|
||||||
|
BindingKind::FromImportation(FromImportation { full_name }) => Some(full_name),
|
||||||
|
BindingKind::SubmoduleImportation(SubmoduleImportation { full_name }) => {
|
||||||
|
Some(full_name)
|
||||||
|
}
|
||||||
|
_ => None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns the fully-qualified name of the module from which this symbol was imported, if this
|
||||||
|
/// symbol was imported from another module.
|
||||||
|
pub fn module_name(&self) -> Option<&str> {
|
||||||
|
match &self.kind {
|
||||||
|
BindingKind::Importation(Importation { full_name })
|
||||||
|
| BindingKind::SubmoduleImportation(SubmoduleImportation { full_name }) => {
|
||||||
|
Some(full_name.split('.').next().unwrap_or(full_name))
|
||||||
|
}
|
||||||
|
BindingKind::FromImportation(FromImportation { full_name }) => Some(
|
||||||
|
full_name
|
||||||
|
.rsplit_once('.')
|
||||||
|
.map_or(full_name, |(module, _)| module),
|
||||||
|
),
|
||||||
|
_ => None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// Returns the appropriate visual range for highlighting this binding.
|
/// Returns the appropriate visual range for highlighting this binding.
|
||||||
pub fn trimmed_range(&self, semantic_model: &SemanticModel, locator: &Locator) -> TextRange {
|
pub fn trimmed_range(&self, semantic_model: &SemanticModel, locator: &Locator) -> TextRange {
|
||||||
match self.kind {
|
match self.kind {
|
||||||
|
|
|
@ -256,14 +256,8 @@ impl<'a> SemanticModel<'a> {
|
||||||
// import pyarrow.csv
|
// import pyarrow.csv
|
||||||
// print(pa.csv.read_csv("test.csv"))
|
// print(pa.csv.read_csv("test.csv"))
|
||||||
// ```
|
// ```
|
||||||
let full_name = match &self.bindings[binding_id].kind {
|
let qualified_name = self.bindings[binding_id].qualified_name()?;
|
||||||
BindingKind::Importation(Importation { full_name }) => *full_name,
|
let has_alias = qualified_name
|
||||||
BindingKind::SubmoduleImportation(SubmoduleImportation { full_name }) => *full_name,
|
|
||||||
BindingKind::FromImportation(FromImportation { full_name }) => full_name.as_str(),
|
|
||||||
_ => return None,
|
|
||||||
};
|
|
||||||
|
|
||||||
let has_alias = full_name
|
|
||||||
.split('.')
|
.split('.')
|
||||||
.last()
|
.last()
|
||||||
.map(|segment| segment != symbol)
|
.map(|segment| segment != symbol)
|
||||||
|
@ -272,7 +266,7 @@ impl<'a> SemanticModel<'a> {
|
||||||
return None;
|
return None;
|
||||||
}
|
}
|
||||||
|
|
||||||
self.scopes[scope_id].get(full_name)
|
self.scopes[scope_id].get(qualified_name)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Resolves the [`Expr`] to a fully-qualified symbol-name, if `value` resolves to an imported
|
/// Resolves the [`Expr`] to a fully-qualified symbol-name, if `value` resolves to an imported
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue