mirror of
https://github.com/astral-sh/ruff.git
synced 2025-11-03 21:23:45 +00:00
Track TYPE_CHECKING blocks in Importer (#4593)
This commit is contained in:
parent
0854543328
commit
ea31229be0
9 changed files with 155 additions and 121 deletions
|
|
@ -41,8 +41,8 @@ libcst = { workspace = true }
|
|||
log = { workspace = true }
|
||||
natord = { version = "1.0.9" }
|
||||
nohash-hasher = { workspace = true }
|
||||
num-bigint = { version = "0.4.3" }
|
||||
num-traits = { version = "0.2.15" }
|
||||
num-bigint = { workspace = true }
|
||||
num-traits = { workspace = true }
|
||||
once_cell = { workspace = true }
|
||||
path-absolutize = { workspace = true, features = [
|
||||
"once_cell_cache",
|
||||
|
|
|
|||
|
|
@ -2076,16 +2076,21 @@ where
|
|||
self.visit_body(body);
|
||||
self.visit_body(orelse);
|
||||
}
|
||||
Stmt::If(ast::StmtIf {
|
||||
test,
|
||||
body,
|
||||
orelse,
|
||||
range: _,
|
||||
}) => {
|
||||
Stmt::If(
|
||||
stmt_if @ ast::StmtIf {
|
||||
test,
|
||||
body,
|
||||
orelse,
|
||||
range: _,
|
||||
},
|
||||
) => {
|
||||
self.visit_boolean_test(test);
|
||||
|
||||
if flake8_type_checking::helpers::is_type_checking_block(&self.semantic_model, test)
|
||||
{
|
||||
if analyze::typing::is_type_checking_block(stmt_if, &self.semantic_model) {
|
||||
if self.semantic_model.at_top_level() {
|
||||
self.importer.visit_type_checking_block(stmt);
|
||||
}
|
||||
|
||||
if self.enabled(Rule::EmptyTypeCheckingBlock) {
|
||||
flake8_type_checking::rules::empty_type_checking_block(self, stmt, body);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -18,10 +18,16 @@ use crate::importer::insertion::Insertion;
|
|||
mod insertion;
|
||||
|
||||
pub(crate) struct Importer<'a> {
|
||||
/// The Python AST to which we are adding imports.
|
||||
python_ast: &'a Suite,
|
||||
/// The [`Locator`] for the Python AST.
|
||||
locator: &'a Locator<'a>,
|
||||
/// The [`Stylist`] for the Python AST.
|
||||
stylist: &'a Stylist<'a>,
|
||||
ordered_imports: Vec<&'a Stmt>,
|
||||
/// The list of visited, top-level runtime imports in the Python AST.
|
||||
runtime_imports: Vec<&'a Stmt>,
|
||||
/// The list of visited, top-level `if TYPE_CHECKING:` blocks in the Python AST.
|
||||
type_checking_blocks: Vec<&'a Stmt>,
|
||||
}
|
||||
|
||||
impl<'a> Importer<'a> {
|
||||
|
|
@ -34,13 +40,19 @@ impl<'a> Importer<'a> {
|
|||
python_ast,
|
||||
locator,
|
||||
stylist,
|
||||
ordered_imports: Vec::default(),
|
||||
runtime_imports: Vec::default(),
|
||||
type_checking_blocks: Vec::default(),
|
||||
}
|
||||
}
|
||||
|
||||
/// Visit a top-level import statement.
|
||||
pub(crate) fn visit_import(&mut self, import: &'a Stmt) {
|
||||
self.ordered_imports.push(import);
|
||||
self.runtime_imports.push(import);
|
||||
}
|
||||
|
||||
/// Visit a top-level type-checking block.
|
||||
pub(crate) fn visit_type_checking_block(&mut self, type_checking_block: &'a Stmt) {
|
||||
self.type_checking_blocks.push(type_checking_block);
|
||||
}
|
||||
|
||||
/// Add an import statement to import the given module.
|
||||
|
|
@ -168,17 +180,17 @@ impl<'a> Importer<'a> {
|
|||
|
||||
/// Return the import statement that precedes the given position, if any.
|
||||
fn preceding_import(&self, at: TextSize) -> Option<&Stmt> {
|
||||
self.ordered_imports
|
||||
self.runtime_imports
|
||||
.partition_point(|stmt| stmt.start() < at)
|
||||
.checked_sub(1)
|
||||
.map(|idx| self.ordered_imports[idx])
|
||||
.map(|idx| self.runtime_imports[idx])
|
||||
}
|
||||
|
||||
/// Return the top-level [`Stmt`] that imports the given module using `Stmt::ImportFrom`
|
||||
/// preceding the given position, if any.
|
||||
fn find_import_from(&self, module: &str, at: TextSize) -> Option<&Stmt> {
|
||||
let mut import_from = None;
|
||||
for stmt in &self.ordered_imports {
|
||||
for stmt in &self.runtime_imports {
|
||||
if stmt.start() >= at {
|
||||
break;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,48 +1,9 @@
|
|||
use num_traits::Zero;
|
||||
use rustpython_parser::ast::{self, Constant, Expr};
|
||||
|
||||
use ruff_python_ast::call_path::from_qualified_name;
|
||||
use ruff_python_ast::helpers::map_callable;
|
||||
use ruff_python_semantic::binding::{Binding, BindingKind};
|
||||
use ruff_python_semantic::model::SemanticModel;
|
||||
use ruff_python_semantic::scope::ScopeKind;
|
||||
|
||||
/// Return `true` if [`Expr`] is a guard for a type-checking block.
|
||||
pub(crate) fn is_type_checking_block(semantic_model: &SemanticModel, test: &Expr) -> bool {
|
||||
// Ex) `if False:`
|
||||
if matches!(
|
||||
test,
|
||||
Expr::Constant(ast::ExprConstant {
|
||||
value: Constant::Bool(false),
|
||||
..
|
||||
})
|
||||
) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// Ex) `if 0:`
|
||||
if let Expr::Constant(ast::ExprConstant {
|
||||
value: Constant::Int(value),
|
||||
..
|
||||
}) = &test
|
||||
{
|
||||
if value.is_zero() {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
// Ex) `if typing.TYPE_CHECKING:`
|
||||
if semantic_model
|
||||
.resolve_call_path(test)
|
||||
.map_or(false, |call_path| {
|
||||
call_path.as_slice() == ["typing", "TYPE_CHECKING"]
|
||||
})
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
false
|
||||
}
|
||||
use rustpython_parser::ast;
|
||||
|
||||
pub(crate) fn is_valid_runtime_import(semantic_model: &SemanticModel, binding: &Binding) -> bool {
|
||||
if matches!(
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue