Respect noqa directives on ImportFrom parents for type-checking rules (#4889)

This commit is contained in:
Charlie Marsh 2023-06-05 22:37:07 -04:00 committed by GitHub
parent c2a3e97b7f
commit 7b0fb1a3b4
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
5 changed files with 59 additions and 35 deletions

View file

@ -150,3 +150,17 @@ def f():
def f():
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

View file

@ -4410,13 +4410,8 @@ impl<'a> Checker<'a> {
},
binding.trimmed_range(&self.semantic_model, self.locator),
);
if let Some(parent) = binding.source {
let parent = self.semantic_model.stmts[parent];
if parent.is_import_from_stmt()
&& parent.range().contains_range(binding.range)
{
diagnostic.set_parent(parent.start());
}
if let Some(range) = binding.parent_range(&self.semantic_model) {
diagnostic.set_parent(range.start());
}
self.diagnostics.push(diagnostic);
}
@ -5044,13 +5039,8 @@ impl<'a> Checker<'a> {
},
binding.trimmed_range(&self.semantic_model, self.locator),
);
if let Some(parent) = binding
.source
.map(|source| &self.semantic_model.stmts[source])
{
if parent.is_import_from_stmt() {
diagnostic.set_parent(parent.start());
}
if let Some(range) = binding.parent_range(&self.semantic_model) {
diagnostic.set_parent(range.start());
}
diagnostics.push(diagnostic);
}

View file

@ -86,8 +86,11 @@ pub(crate) fn runtime_import_in_type_checking_block(
RuntimeImportInTypeCheckingBlock {
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()) {
diagnostic.try_set_fix(|| {

View file

@ -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_python_semantic::binding::Binding;
@ -263,7 +263,7 @@ pub(crate) fn typing_only_runtime_import(
.unwrap();
// Categorize the import.
let mut diagnostic = match categorize(
let kind: DiagnosticKind = match categorize(
qualified_name,
Some(level),
&checker.settings.src,
@ -272,32 +272,35 @@ pub(crate) fn typing_only_runtime_import(
checker.settings.target_version,
) {
ImportSection::Known(ImportType::LocalFolder | ImportType::FirstParty) => {
Diagnostic::new(
TypingOnlyFirstPartyImport {
qualified_name: qualified_name.to_string(),
},
binding.range,
)
TypingOnlyFirstPartyImport {
qualified_name: qualified_name.to_string(),
}
.into()
}
ImportSection::Known(ImportType::ThirdParty) | ImportSection::UserDefined(_) => {
Diagnostic::new(
TypingOnlyThirdPartyImport {
qualified_name: qualified_name.to_string(),
},
binding.range,
)
}
ImportSection::Known(ImportType::StandardLibrary) => Diagnostic::new(
TypingOnlyStandardLibraryImport {
TypingOnlyThirdPartyImport {
qualified_name: qualified_name.to_string(),
},
binding.range,
),
}
.into()
}
ImportSection::Known(ImportType::StandardLibrary) => TypingOnlyStandardLibraryImport {
qualified_name: qualified_name.to_string(),
}
.into(),
ImportSection::Known(ImportType::Future) => {
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()) {
diagnostic.try_set_fix(|| {
// Step 1) Remove the import.

View file

@ -2,6 +2,7 @@ use std::ops::{Deref, DerefMut};
use bitflags::bitflags;
use ruff_text_size::TextRange;
use rustpython_parser::ast::Ranged;
use ruff_index::{newtype_index, IndexSlice, IndexVec};
use ruff_python_ast::helpers;
@ -143,6 +144,19 @@ impl<'a> Binding<'a> {
_ => 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! {