Implement E0117 (nonlocal-without-binding) (#1180)

This commit is contained in:
Charlie Marsh 2022-12-10 11:41:57 -05:00 committed by GitHub
parent 6f8e0224d0
commit add96d3dc5
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
6 changed files with 81 additions and 4 deletions

View file

@ -0,0 +1,19 @@
nonlocal x
def f():
nonlocal x
def f():
nonlocal y
def f():
x = 1
def f():
nonlocal x
def f():
nonlocal y

View file

@ -305,11 +305,23 @@ where
// Mark the binding in the defining scopes as used too. (Skip the global scope
// and the current scope.)
for name in names {
let mut exists = false;
for index in self.scope_stack.iter().skip(1).rev().skip(1) {
if let Some(index) = self.scopes[*index].values.get(&name.as_str()) {
exists = true;
self.bindings[*index].used = usage;
}
}
// Ensure that every nonlocal has an existing binding from a parent scope.
if !exists {
if self.settings.enabled.contains(&CheckCode::PLE0117) {
self.add_check(Check::new(
CheckKind::NonlocalWithoutBinding(name.to_string()),
Range::from_located(stmt),
));
}
}
}
}

View file

@ -99,6 +99,7 @@ pub enum CheckCode {
PLC0414,
PLC2201,
PLC3002,
PLE0117,
PLE0118,
PLE1142,
PLR0206,
@ -642,6 +643,7 @@ pub enum CheckKind {
ConsiderUsingFromImport(String, String),
GlobalVariableNotAssigned(String),
MisplacedComparisonConstant(String),
NonlocalWithoutBinding(String),
PropertyWithParameters,
UnnecessaryDirectLambdaCall,
UseSysExit(String),
@ -954,6 +956,7 @@ impl CheckCode {
CheckCode::PLC0414 => CheckKind::UselessImportAlias,
CheckCode::PLC2201 => CheckKind::MisplacedComparisonConstant("...".to_string()),
CheckCode::PLC3002 => CheckKind::UnnecessaryDirectLambdaCall,
CheckCode::PLE0117 => CheckKind::NonlocalWithoutBinding("...".to_string()),
CheckCode::PLE0118 => CheckKind::UsedPriorGlobalDeclaration("...".to_string(), 1),
CheckCode::PLE1142 => CheckKind::AwaitOutsideAsync,
CheckCode::PLR0402 => {
@ -1408,6 +1411,7 @@ impl CheckCode {
CheckCode::PLC0414 => CheckCategory::Pylint,
CheckCode::PLC2201 => CheckCategory::Pylint,
CheckCode::PLC3002 => CheckCategory::Pylint,
CheckCode::PLE0117 => CheckCategory::Pylint,
CheckCode::PLE0118 => CheckCategory::Pylint,
CheckCode::PLE1142 => CheckCategory::Pylint,
CheckCode::PLR0206 => CheckCategory::Pylint,
@ -1546,6 +1550,7 @@ impl CheckKind {
CheckKind::PropertyWithParameters => &CheckCode::PLR0206,
CheckKind::UnnecessaryDirectLambdaCall => &CheckCode::PLC3002,
CheckKind::UseSysExit(_) => &CheckCode::PLR1722,
CheckKind::NonlocalWithoutBinding(..) => &CheckCode::PLE0117,
CheckKind::UsedPriorGlobalDeclaration(..) => &CheckCode::PLE0118,
CheckKind::UselessElseOnLoop => &CheckCode::PLW0120,
CheckKind::UselessImportAlias => &CheckCode::PLC0414,
@ -1957,6 +1962,9 @@ impl CheckKind {
CheckKind::MisplacedComparisonConstant(comparison) => {
format!("Comparison should be {comparison}")
}
CheckKind::NonlocalWithoutBinding(name) => {
format!("Nonlocal name `{name}` found without binding")
}
CheckKind::UnnecessaryDirectLambdaCall => "Lambda expression called directly. Execute \
the expression inline instead."
.to_string(),

View file

@ -318,6 +318,7 @@ pub enum CheckCodePrefix {
PLE0,
PLE01,
PLE011,
PLE0117,
PLE0118,
PLE1,
PLE11,
@ -1350,10 +1351,13 @@ impl CheckCodePrefix {
CheckCodePrefix::PLC30 => vec![CheckCode::PLC3002],
CheckCodePrefix::PLC300 => vec![CheckCode::PLC3002],
CheckCodePrefix::PLC3002 => vec![CheckCode::PLC3002],
CheckCodePrefix::PLE => vec![CheckCode::PLE0118, CheckCode::PLE1142],
CheckCodePrefix::PLE0 => vec![CheckCode::PLE0118],
CheckCodePrefix::PLE01 => vec![CheckCode::PLE0118],
CheckCodePrefix::PLE011 => vec![CheckCode::PLE0118],
CheckCodePrefix::PLE => {
vec![CheckCode::PLE0117, CheckCode::PLE0118, CheckCode::PLE1142]
}
CheckCodePrefix::PLE0 => vec![CheckCode::PLE0117, CheckCode::PLE0118],
CheckCodePrefix::PLE01 => vec![CheckCode::PLE0117, CheckCode::PLE0118],
CheckCodePrefix::PLE011 => vec![CheckCode::PLE0117, CheckCode::PLE0118],
CheckCodePrefix::PLE0117 => vec![CheckCode::PLE0117],
CheckCodePrefix::PLE0118 => vec![CheckCode::PLE0118],
CheckCodePrefix::PLE1 => vec![CheckCode::PLE1142],
CheckCodePrefix::PLE11 => vec![CheckCode::PLE1142],
@ -2135,6 +2139,7 @@ impl CheckCodePrefix {
CheckCodePrefix::PLE0 => SuffixLength::One,
CheckCodePrefix::PLE01 => SuffixLength::Two,
CheckCodePrefix::PLE011 => SuffixLength::Three,
CheckCodePrefix::PLE0117 => SuffixLength::Four,
CheckCodePrefix::PLE0118 => SuffixLength::Four,
CheckCodePrefix::PLE1 => SuffixLength::One,
CheckCodePrefix::PLE11 => SuffixLength::Two,

View file

@ -14,6 +14,7 @@ mod tests {
#[test_case(CheckCode::PLC0414, Path::new("import_aliasing.py"); "PLC0414")]
#[test_case(CheckCode::PLC2201, Path::new("misplaced_comparison_constant.py"); "PLC2201")]
#[test_case(CheckCode::PLC3002, Path::new("unnecessary_direct_lambda_call.py"); "PLC3002")]
#[test_case(CheckCode::PLE0117, Path::new("nonlocal_without_binding.py"); "PLE0117")]
#[test_case(CheckCode::PLE0118, Path::new("used_prior_global_declaration.py"); "PLE0118")]
#[test_case(CheckCode::PLE1142, Path::new("await_outside_async.py"); "PLE1142")]
#[test_case(CheckCode::PLR0206, Path::new("property_with_parameters.py"); "PLR0206")]

View file

@ -0,0 +1,32 @@
---
source: src/pylint/mod.rs
expression: checks
---
- kind:
NonlocalWithoutBinding: x
location:
row: 5
column: 4
end_location:
row: 5
column: 14
fix: ~
- kind:
NonlocalWithoutBinding: y
location:
row: 9
column: 4
end_location:
row: 9
column: 14
fix: ~
- kind:
NonlocalWithoutBinding: y
location:
row: 19
column: 8
end_location:
row: 19
column: 18
fix: ~