mirror of
https://github.com/astral-sh/ruff.git
synced 2025-09-26 11:59:10 +00:00
Respect noqa directives on ImportFrom
parents for type-checking rules (#4889)
This commit is contained in:
parent
c2a3e97b7f
commit
7b0fb1a3b4
5 changed files with 59 additions and 35 deletions
|
@ -150,3 +150,17 @@ def f():
|
||||||
|
|
||||||
def f():
|
def f():
|
||||||
import pandas as pd
|
import pandas as pd
|
||||||
|
|
||||||
|
|
||||||
|
def f():
|
||||||
|
from pandas import DataFrame # noqa: TCH002
|
||||||
|
|
||||||
|
x: DataFrame = 2
|
||||||
|
|
||||||
|
|
||||||
|
def f():
|
||||||
|
from pandas import ( # noqa: TCH002
|
||||||
|
DataFrame,
|
||||||
|
)
|
||||||
|
|
||||||
|
x: DataFrame = 2
|
||||||
|
|
|
@ -4410,13 +4410,8 @@ impl<'a> Checker<'a> {
|
||||||
},
|
},
|
||||||
binding.trimmed_range(&self.semantic_model, self.locator),
|
binding.trimmed_range(&self.semantic_model, self.locator),
|
||||||
);
|
);
|
||||||
if let Some(parent) = binding.source {
|
if let Some(range) = binding.parent_range(&self.semantic_model) {
|
||||||
let parent = self.semantic_model.stmts[parent];
|
diagnostic.set_parent(range.start());
|
||||||
if parent.is_import_from_stmt()
|
|
||||||
&& parent.range().contains_range(binding.range)
|
|
||||||
{
|
|
||||||
diagnostic.set_parent(parent.start());
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
self.diagnostics.push(diagnostic);
|
self.diagnostics.push(diagnostic);
|
||||||
}
|
}
|
||||||
|
@ -5044,13 +5039,8 @@ impl<'a> Checker<'a> {
|
||||||
},
|
},
|
||||||
binding.trimmed_range(&self.semantic_model, self.locator),
|
binding.trimmed_range(&self.semantic_model, self.locator),
|
||||||
);
|
);
|
||||||
if let Some(parent) = binding
|
if let Some(range) = binding.parent_range(&self.semantic_model) {
|
||||||
.source
|
diagnostic.set_parent(range.start());
|
||||||
.map(|source| &self.semantic_model.stmts[source])
|
|
||||||
{
|
|
||||||
if parent.is_import_from_stmt() {
|
|
||||||
diagnostic.set_parent(parent.start());
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
diagnostics.push(diagnostic);
|
diagnostics.push(diagnostic);
|
||||||
}
|
}
|
||||||
|
|
|
@ -86,8 +86,11 @@ pub(crate) fn runtime_import_in_type_checking_block(
|
||||||
RuntimeImportInTypeCheckingBlock {
|
RuntimeImportInTypeCheckingBlock {
|
||||||
qualified_name: qualified_name.to_string(),
|
qualified_name: qualified_name.to_string(),
|
||||||
},
|
},
|
||||||
binding.range,
|
binding.trimmed_range(checker.semantic_model(), checker.locator),
|
||||||
);
|
);
|
||||||
|
if let Some(range) = binding.parent_range(checker.semantic_model()) {
|
||||||
|
diagnostic.set_parent(range.start());
|
||||||
|
}
|
||||||
|
|
||||||
if checker.patch(diagnostic.kind.rule()) {
|
if checker.patch(diagnostic.kind.rule()) {
|
||||||
diagnostic.try_set_fix(|| {
|
diagnostic.try_set_fix(|| {
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
use ruff_diagnostics::{AutofixKind, Diagnostic, Fix, Violation};
|
use ruff_diagnostics::{AutofixKind, Diagnostic, DiagnosticKind, Fix, Violation};
|
||||||
use ruff_macros::{derive_message_formats, violation};
|
use ruff_macros::{derive_message_formats, violation};
|
||||||
use ruff_python_semantic::binding::Binding;
|
use ruff_python_semantic::binding::Binding;
|
||||||
|
|
||||||
|
@ -263,7 +263,7 @@ pub(crate) fn typing_only_runtime_import(
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
|
||||||
// Categorize the import.
|
// Categorize the import.
|
||||||
let mut diagnostic = match categorize(
|
let kind: DiagnosticKind = match categorize(
|
||||||
qualified_name,
|
qualified_name,
|
||||||
Some(level),
|
Some(level),
|
||||||
&checker.settings.src,
|
&checker.settings.src,
|
||||||
|
@ -272,32 +272,35 @@ pub(crate) fn typing_only_runtime_import(
|
||||||
checker.settings.target_version,
|
checker.settings.target_version,
|
||||||
) {
|
) {
|
||||||
ImportSection::Known(ImportType::LocalFolder | ImportType::FirstParty) => {
|
ImportSection::Known(ImportType::LocalFolder | ImportType::FirstParty) => {
|
||||||
Diagnostic::new(
|
|
||||||
TypingOnlyFirstPartyImport {
|
TypingOnlyFirstPartyImport {
|
||||||
qualified_name: qualified_name.to_string(),
|
qualified_name: qualified_name.to_string(),
|
||||||
},
|
}
|
||||||
binding.range,
|
.into()
|
||||||
)
|
|
||||||
}
|
}
|
||||||
ImportSection::Known(ImportType::ThirdParty) | ImportSection::UserDefined(_) => {
|
ImportSection::Known(ImportType::ThirdParty) | ImportSection::UserDefined(_) => {
|
||||||
Diagnostic::new(
|
|
||||||
TypingOnlyThirdPartyImport {
|
TypingOnlyThirdPartyImport {
|
||||||
qualified_name: qualified_name.to_string(),
|
qualified_name: qualified_name.to_string(),
|
||||||
},
|
|
||||||
binding.range,
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
ImportSection::Known(ImportType::StandardLibrary) => Diagnostic::new(
|
.into()
|
||||||
TypingOnlyStandardLibraryImport {
|
}
|
||||||
|
ImportSection::Known(ImportType::StandardLibrary) => TypingOnlyStandardLibraryImport {
|
||||||
qualified_name: qualified_name.to_string(),
|
qualified_name: qualified_name.to_string(),
|
||||||
},
|
}
|
||||||
binding.range,
|
.into(),
|
||||||
),
|
|
||||||
ImportSection::Known(ImportType::Future) => {
|
ImportSection::Known(ImportType::Future) => {
|
||||||
unreachable!("`__future__` imports should be marked as used")
|
unreachable!("`__future__` imports should be marked as used")
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
let mut diagnostic = Diagnostic::new(
|
||||||
|
kind,
|
||||||
|
binding.trimmed_range(checker.semantic_model(), checker.locator),
|
||||||
|
);
|
||||||
|
if let Some(range) = binding.parent_range(checker.semantic_model()) {
|
||||||
|
diagnostic.set_parent(range.start());
|
||||||
|
}
|
||||||
|
|
||||||
if checker.patch(diagnostic.kind.rule()) {
|
if checker.patch(diagnostic.kind.rule()) {
|
||||||
diagnostic.try_set_fix(|| {
|
diagnostic.try_set_fix(|| {
|
||||||
// Step 1) Remove the import.
|
// Step 1) Remove the import.
|
||||||
|
|
|
@ -2,6 +2,7 @@ use std::ops::{Deref, DerefMut};
|
||||||
|
|
||||||
use bitflags::bitflags;
|
use bitflags::bitflags;
|
||||||
use ruff_text_size::TextRange;
|
use ruff_text_size::TextRange;
|
||||||
|
use rustpython_parser::ast::Ranged;
|
||||||
|
|
||||||
use ruff_index::{newtype_index, IndexSlice, IndexVec};
|
use ruff_index::{newtype_index, IndexSlice, IndexVec};
|
||||||
use ruff_python_ast::helpers;
|
use ruff_python_ast::helpers;
|
||||||
|
@ -143,6 +144,19 @@ impl<'a> Binding<'a> {
|
||||||
_ => self.range,
|
_ => self.range,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Returns the range of the binding's parent.
|
||||||
|
pub fn parent_range(&self, semantic_model: &SemanticModel) -> Option<TextRange> {
|
||||||
|
self.source
|
||||||
|
.map(|node_id| semantic_model.stmts[node_id])
|
||||||
|
.and_then(|parent| {
|
||||||
|
if parent.is_import_from_stmt() {
|
||||||
|
Some(parent.range())
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
bitflags! {
|
bitflags! {
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue