mirror of https://github.com/astral-sh/ruff
Make unused variable pattern configurable (#265)
This commit is contained in:
parent
d77979429c
commit
dce86e065b
|
|
@ -10,13 +10,13 @@ except ValueError as e:
|
|||
print(e)
|
||||
|
||||
|
||||
def f():
|
||||
def f1():
|
||||
x = 1
|
||||
y = 2
|
||||
z = x + y
|
||||
|
||||
|
||||
def g():
|
||||
def f2():
|
||||
foo = (1, 2)
|
||||
(a, b) = (1, 2)
|
||||
|
||||
|
|
@ -26,6 +26,12 @@ def g():
|
|||
(x, y) = baz = bar
|
||||
|
||||
|
||||
def h():
|
||||
def f3():
|
||||
locals()
|
||||
x = 1
|
||||
|
||||
|
||||
def f4():
|
||||
_ = 1
|
||||
__ = 1
|
||||
_discarded = 1
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
use std::collections::BTreeSet;
|
||||
|
||||
use itertools::izip;
|
||||
use regex::Regex;
|
||||
use rustpython_parser::ast::{
|
||||
Arg, Arguments, Cmpop, Constant, Excepthandler, ExcepthandlerKind, Expr, ExprKind, Keyword,
|
||||
Location, Stmt, StmtKind, Unaryop,
|
||||
|
|
@ -71,7 +72,11 @@ pub fn check_not_tests(
|
|||
}
|
||||
|
||||
/// Check UnusedVariable compliance.
|
||||
pub fn check_unused_variables(scope: &Scope, locator: &dyn CheckLocator) -> Vec<Check> {
|
||||
pub fn check_unused_variables(
|
||||
scope: &Scope,
|
||||
locator: &dyn CheckLocator,
|
||||
dummy_variable_rgx: &Regex,
|
||||
) -> Vec<Check> {
|
||||
let mut checks: Vec<Check> = vec![];
|
||||
|
||||
if matches!(
|
||||
|
|
@ -82,13 +87,12 @@ pub fn check_unused_variables(scope: &Scope, locator: &dyn CheckLocator) -> Vec<
|
|||
}
|
||||
|
||||
for (name, binding) in scope.values.iter() {
|
||||
// TODO(charlie): Ignore if using `locals`.
|
||||
if binding.used.is_none()
|
||||
&& name != "_"
|
||||
&& matches!(binding.kind, BindingKind::Assignment)
|
||||
&& !dummy_variable_rgx.is_match(name)
|
||||
&& name != "__tracebackhide__"
|
||||
&& name != "__traceback_info__"
|
||||
&& name != "__traceback_supplement__"
|
||||
&& matches!(binding.kind, BindingKind::Assignment)
|
||||
{
|
||||
checks.push(Check::new(
|
||||
CheckKind::UnusedVariable(name.to_string()),
|
||||
|
|
|
|||
|
|
@ -1415,8 +1415,11 @@ impl<'a> Checker<'a> {
|
|||
fn check_deferred_assignments(&mut self) {
|
||||
if self.settings.select.contains(&CheckCode::F841) {
|
||||
while let Some(index) = self.deferred_assignments.pop() {
|
||||
self.checks
|
||||
.extend(checks::check_unused_variables(&self.scopes[index], self));
|
||||
self.checks.extend(checks::check_unused_variables(
|
||||
&self.scopes[index],
|
||||
self,
|
||||
&self.settings.dummy_variable_rgx,
|
||||
));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -182,6 +182,8 @@ pub fn check_lines(
|
|||
mod tests {
|
||||
use std::collections::BTreeSet;
|
||||
|
||||
use regex::Regex;
|
||||
|
||||
use super::check_lines;
|
||||
use super::*;
|
||||
|
||||
|
|
@ -198,6 +200,7 @@ mod tests {
|
|||
exclude: vec![],
|
||||
extend_exclude: vec![],
|
||||
select: BTreeSet::from_iter(vec![CheckCode::E501]),
|
||||
dummy_variable_rgx: Regex::new(r"^_+").unwrap(),
|
||||
};
|
||||
check_lines(
|
||||
&mut checks,
|
||||
|
|
|
|||
|
|
@ -119,6 +119,7 @@ mod tests {
|
|||
use std::path::Path;
|
||||
|
||||
use anyhow::Result;
|
||||
use regex::Regex;
|
||||
use rustpython_parser::lexer;
|
||||
use rustpython_parser::lexer::LexResult;
|
||||
|
||||
|
|
@ -596,6 +597,21 @@ mod tests {
|
|||
Ok(())
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn f841_dummy_variable_rgx() -> Result<()> {
|
||||
let mut checks = check_path(
|
||||
Path::new("./resources/test/fixtures/F841.py"),
|
||||
&settings::Settings {
|
||||
dummy_variable_rgx: Regex::new(r"^z$").unwrap(),
|
||||
..settings::Settings::for_rule(CheckCode::F841)
|
||||
},
|
||||
&fixer::Mode::Generate,
|
||||
)?;
|
||||
checks.sort_by_key(|check| check.location);
|
||||
insta::assert_yaml_snapshot!(checks);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn f901() -> Result<()> {
|
||||
let mut checks = check_path(
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
extern crate core;
|
||||
|
||||
use regex::Regex;
|
||||
use std::io;
|
||||
use std::path::{Path, PathBuf};
|
||||
use std::process::ExitCode;
|
||||
|
|
@ -85,6 +86,9 @@ struct Cli {
|
|||
/// Enable automatic additions of noqa directives to failing lines.
|
||||
#[clap(long, action)]
|
||||
add_noqa: bool,
|
||||
/// Regular expression matching the name of dummy variables.
|
||||
#[clap(long)]
|
||||
dummy_variable_rgx: Option<Regex>,
|
||||
}
|
||||
|
||||
#[cfg(feature = "update-informer")]
|
||||
|
|
@ -265,6 +269,9 @@ fn inner_main() -> Result<ExitCode> {
|
|||
if !cli.extend_ignore.is_empty() {
|
||||
settings.ignore(&cli.extend_ignore);
|
||||
}
|
||||
if let Some(dummy_variable_rgx) = cli.dummy_variable_rgx {
|
||||
settings.dummy_variable_rgx = dummy_variable_rgx;
|
||||
}
|
||||
|
||||
if cli.show_settings && cli.show_files {
|
||||
eprintln!("Error: specify --show-settings or show-files (not both).");
|
||||
|
|
|
|||
|
|
@ -30,6 +30,7 @@ pub struct Config {
|
|||
pub extend_exclude: Option<Vec<String>>,
|
||||
pub select: Option<Vec<CheckCode>>,
|
||||
pub ignore: Option<Vec<CheckCode>>,
|
||||
pub dummy_variable_rgx: Option<String>,
|
||||
}
|
||||
|
||||
#[derive(Debug, PartialEq, Eq, Deserialize)]
|
||||
|
|
@ -130,6 +131,7 @@ mod tests {
|
|||
extend_exclude: None,
|
||||
select: None,
|
||||
ignore: None,
|
||||
dummy_variable_rgx: None,
|
||||
})
|
||||
})
|
||||
);
|
||||
|
|
@ -150,6 +152,7 @@ line-length = 79
|
|||
extend_exclude: None,
|
||||
select: None,
|
||||
ignore: None,
|
||||
dummy_variable_rgx: None,
|
||||
})
|
||||
})
|
||||
);
|
||||
|
|
@ -170,6 +173,7 @@ exclude = ["foo.py"]
|
|||
extend_exclude: None,
|
||||
select: None,
|
||||
ignore: None,
|
||||
dummy_variable_rgx: None,
|
||||
})
|
||||
})
|
||||
);
|
||||
|
|
@ -190,6 +194,7 @@ select = ["E501"]
|
|||
extend_exclude: None,
|
||||
select: Some(vec![CheckCode::E501]),
|
||||
ignore: None,
|
||||
dummy_variable_rgx: None,
|
||||
})
|
||||
})
|
||||
);
|
||||
|
|
@ -210,6 +215,7 @@ ignore = ["E501"]
|
|||
extend_exclude: None,
|
||||
select: None,
|
||||
ignore: Some(vec![CheckCode::E501]),
|
||||
dummy_variable_rgx: None,
|
||||
})
|
||||
})
|
||||
);
|
||||
|
|
@ -274,6 +280,7 @@ other-attribute = 1
|
|||
]),
|
||||
select: None,
|
||||
ignore: None,
|
||||
dummy_variable_rgx: None,
|
||||
}
|
||||
);
|
||||
|
||||
|
|
|
|||
|
|
@ -2,9 +2,10 @@ use std::collections::BTreeSet;
|
|||
use std::hash::{Hash, Hasher};
|
||||
use std::path::{Path, PathBuf};
|
||||
|
||||
use anyhow::Result;
|
||||
use anyhow::{anyhow, Result};
|
||||
use glob::Pattern;
|
||||
use once_cell::sync::Lazy;
|
||||
use regex::Regex;
|
||||
|
||||
use crate::checks::{CheckCode, DEFAULT_CHECK_CODES};
|
||||
use crate::fs;
|
||||
|
|
@ -43,6 +44,7 @@ pub struct Settings {
|
|||
pub exclude: Vec<FilePattern>,
|
||||
pub extend_exclude: Vec<FilePattern>,
|
||||
pub select: BTreeSet<CheckCode>,
|
||||
pub dummy_variable_rgx: Regex,
|
||||
}
|
||||
|
||||
impl Settings {
|
||||
|
|
@ -54,6 +56,7 @@ impl Settings {
|
|||
exclude: vec![],
|
||||
extend_exclude: vec![],
|
||||
select: BTreeSet::from([check_code]),
|
||||
dummy_variable_rgx: DEFAULT_DUMMY_VARIABLE_RGX.clone(),
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -65,6 +68,7 @@ impl Settings {
|
|||
exclude: vec![],
|
||||
extend_exclude: vec![],
|
||||
select: BTreeSet::from_iter(check_codes),
|
||||
dummy_variable_rgx: DEFAULT_DUMMY_VARIABLE_RGX.clone(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -72,6 +76,7 @@ impl Settings {
|
|||
impl Hash for Settings {
|
||||
fn hash<H: Hasher>(&self, state: &mut H) {
|
||||
self.line_length.hash(state);
|
||||
self.dummy_variable_rgx.as_str().hash(state);
|
||||
for value in self.select.iter() {
|
||||
value.hash(state);
|
||||
}
|
||||
|
|
@ -102,6 +107,9 @@ static DEFAULT_EXCLUDE: Lazy<Vec<FilePattern>> = Lazy::new(|| {
|
|||
]
|
||||
});
|
||||
|
||||
static DEFAULT_DUMMY_VARIABLE_RGX: Lazy<Regex> =
|
||||
Lazy::new(|| Regex::new("^(_+|(_+[a-zA-Z0-9_]*[a-zA-Z0-9]+?))$").unwrap());
|
||||
|
||||
impl Settings {
|
||||
pub fn from_pyproject(
|
||||
pyproject: Option<PathBuf>,
|
||||
|
|
@ -133,6 +141,11 @@ impl Settings {
|
|||
} else {
|
||||
BTreeSet::from_iter(DEFAULT_CHECK_CODES)
|
||||
},
|
||||
dummy_variable_rgx: match config.dummy_variable_rgx {
|
||||
Some(pattern) => Regex::new(&pattern)
|
||||
.map_err(|e| anyhow!("Invalid dummy-variable-rgx value: {e}"))?,
|
||||
None => DEFAULT_DUMMY_VARIABLE_RGX.clone(),
|
||||
},
|
||||
pyproject,
|
||||
project_root,
|
||||
};
|
||||
|
|
|
|||
|
|
@ -0,0 +1,47 @@
|
|||
---
|
||||
source: src/linter.rs
|
||||
expression: checks
|
||||
---
|
||||
- kind:
|
||||
UnusedVariable: e
|
||||
location:
|
||||
row: 3
|
||||
column: 1
|
||||
fix: ~
|
||||
- kind:
|
||||
UnusedVariable: foo
|
||||
location:
|
||||
row: 20
|
||||
column: 5
|
||||
fix: ~
|
||||
- kind:
|
||||
UnusedVariable: a
|
||||
location:
|
||||
row: 21
|
||||
column: 6
|
||||
fix: ~
|
||||
- kind:
|
||||
UnusedVariable: b
|
||||
location:
|
||||
row: 21
|
||||
column: 9
|
||||
fix: ~
|
||||
- kind:
|
||||
UnusedVariable: _
|
||||
location:
|
||||
row: 35
|
||||
column: 5
|
||||
fix: ~
|
||||
- kind:
|
||||
UnusedVariable: __
|
||||
location:
|
||||
row: 36
|
||||
column: 5
|
||||
fix: ~
|
||||
- kind:
|
||||
UnusedVariable: _discarded
|
||||
location:
|
||||
row: 37
|
||||
column: 5
|
||||
fix: ~
|
||||
|
||||
Loading…
Reference in New Issue