Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

enforce required imports even with useless alias #14287

Merged
merged 10 commits into from
Nov 14, 2024
2 changes: 1 addition & 1 deletion crates/ruff_linter/src/checkers/ast/analyze/statement.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1035,7 +1035,7 @@ pub(crate) fn statement(stmt: &Stmt, checker: &mut Checker) {
}
if !checker.source_type.is_stub() {
if checker.enabled(Rule::UselessImportAlias) {
pylint::rules::useless_import_alias(checker, alias);
pylint::rules::useless_importfrom_alias(checker, alias, module, level);
}
}
}
Expand Down
68 changes: 68 additions & 0 deletions crates/ruff_linter/src/rules/pylint/rules/useless_import_alias.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ use ruff_python_ast::Alias;

use ruff_diagnostics::{AlwaysFixableViolation, Diagnostic, Edit, Fix};
use ruff_macros::{derive_message_formats, violation};
use ruff_python_semantic as semantic;
use ruff_text_size::Ranged;

use crate::checkers::ast::Checker;
Expand Down Expand Up @@ -52,6 +53,73 @@ pub(crate) fn useless_import_alias(checker: &mut Checker, alias: &Alias) {
if alias.name.as_str() != asname.as_str() {
return;
}
// While the fix is specified as always available,
// the presence of a user-specified required import (I002)
// with a useless alias causes an infinite loop. So
// in this case we keep the diagnostic but omit the fix.
// See https://github.com/astral-sh/ruff/issues/14283
dylwil3 marked this conversation as resolved.
Show resolved Hide resolved
let required_imports = &checker.settings.isort.required_imports;
if !required_imports.is_empty() {
let semantic_alias = semantic::Alias {
name: alias.name.as_str().to_owned(),
as_name: Some(asname.as_str().to_owned()),
};
if required_imports.contains(&semantic::NameImport::Import(semantic::ModuleNameImport {
name: semantic_alias,
})) {
let diagnostic = Diagnostic::new(UselessImportAlias, alias.range());
checker.diagnostics.push(diagnostic);
return;
}
}

let mut diagnostic = Diagnostic::new(UselessImportAlias, alias.range());
diagnostic.set_fix(Fix::unsafe_edit(Edit::range_replacement(
asname.to_string(),
alias.range(),
)));
checker.diagnostics.push(diagnostic);
}

/// PLC0414
pub(crate) fn useless_importfrom_alias(
checker: &mut Checker,
alias: &Alias,
module: Option<&str>,
level: u32,
) {
let Some(asname) = &alias.asname else {
return;
};
if alias.name.contains('.') {
dylwil3 marked this conversation as resolved.
Show resolved Hide resolved
return;
}
if alias.name.as_str() != asname.as_str() {
return;
}
dylwil3 marked this conversation as resolved.
Show resolved Hide resolved
// While the fix is specified as always available,
// the presence of a user-specified required import (I002)
// with a useless alias causes an infinite loop. So
// in this case we keep the diagnostic but omit the fix.
// See https://github.com/astral-sh/ruff/issues/14283
let required_imports = &checker.settings.isort.required_imports;
if !required_imports.is_empty() {
let semantic_alias = semantic::Alias {
name: alias.name.as_str().to_owned(),
as_name: Some(asname.as_str().to_owned()),
};
if required_imports.contains(&semantic::NameImport::ImportFrom(
semantic::MemberNameImport {
name: semantic_alias,
module: module.map(str::to_string),
level,
},
)) {
let diagnostic = Diagnostic::new(UselessImportAlias, alias.range());
checker.diagnostics.push(diagnostic);
return;
}
}

let mut diagnostic = Diagnostic::new(UselessImportAlias, alias.range());
diagnostic.set_fix(Fix::unsafe_edit(Edit::range_replacement(
Expand Down
Loading