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