mirror of
https://github.com/astral-sh/ruff.git
synced 2025-09-18 16:20:06 +00:00
Implement E741 (#137)
This commit is contained in:
parent
81ae3bfc94
commit
c4565fe0f5
10 changed files with 275 additions and 1 deletions
|
@ -153,6 +153,7 @@ Beyond rule-set parity, ruff suffers from the following limitations vis-à-vis F
|
||||||
| E713 | NotInTest | Test for membership should be `not in` |
|
| E713 | NotInTest | Test for membership should be `not in` |
|
||||||
| E714 | NotIsTest | Test for object identity should be `is not` |
|
| E714 | NotIsTest | Test for object identity should be `is not` |
|
||||||
| E731 | DoNotAssignLambda | Do not assign a lambda expression, use a def |
|
| E731 | DoNotAssignLambda | Do not assign a lambda expression, use a def |
|
||||||
|
| E741 | AmbiguousVariableName | ambiguous variable name '...' |
|
||||||
| E902 | IOError | No such file or directory: `...` |
|
| E902 | IOError | No such file or directory: `...` |
|
||||||
| F401 | UnusedImport | `...` imported but unused |
|
| F401 | UnusedImport | `...` imported but unused |
|
||||||
| F403 | ImportStarUsage | Unable to detect undefined names |
|
| F403 | ImportStarUsage | Unable to detect undefined names |
|
||||||
|
|
|
@ -3,6 +3,7 @@ use ruff::checks::{CheckKind, RejectedCmpop};
|
||||||
|
|
||||||
fn main() {
|
fn main() {
|
||||||
let mut check_kinds: Vec<CheckKind> = vec![
|
let mut check_kinds: Vec<CheckKind> = vec![
|
||||||
|
CheckKind::AmbiguousVariableName("...".to_string()),
|
||||||
CheckKind::AssertTuple,
|
CheckKind::AssertTuple,
|
||||||
CheckKind::DefaultExceptNotLast,
|
CheckKind::DefaultExceptNotLast,
|
||||||
CheckKind::DoNotAssignLambda,
|
CheckKind::DoNotAssignLambda,
|
||||||
|
|
72
resources/test/fixtures/E741.py
vendored
Normal file
72
resources/test/fixtures/E741.py
vendored
Normal file
|
@ -0,0 +1,72 @@
|
||||||
|
from contextlib import contextmanager
|
||||||
|
|
||||||
|
l = 0
|
||||||
|
I = 0
|
||||||
|
O = 0
|
||||||
|
l: int = 0
|
||||||
|
|
||||||
|
a, l = 0, 1
|
||||||
|
[a, l] = 0, 1
|
||||||
|
a, *l = 0, 1, 2
|
||||||
|
a = l = 0
|
||||||
|
|
||||||
|
o = 0
|
||||||
|
i = 0
|
||||||
|
|
||||||
|
for l in range(3):
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
for a, l in zip(range(3), range(3)):
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
def f1():
|
||||||
|
global l
|
||||||
|
l = 0
|
||||||
|
|
||||||
|
|
||||||
|
def f2():
|
||||||
|
l = 0
|
||||||
|
|
||||||
|
def f3():
|
||||||
|
nonlocal l
|
||||||
|
l = 1
|
||||||
|
|
||||||
|
f3()
|
||||||
|
return l
|
||||||
|
|
||||||
|
|
||||||
|
def f4(l, /, I):
|
||||||
|
return l, I, O
|
||||||
|
|
||||||
|
|
||||||
|
def f5(l=0, *, I=1):
|
||||||
|
return l, I
|
||||||
|
|
||||||
|
|
||||||
|
def f6(*l, **I):
|
||||||
|
return l, I
|
||||||
|
|
||||||
|
|
||||||
|
@contextmanager
|
||||||
|
def ctx1():
|
||||||
|
yield 0
|
||||||
|
|
||||||
|
|
||||||
|
with ctx1() as l:
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
@contextmanager
|
||||||
|
def ctx2():
|
||||||
|
yield 0, 1
|
||||||
|
|
||||||
|
|
||||||
|
with ctx2() as (a, l):
|
||||||
|
pass
|
||||||
|
|
||||||
|
try:
|
||||||
|
pass
|
||||||
|
except ValueError as l:
|
||||||
|
pass
|
1
resources/test/fixtures/pyproject.toml
vendored
1
resources/test/fixtures/pyproject.toml
vendored
|
@ -9,6 +9,7 @@ select = [
|
||||||
"E713",
|
"E713",
|
||||||
"E714",
|
"E714",
|
||||||
"E731",
|
"E731",
|
||||||
|
"E741",
|
||||||
"E902",
|
"E902",
|
||||||
"F401",
|
"F401",
|
||||||
"F403",
|
"F403",
|
||||||
|
|
|
@ -93,6 +93,22 @@ pub fn check_do_not_assign_lambda(value: &Expr, location: Location) -> Option<Ch
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn is_ambiguous_name(name: &str) -> bool {
|
||||||
|
name == "l" || name == "I" || name == "O"
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Check AmbiguousVariableName compliance.
|
||||||
|
pub fn check_ambiguous_variable_name(name: &str, location: Location) -> Option<Check> {
|
||||||
|
if is_ambiguous_name(name) {
|
||||||
|
Some(Check::new(
|
||||||
|
CheckKind::AmbiguousVariableName(name.to_string()),
|
||||||
|
location,
|
||||||
|
))
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// Check UselessObjectInheritance compliance.
|
/// Check UselessObjectInheritance compliance.
|
||||||
pub fn check_useless_object_inheritance(
|
pub fn check_useless_object_inheritance(
|
||||||
stmt: &Stmt,
|
stmt: &Stmt,
|
||||||
|
|
|
@ -158,6 +158,12 @@ where
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if self.settings.select.contains(&CheckCode::E741) {
|
||||||
|
self.checks.extend(names.iter().filter_map(|name| {
|
||||||
|
checks::check_ambiguous_variable_name(name, stmt.location)
|
||||||
|
}));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
StmtKind::FunctionDef {
|
StmtKind::FunctionDef {
|
||||||
name,
|
name,
|
||||||
|
@ -507,9 +513,16 @@ where
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
ExprKind::Name { ctx, .. } => match ctx {
|
ExprKind::Name { id, ctx } => match ctx {
|
||||||
ExprContext::Load => self.handle_node_load(expr),
|
ExprContext::Load => self.handle_node_load(expr),
|
||||||
ExprContext::Store => {
|
ExprContext::Store => {
|
||||||
|
if self.settings.select.contains(&CheckCode::E741) {
|
||||||
|
if let Some(check) =
|
||||||
|
checks::check_ambiguous_variable_name(id, expr.location)
|
||||||
|
{
|
||||||
|
self.checks.push(check);
|
||||||
|
}
|
||||||
|
}
|
||||||
let parent =
|
let parent =
|
||||||
self.parents[*(self.parent_stack.last().expect("No parent found."))];
|
self.parents[*(self.parent_stack.last().expect("No parent found."))];
|
||||||
self.handle_node_store(expr, Some(parent));
|
self.handle_node_store(expr, Some(parent));
|
||||||
|
@ -746,6 +759,13 @@ where
|
||||||
match &excepthandler.node {
|
match &excepthandler.node {
|
||||||
ExcepthandlerKind::ExceptHandler { name, .. } => match name {
|
ExcepthandlerKind::ExceptHandler { name, .. } => match name {
|
||||||
Some(name) => {
|
Some(name) => {
|
||||||
|
if self.settings.select.contains(&CheckCode::E741) {
|
||||||
|
if let Some(check) =
|
||||||
|
checks::check_ambiguous_variable_name(name, excepthandler.location)
|
||||||
|
{
|
||||||
|
self.checks.push(check);
|
||||||
|
}
|
||||||
|
}
|
||||||
let scope =
|
let scope =
|
||||||
&self.scopes[*(self.scope_stack.last().expect("No current scope found."))];
|
&self.scopes[*(self.scope_stack.last().expect("No current scope found."))];
|
||||||
if scope.values.contains_key(name) {
|
if scope.values.contains_key(name) {
|
||||||
|
@ -838,6 +858,13 @@ where
|
||||||
location: arg.location,
|
location: arg.location,
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
|
|
||||||
|
if self.settings.select.contains(&CheckCode::E741) {
|
||||||
|
if let Some(check) = checks::check_ambiguous_variable_name(&arg.node.arg, arg.location)
|
||||||
|
{
|
||||||
|
self.checks.push(check);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -15,6 +15,7 @@ pub enum CheckCode {
|
||||||
E713,
|
E713,
|
||||||
E714,
|
E714,
|
||||||
E731,
|
E731,
|
||||||
|
E741,
|
||||||
E902,
|
E902,
|
||||||
F401,
|
F401,
|
||||||
F403,
|
F403,
|
||||||
|
@ -50,6 +51,7 @@ impl FromStr for CheckCode {
|
||||||
"E713" => Ok(CheckCode::E713),
|
"E713" => Ok(CheckCode::E713),
|
||||||
"E714" => Ok(CheckCode::E714),
|
"E714" => Ok(CheckCode::E714),
|
||||||
"E731" => Ok(CheckCode::E731),
|
"E731" => Ok(CheckCode::E731),
|
||||||
|
"E741" => Ok(CheckCode::E741),
|
||||||
"E902" => Ok(CheckCode::E902),
|
"E902" => Ok(CheckCode::E902),
|
||||||
"F401" => Ok(CheckCode::F401),
|
"F401" => Ok(CheckCode::F401),
|
||||||
"F403" => Ok(CheckCode::F403),
|
"F403" => Ok(CheckCode::F403),
|
||||||
|
@ -86,6 +88,7 @@ impl CheckCode {
|
||||||
CheckCode::E713 => "E713",
|
CheckCode::E713 => "E713",
|
||||||
CheckCode::E714 => "E714",
|
CheckCode::E714 => "E714",
|
||||||
CheckCode::E731 => "E731",
|
CheckCode::E731 => "E731",
|
||||||
|
CheckCode::E741 => "E741",
|
||||||
CheckCode::E902 => "E902",
|
CheckCode::E902 => "E902",
|
||||||
CheckCode::F401 => "F401",
|
CheckCode::F401 => "F401",
|
||||||
CheckCode::F403 => "F403",
|
CheckCode::F403 => "F403",
|
||||||
|
@ -120,6 +123,7 @@ impl CheckCode {
|
||||||
CheckCode::E713 => &LintSource::AST,
|
CheckCode::E713 => &LintSource::AST,
|
||||||
CheckCode::E714 => &LintSource::AST,
|
CheckCode::E714 => &LintSource::AST,
|
||||||
CheckCode::E731 => &LintSource::AST,
|
CheckCode::E731 => &LintSource::AST,
|
||||||
|
CheckCode::E741 => &LintSource::AST,
|
||||||
CheckCode::E902 => &LintSource::FileSystem,
|
CheckCode::E902 => &LintSource::FileSystem,
|
||||||
CheckCode::F401 => &LintSource::AST,
|
CheckCode::F401 => &LintSource::AST,
|
||||||
CheckCode::F403 => &LintSource::AST,
|
CheckCode::F403 => &LintSource::AST,
|
||||||
|
@ -161,6 +165,7 @@ pub enum RejectedCmpop {
|
||||||
#[derive(Debug, PartialEq, Eq, Serialize, Deserialize)]
|
#[derive(Debug, PartialEq, Eq, Serialize, Deserialize)]
|
||||||
pub enum CheckKind {
|
pub enum CheckKind {
|
||||||
AssertTuple,
|
AssertTuple,
|
||||||
|
AmbiguousVariableName(String),
|
||||||
DefaultExceptNotLast,
|
DefaultExceptNotLast,
|
||||||
DoNotAssignLambda,
|
DoNotAssignLambda,
|
||||||
DuplicateArgumentName,
|
DuplicateArgumentName,
|
||||||
|
@ -195,6 +200,7 @@ impl CheckKind {
|
||||||
pub fn name(&self) -> &'static str {
|
pub fn name(&self) -> &'static str {
|
||||||
match self {
|
match self {
|
||||||
CheckKind::AssertTuple => "AssertTuple",
|
CheckKind::AssertTuple => "AssertTuple",
|
||||||
|
CheckKind::AmbiguousVariableName(_) => "AmbiguousVariableName",
|
||||||
CheckKind::DefaultExceptNotLast => "DefaultExceptNotLast",
|
CheckKind::DefaultExceptNotLast => "DefaultExceptNotLast",
|
||||||
CheckKind::DuplicateArgumentName => "DuplicateArgumentName",
|
CheckKind::DuplicateArgumentName => "DuplicateArgumentName",
|
||||||
CheckKind::FStringMissingPlaceholders => "FStringMissingPlaceholders",
|
CheckKind::FStringMissingPlaceholders => "FStringMissingPlaceholders",
|
||||||
|
@ -239,6 +245,7 @@ impl CheckKind {
|
||||||
CheckKind::ImportStarUsage => &CheckCode::F403,
|
CheckKind::ImportStarUsage => &CheckCode::F403,
|
||||||
CheckKind::LineTooLong => &CheckCode::E501,
|
CheckKind::LineTooLong => &CheckCode::E501,
|
||||||
CheckKind::DoNotAssignLambda => &CheckCode::E731,
|
CheckKind::DoNotAssignLambda => &CheckCode::E731,
|
||||||
|
CheckKind::AmbiguousVariableName(_) => &CheckCode::E741,
|
||||||
CheckKind::ModuleImportNotAtTopOfFile => &CheckCode::E402,
|
CheckKind::ModuleImportNotAtTopOfFile => &CheckCode::E402,
|
||||||
CheckKind::MultiValueRepeatedKeyLiteral => &CheckCode::F601,
|
CheckKind::MultiValueRepeatedKeyLiteral => &CheckCode::F601,
|
||||||
CheckKind::MultiValueRepeatedKeyVariable(_) => &CheckCode::F602,
|
CheckKind::MultiValueRepeatedKeyVariable(_) => &CheckCode::F602,
|
||||||
|
@ -285,6 +292,9 @@ impl CheckKind {
|
||||||
CheckKind::DoNotAssignLambda => {
|
CheckKind::DoNotAssignLambda => {
|
||||||
"Do not assign a lambda expression, use a def".to_string()
|
"Do not assign a lambda expression, use a def".to_string()
|
||||||
}
|
}
|
||||||
|
CheckKind::AmbiguousVariableName(name) => {
|
||||||
|
format!("ambiguous variable name '{}'", name)
|
||||||
|
}
|
||||||
CheckKind::ModuleImportNotAtTopOfFile => {
|
CheckKind::ModuleImportNotAtTopOfFile => {
|
||||||
"Module level import not at top of file".to_string()
|
"Module level import not at top of file".to_string()
|
||||||
}
|
}
|
||||||
|
@ -366,6 +376,7 @@ impl CheckKind {
|
||||||
CheckKind::IfTuple => false,
|
CheckKind::IfTuple => false,
|
||||||
CheckKind::ImportStarUsage => false,
|
CheckKind::ImportStarUsage => false,
|
||||||
CheckKind::DoNotAssignLambda => false,
|
CheckKind::DoNotAssignLambda => false,
|
||||||
|
CheckKind::AmbiguousVariableName(_) => false,
|
||||||
CheckKind::LineTooLong => false,
|
CheckKind::LineTooLong => false,
|
||||||
CheckKind::ModuleImportNotAtTopOfFile => false,
|
CheckKind::ModuleImportNotAtTopOfFile => false,
|
||||||
CheckKind::MultiValueRepeatedKeyLiteral => false,
|
CheckKind::MultiValueRepeatedKeyLiteral => false,
|
||||||
|
|
143
src/linter.rs
143
src/linter.rs
|
@ -298,6 +298,149 @@ mod tests {
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn e741() -> Result<()> {
|
||||||
|
let mut actual = check_path(
|
||||||
|
Path::new("./resources/test/fixtures/E741.py"),
|
||||||
|
&settings::Settings {
|
||||||
|
line_length: 88,
|
||||||
|
exclude: vec![],
|
||||||
|
select: BTreeSet::from([CheckCode::E741]),
|
||||||
|
},
|
||||||
|
&fixer::Mode::Generate,
|
||||||
|
)?;
|
||||||
|
actual.sort_by_key(|check| check.location);
|
||||||
|
let expected = vec![
|
||||||
|
Check {
|
||||||
|
kind: CheckKind::AmbiguousVariableName("l".to_string()),
|
||||||
|
location: Location::new(3, 1),
|
||||||
|
fix: None,
|
||||||
|
},
|
||||||
|
Check {
|
||||||
|
kind: CheckKind::AmbiguousVariableName("I".to_string()),
|
||||||
|
location: Location::new(4, 1),
|
||||||
|
fix: None,
|
||||||
|
},
|
||||||
|
Check {
|
||||||
|
kind: CheckKind::AmbiguousVariableName("O".to_string()),
|
||||||
|
location: Location::new(5, 1),
|
||||||
|
fix: None,
|
||||||
|
},
|
||||||
|
Check {
|
||||||
|
kind: CheckKind::AmbiguousVariableName("l".to_string()),
|
||||||
|
location: Location::new(6, 1),
|
||||||
|
fix: None,
|
||||||
|
},
|
||||||
|
Check {
|
||||||
|
kind: CheckKind::AmbiguousVariableName("l".to_string()),
|
||||||
|
location: Location::new(8, 4),
|
||||||
|
fix: None,
|
||||||
|
},
|
||||||
|
Check {
|
||||||
|
kind: CheckKind::AmbiguousVariableName("l".to_string()),
|
||||||
|
location: Location::new(9, 5),
|
||||||
|
fix: None,
|
||||||
|
},
|
||||||
|
Check {
|
||||||
|
kind: CheckKind::AmbiguousVariableName("l".to_string()),
|
||||||
|
location: Location::new(10, 5),
|
||||||
|
fix: None,
|
||||||
|
},
|
||||||
|
Check {
|
||||||
|
kind: CheckKind::AmbiguousVariableName("l".to_string()),
|
||||||
|
location: Location::new(11, 5),
|
||||||
|
fix: None,
|
||||||
|
},
|
||||||
|
Check {
|
||||||
|
kind: CheckKind::AmbiguousVariableName("l".to_string()),
|
||||||
|
location: Location::new(16, 5),
|
||||||
|
fix: None,
|
||||||
|
},
|
||||||
|
Check {
|
||||||
|
kind: CheckKind::AmbiguousVariableName("l".to_string()),
|
||||||
|
location: Location::new(20, 8),
|
||||||
|
fix: None,
|
||||||
|
},
|
||||||
|
Check {
|
||||||
|
kind: CheckKind::AmbiguousVariableName("l".to_string()),
|
||||||
|
location: Location::new(25, 5),
|
||||||
|
fix: None,
|
||||||
|
},
|
||||||
|
Check {
|
||||||
|
kind: CheckKind::AmbiguousVariableName("l".to_string()),
|
||||||
|
location: Location::new(26, 5),
|
||||||
|
fix: None,
|
||||||
|
},
|
||||||
|
Check {
|
||||||
|
kind: CheckKind::AmbiguousVariableName("l".to_string()),
|
||||||
|
location: Location::new(30, 5),
|
||||||
|
fix: None,
|
||||||
|
},
|
||||||
|
Check {
|
||||||
|
kind: CheckKind::AmbiguousVariableName("l".to_string()),
|
||||||
|
location: Location::new(33, 9),
|
||||||
|
fix: None,
|
||||||
|
},
|
||||||
|
Check {
|
||||||
|
kind: CheckKind::AmbiguousVariableName("l".to_string()),
|
||||||
|
location: Location::new(34, 9),
|
||||||
|
fix: None,
|
||||||
|
},
|
||||||
|
Check {
|
||||||
|
kind: CheckKind::AmbiguousVariableName("l".to_string()),
|
||||||
|
location: Location::new(40, 8),
|
||||||
|
fix: None,
|
||||||
|
},
|
||||||
|
Check {
|
||||||
|
kind: CheckKind::AmbiguousVariableName("I".to_string()),
|
||||||
|
location: Location::new(40, 14),
|
||||||
|
fix: None,
|
||||||
|
},
|
||||||
|
Check {
|
||||||
|
kind: CheckKind::AmbiguousVariableName("l".to_string()),
|
||||||
|
location: Location::new(44, 8),
|
||||||
|
fix: None,
|
||||||
|
},
|
||||||
|
Check {
|
||||||
|
kind: CheckKind::AmbiguousVariableName("I".to_string()),
|
||||||
|
location: Location::new(44, 16),
|
||||||
|
fix: None,
|
||||||
|
},
|
||||||
|
Check {
|
||||||
|
kind: CheckKind::AmbiguousVariableName("l".to_string()),
|
||||||
|
location: Location::new(48, 9),
|
||||||
|
fix: None,
|
||||||
|
},
|
||||||
|
Check {
|
||||||
|
kind: CheckKind::AmbiguousVariableName("I".to_string()),
|
||||||
|
location: Location::new(48, 14),
|
||||||
|
fix: None,
|
||||||
|
},
|
||||||
|
Check {
|
||||||
|
kind: CheckKind::AmbiguousVariableName("l".to_string()),
|
||||||
|
location: Location::new(57, 16),
|
||||||
|
fix: None,
|
||||||
|
},
|
||||||
|
Check {
|
||||||
|
kind: CheckKind::AmbiguousVariableName("l".to_string()),
|
||||||
|
location: Location::new(66, 20),
|
||||||
|
fix: None,
|
||||||
|
},
|
||||||
|
Check {
|
||||||
|
kind: CheckKind::AmbiguousVariableName("l".to_string()),
|
||||||
|
location: Location::new(71, 1),
|
||||||
|
fix: None,
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
assert_eq!(actual.len(), expected.len());
|
||||||
|
for i in 0..actual.len() {
|
||||||
|
assert_eq!(actual[i], expected[i]);
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn f401() -> Result<()> {
|
fn f401() -> Result<()> {
|
||||||
let mut actual = check_path(
|
let mut actual = check_path(
|
||||||
|
|
|
@ -266,6 +266,7 @@ other-attribute = 1
|
||||||
CheckCode::E713,
|
CheckCode::E713,
|
||||||
CheckCode::E714,
|
CheckCode::E714,
|
||||||
CheckCode::E731,
|
CheckCode::E731,
|
||||||
|
CheckCode::E741,
|
||||||
CheckCode::E902,
|
CheckCode::E902,
|
||||||
CheckCode::F401,
|
CheckCode::F401,
|
||||||
CheckCode::F403,
|
CheckCode::F403,
|
||||||
|
|
|
@ -51,6 +51,7 @@ impl Settings {
|
||||||
CheckCode::E713,
|
CheckCode::E713,
|
||||||
CheckCode::E714,
|
CheckCode::E714,
|
||||||
CheckCode::E731,
|
CheckCode::E731,
|
||||||
|
CheckCode::E741,
|
||||||
CheckCode::E902,
|
CheckCode::E902,
|
||||||
CheckCode::F401,
|
CheckCode::F401,
|
||||||
CheckCode::F403,
|
CheckCode::F403,
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue