-
Notifications
You must be signed in to change notification settings - Fork 879
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
## Summary Implement `E115` in the issue #970. Reference to pylint docs: https://pylint.readthedocs.io/en/stable/user_guide/messages/error/nonlocal-and-global.html Throws an error when a variable name is both declared as global and nonlocal ## Test Plan With `nonlocal_and_global.py`
- Loading branch information
1 parent
6123a5b
commit fd26b29
Showing
8 changed files
with
183 additions
and
1 deletion.
There are no files selected for viewing
67 changes: 67 additions & 0 deletions
67
crates/ruff_linter/resources/test/fixtures/pylint/nonlocal_and_global.py
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,67 @@ | ||
# Positive cases | ||
|
||
counter = 0 | ||
|
||
|
||
def count(): | ||
global counter | ||
nonlocal counter | ||
counter += 1 | ||
|
||
|
||
def count(): | ||
counter = 0 | ||
|
||
def count(counter_type): | ||
if counter_type == "nonlocal": | ||
nonlocal counter | ||
counter += 1 | ||
else: | ||
global counter | ||
counter += 1 | ||
|
||
|
||
def count(): | ||
counter = 0 | ||
|
||
def count_twice(): | ||
for i in range(2): | ||
nonlocal counter | ||
counter += 1 | ||
global counter | ||
|
||
|
||
def count(): | ||
nonlocal counter | ||
global counter | ||
counter += 1 | ||
|
||
|
||
# Negative cases | ||
|
||
counter = 0 | ||
|
||
|
||
def count(): | ||
global counter | ||
counter += 1 | ||
|
||
|
||
def count(): | ||
counter = 0 | ||
|
||
def count_local(): | ||
nonlocal counter | ||
counter += 1 | ||
|
||
|
||
def count(): | ||
counter = 0 | ||
|
||
def count_local(): | ||
nonlocal counter | ||
counter += 1 | ||
|
||
def count_global(): | ||
global counter | ||
counter += 1 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
70 changes: 70 additions & 0 deletions
70
crates/ruff_linter/src/rules/pylint/rules/nonlocal_and_global.rs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,70 @@ | ||
use ruff_diagnostics::{Diagnostic, Violation}; | ||
use ruff_macros::{derive_message_formats, violation}; | ||
use ruff_python_ast as ast; | ||
|
||
use crate::checkers::ast::Checker; | ||
|
||
/// ## What it does | ||
/// Checks for variables which are both declared as both `nonlocal` and | ||
/// `global`. | ||
/// | ||
/// ## Why is this bad? | ||
/// A `nonlocal` variable is a variable that is defined in the nearest | ||
/// enclosing scope, but not in the global scope, while a `global` variable is | ||
/// a variable that is defined in the global scope. | ||
/// | ||
/// Declaring a variable as both `nonlocal` and `global` is contradictory and | ||
/// will raise a `SyntaxError`. | ||
/// | ||
/// ## Example | ||
/// ```python | ||
/// counter = 0 | ||
/// | ||
/// | ||
/// def increment(): | ||
/// global counter | ||
/// nonlocal counter | ||
/// counter += 1 | ||
/// ``` | ||
/// | ||
/// Use instead: | ||
/// ```python | ||
/// counter = 0 | ||
/// | ||
/// | ||
/// def increment(): | ||
/// global counter | ||
/// counter += 1 | ||
/// ``` | ||
/// | ||
/// ## References | ||
/// - [Python documentation: The `global` statement](https://docs.python.org/3/reference/simple_stmts.html#the-global-statement) | ||
/// - [Python documentation: The `nonlocal` statement](https://docs.python.org/3/reference/simple_stmts.html#nonlocal) | ||
#[violation] | ||
pub struct NonlocalAndGlobal { | ||
pub(crate) name: String, | ||
} | ||
|
||
impl Violation for NonlocalAndGlobal { | ||
#[derive_message_formats] | ||
fn message(&self) -> String { | ||
let NonlocalAndGlobal { name } = self; | ||
format!("Name `{name}` is both `nonlocal` and `global`") | ||
} | ||
} | ||
|
||
/// E115 | ||
pub(crate) fn nonlocal_and_global(checker: &mut Checker, nonlocal: &ast::StmtNonlocal) { | ||
// Determine whether any of the newly declared `nonlocal` variables are already declared as | ||
// `global`. | ||
for name in &nonlocal.names { | ||
if let Some(global) = checker.semantic().global(name) { | ||
checker.diagnostics.push(Diagnostic::new( | ||
NonlocalAndGlobal { | ||
name: name.to_string(), | ||
}, | ||
global, | ||
)); | ||
} | ||
} | ||
} |
37 changes: 37 additions & 0 deletions
37
...s/pylint/snapshots/ruff_linter__rules__pylint__tests__PLE0115_nonlocal_and_global.py.snap
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,37 @@ | ||
--- | ||
source: crates/ruff_linter/src/rules/pylint/mod.rs | ||
--- | ||
nonlocal_and_global.py:7:12: PLE0115 Name `counter` is both `nonlocal` and `global` | ||
| | ||
6 | def count(): | ||
7 | global counter | ||
| ^^^^^^^ PLE0115 | ||
8 | nonlocal counter | ||
9 | counter += 1 | ||
| | ||
|
||
nonlocal_and_global.py:20:20: PLE0115 Name `counter` is both `nonlocal` and `global` | ||
| | ||
18 | counter += 1 | ||
19 | else: | ||
20 | global counter | ||
| ^^^^^^^ PLE0115 | ||
21 | counter += 1 | ||
| | ||
|
||
nonlocal_and_global.py:31:16: PLE0115 Name `counter` is both `nonlocal` and `global` | ||
| | ||
29 | nonlocal counter | ||
30 | counter += 1 | ||
31 | global counter | ||
| ^^^^^^^ PLE0115 | ||
| | ||
|
||
nonlocal_and_global.py:36:12: PLE0115 Name `counter` is both `nonlocal` and `global` | ||
| | ||
34 | def count(): | ||
35 | nonlocal counter | ||
36 | global counter | ||
| ^^^^^^^ PLE0115 | ||
37 | counter += 1 | ||
| |
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Oops, something went wrong.