[pylint] - implement non-ascii-name (C2401) (#8038)
## Summary Adds [`non-ascii-name` / `C2401`](https://pylint.pycqa.org/en/latest/user_guide/messages/convention/non-ascii-name.html) See #970 ## Test Plan `cargo test` and manually
This commit is contained in:
31
crates/ruff_linter/resources/test/fixtures/pylint/non_ascii_name.py
vendored
Normal file
31
crates/ruff_linter/resources/test/fixtures/pylint/non_ascii_name.py
vendored
Normal file
@@ -0,0 +1,31 @@
|
||||
ápple_count: int = 1 # C2401
|
||||
ápple_count += 2 # C2401
|
||||
ápple_count = 3 # C2401
|
||||
|
||||
(ápple_count for ápple_count in y)
|
||||
|
||||
|
||||
def func(ápple_count):
|
||||
global ápple_count
|
||||
nonlocal ápple_count
|
||||
|
||||
|
||||
def ápple_count():
|
||||
pass
|
||||
|
||||
|
||||
match ápple_count:
|
||||
case ápple_count:
|
||||
pass
|
||||
|
||||
ápple_count: int
|
||||
|
||||
try:
|
||||
1/0
|
||||
except ápple_count:
|
||||
pass
|
||||
|
||||
# OK
|
||||
print(ápple_count)
|
||||
ápple_count == 3
|
||||
apple_count = 4
|
||||
@@ -10,6 +10,7 @@ pub(crate) fn bindings(checker: &mut Checker) {
|
||||
if !checker.any_enabled(&[
|
||||
Rule::InvalidAllFormat,
|
||||
Rule::InvalidAllObject,
|
||||
Rule::NonAsciiName,
|
||||
Rule::UnaliasedCollectionsAbcSetImport,
|
||||
Rule::UnconventionalImportAlias,
|
||||
Rule::UnusedVariable,
|
||||
@@ -49,6 +50,11 @@ pub(crate) fn bindings(checker: &mut Checker) {
|
||||
checker.diagnostics.push(diagnostic);
|
||||
}
|
||||
}
|
||||
if checker.enabled(Rule::NonAsciiName) {
|
||||
if let Some(diagnostic) = pylint::rules::non_ascii_name(binding, checker.locator) {
|
||||
checker.diagnostics.push(diagnostic);
|
||||
}
|
||||
}
|
||||
if checker.enabled(Rule::UnconventionalImportAlias) {
|
||||
if let Some(diagnostic) = flake8_import_conventions::rules::unconventional_import_alias(
|
||||
checker,
|
||||
|
||||
@@ -1325,6 +1325,7 @@ pub(crate) fn statement(stmt: &Stmt, checker: &mut Checker) {
|
||||
}
|
||||
}
|
||||
Stmt::Assign(assign @ ast::StmtAssign { targets, value, .. }) => {
|
||||
checker.enabled(Rule::NonAsciiName);
|
||||
if checker.enabled(Rule::LambdaAssignment) {
|
||||
if let [target] = &targets[..] {
|
||||
pycodestyle::rules::lambda_assignment(checker, target, value, None, stmt);
|
||||
|
||||
@@ -211,6 +211,7 @@ pub fn code_to_rule(linter: Linter, code: &str) -> Option<(RuleGroup, Rule)> {
|
||||
(Pylint, "C0205") => (RuleGroup::Stable, rules::pylint::rules::SingleStringSlots),
|
||||
(Pylint, "C0208") => (RuleGroup::Stable, rules::pylint::rules::IterationOverSet),
|
||||
(Pylint, "C0414") => (RuleGroup::Stable, rules::pylint::rules::UselessImportAlias),
|
||||
(Pylint, "C2401") => (RuleGroup::Preview, rules::pylint::rules::NonAsciiName),
|
||||
(Pylint, "C2403") => (RuleGroup::Preview, rules::pylint::rules::NonAsciiImportName),
|
||||
#[allow(deprecated)]
|
||||
(Pylint, "C1901") => (RuleGroup::Nursery, rules::pylint::rules::CompareToEmptyString),
|
||||
|
||||
@@ -141,6 +141,7 @@ mod tests {
|
||||
#[test_case(Rule::GlobalAtModuleLevel, Path::new("global_at_module_level.py"))]
|
||||
#[test_case(Rule::UnnecessaryLambda, Path::new("unnecessary_lambda.py"))]
|
||||
#[test_case(Rule::NonAsciiImportName, Path::new("non_ascii_module_import.py"))]
|
||||
#[test_case(Rule::NonAsciiName, Path::new("non_ascii_name.py"))]
|
||||
fn rules(rule_code: Rule, path: &Path) -> Result<()> {
|
||||
let snapshot = format!("{}_{}", rule_code.noqa_code(), path.to_string_lossy());
|
||||
let diagnostics = test_path(
|
||||
|
||||
@@ -35,6 +35,7 @@ pub(crate) use named_expr_without_context::*;
|
||||
pub(crate) use nested_min_max::*;
|
||||
pub(crate) use no_self_use::*;
|
||||
pub(crate) use non_ascii_module_import::*;
|
||||
pub(crate) use non_ascii_name::*;
|
||||
pub(crate) use nonlocal_without_binding::*;
|
||||
pub(crate) use property_with_parameters::*;
|
||||
pub(crate) use redefined_loop_name::*;
|
||||
@@ -102,6 +103,7 @@ mod named_expr_without_context;
|
||||
mod nested_min_max;
|
||||
mod no_self_use;
|
||||
mod non_ascii_module_import;
|
||||
mod non_ascii_name;
|
||||
mod nonlocal_without_binding;
|
||||
mod property_with_parameters;
|
||||
mod redefined_loop_name;
|
||||
|
||||
@@ -10,8 +10,8 @@ use crate::checkers::ast::Checker;
|
||||
/// Checks for the use of non-ASCII characters in import statements.
|
||||
///
|
||||
/// ## Why is this bad?
|
||||
/// Pylint discourages the use of non-ASCII characters in symbol names as
|
||||
/// they can cause confusion and compatibility issues.
|
||||
/// The use of non-ASCII characters in import statements can cause confusion
|
||||
/// and compatibility issues (see: [PEP 672]).
|
||||
///
|
||||
/// ## Example
|
||||
/// ```python
|
||||
@@ -28,8 +28,7 @@ use crate::checkers::ast::Checker;
|
||||
/// import bár as bar
|
||||
/// ```
|
||||
///
|
||||
/// ## References
|
||||
/// - [PEP 672](https://peps.python.org/pep-0672/)
|
||||
/// [PEP 672]: https://peps.python.org/pep-0672/
|
||||
#[violation]
|
||||
pub struct NonAsciiImportName {
|
||||
name: String,
|
||||
|
||||
116
crates/ruff_linter/src/rules/pylint/rules/non_ascii_name.rs
Normal file
116
crates/ruff_linter/src/rules/pylint/rules/non_ascii_name.rs
Normal file
@@ -0,0 +1,116 @@
|
||||
use std::fmt;
|
||||
|
||||
use ruff_diagnostics::{Diagnostic, Violation};
|
||||
use ruff_macros::{derive_message_formats, violation};
|
||||
use ruff_python_semantic::{Binding, BindingKind};
|
||||
use ruff_source_file::Locator;
|
||||
use ruff_text_size::Ranged;
|
||||
|
||||
/// ## What it does
|
||||
/// Checks for the use of non-ASCII characters in variable names.
|
||||
///
|
||||
/// ## Why is this bad?
|
||||
/// The use of non-ASCII characters in variable names can cause confusion
|
||||
/// and compatibility issues (see: [PEP 672]).
|
||||
///
|
||||
/// ## Example
|
||||
/// ```python
|
||||
/// ápple_count: int
|
||||
/// ```
|
||||
///
|
||||
/// Use instead:
|
||||
/// ```python
|
||||
/// apple_count: int
|
||||
/// ```
|
||||
///
|
||||
/// [PEP 672]: https://peps.python.org/pep-0672/
|
||||
#[violation]
|
||||
pub struct NonAsciiName {
|
||||
name: String,
|
||||
kind: Kind,
|
||||
}
|
||||
|
||||
impl Violation for NonAsciiName {
|
||||
#[derive_message_formats]
|
||||
fn message(&self) -> String {
|
||||
let Self { name, kind } = self;
|
||||
format!("{kind} name `{name}` contains a non-ASCII character, consider renaming it")
|
||||
}
|
||||
}
|
||||
|
||||
/// PLC2401
|
||||
pub(crate) fn non_ascii_name(binding: &Binding, locator: &Locator) -> Option<Diagnostic> {
|
||||
let name = binding.name(locator);
|
||||
if name.is_ascii() {
|
||||
return None;
|
||||
}
|
||||
|
||||
let kind = match binding.kind {
|
||||
BindingKind::Annotation => Kind::Annotation,
|
||||
BindingKind::Argument => Kind::Argument,
|
||||
BindingKind::NamedExprAssignment => Kind::NamedExprAssignment,
|
||||
BindingKind::UnpackedAssignment => Kind::UnpackedAssignment,
|
||||
BindingKind::Assignment => Kind::Assignment,
|
||||
BindingKind::TypeParam => Kind::TypeParam,
|
||||
BindingKind::LoopVar => Kind::LoopVar,
|
||||
BindingKind::Global => Kind::Global,
|
||||
BindingKind::Nonlocal(_) => Kind::Nonlocal,
|
||||
BindingKind::ClassDefinition(_) => Kind::ClassDefinition,
|
||||
BindingKind::FunctionDefinition(_) => Kind::FunctionDefinition,
|
||||
BindingKind::BoundException => Kind::BoundException,
|
||||
|
||||
BindingKind::Builtin
|
||||
| BindingKind::Export(_)
|
||||
| BindingKind::FutureImport
|
||||
| BindingKind::Import(_)
|
||||
| BindingKind::FromImport(_)
|
||||
| BindingKind::SubmoduleImport(_)
|
||||
| BindingKind::Deletion
|
||||
| BindingKind::UnboundException(_) => {
|
||||
return None;
|
||||
}
|
||||
};
|
||||
|
||||
Some(Diagnostic::new(
|
||||
NonAsciiName {
|
||||
name: name.to_string(),
|
||||
kind,
|
||||
},
|
||||
binding.range(),
|
||||
))
|
||||
}
|
||||
|
||||
#[derive(Debug, PartialEq, Eq)]
|
||||
enum Kind {
|
||||
Annotation,
|
||||
Argument,
|
||||
NamedExprAssignment,
|
||||
UnpackedAssignment,
|
||||
Assignment,
|
||||
TypeParam,
|
||||
LoopVar,
|
||||
Global,
|
||||
Nonlocal,
|
||||
ClassDefinition,
|
||||
FunctionDefinition,
|
||||
BoundException,
|
||||
}
|
||||
|
||||
impl fmt::Display for Kind {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
match self {
|
||||
Kind::Annotation => f.write_str("Annotation"),
|
||||
Kind::Argument => f.write_str("Argument"),
|
||||
Kind::NamedExprAssignment => f.write_str("Variable"),
|
||||
Kind::UnpackedAssignment => f.write_str("Variable"),
|
||||
Kind::Assignment => f.write_str("Variable"),
|
||||
Kind::TypeParam => f.write_str("Type parameter"),
|
||||
Kind::LoopVar => f.write_str("Variable"),
|
||||
Kind::Global => f.write_str("Global"),
|
||||
Kind::Nonlocal => f.write_str("Nonlocal"),
|
||||
Kind::ClassDefinition => f.write_str("Class"),
|
||||
Kind::FunctionDefinition => f.write_str("Function"),
|
||||
Kind::BoundException => f.write_str("Exception"),
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,79 @@
|
||||
---
|
||||
source: crates/ruff_linter/src/rules/pylint/mod.rs
|
||||
---
|
||||
non_ascii_name.py:1:1: PLC2401 Variable name `ápple_count` contains a non-ASCII character, consider renaming it
|
||||
|
|
||||
1 | ápple_count: int = 1 # C2401
|
||||
| ^^^^^^^^^^^ PLC2401
|
||||
2 | ápple_count += 2 # C2401
|
||||
3 | ápple_count = 3 # C2401
|
||||
|
|
||||
|
||||
non_ascii_name.py:2:1: PLC2401 Variable name `ápple_count` contains a non-ASCII character, consider renaming it
|
||||
|
|
||||
1 | ápple_count: int = 1 # C2401
|
||||
2 | ápple_count += 2 # C2401
|
||||
| ^^^^^^^^^^^ PLC2401
|
||||
3 | ápple_count = 3 # C2401
|
||||
|
|
||||
|
||||
non_ascii_name.py:3:1: PLC2401 Variable name `ápple_count` contains a non-ASCII character, consider renaming it
|
||||
|
|
||||
1 | ápple_count: int = 1 # C2401
|
||||
2 | ápple_count += 2 # C2401
|
||||
3 | ápple_count = 3 # C2401
|
||||
| ^^^^^^^^^^^ PLC2401
|
||||
4 |
|
||||
5 | (ápple_count for ápple_count in y)
|
||||
|
|
||||
|
||||
non_ascii_name.py:5:18: PLC2401 Variable name `ápple_count` contains a non-ASCII character, consider renaming it
|
||||
|
|
||||
3 | ápple_count = 3 # C2401
|
||||
4 |
|
||||
5 | (ápple_count for ápple_count in y)
|
||||
| ^^^^^^^^^^^ PLC2401
|
||||
|
|
||||
|
||||
non_ascii_name.py:8:10: PLC2401 Argument name `ápple_count` contains a non-ASCII character, consider renaming it
|
||||
|
|
||||
8 | def func(ápple_count):
|
||||
| ^^^^^^^^^^^ PLC2401
|
||||
9 | global ápple_count
|
||||
10 | nonlocal ápple_count
|
||||
|
|
||||
|
||||
non_ascii_name.py:9:12: PLC2401 Global name `ápple_count` contains a non-ASCII character, consider renaming it
|
||||
|
|
||||
8 | def func(ápple_count):
|
||||
9 | global ápple_count
|
||||
| ^^^^^^^^^^^ PLC2401
|
||||
10 | nonlocal ápple_count
|
||||
|
|
||||
|
||||
non_ascii_name.py:13:5: PLC2401 Function name `ápple_count` contains a non-ASCII character, consider renaming it
|
||||
|
|
||||
13 | def ápple_count():
|
||||
| ^^^^^^^^^^^ PLC2401
|
||||
14 | pass
|
||||
|
|
||||
|
||||
non_ascii_name.py:18:10: PLC2401 Variable name `ápple_count` contains a non-ASCII character, consider renaming it
|
||||
|
|
||||
17 | match ápple_count:
|
||||
18 | case ápple_count:
|
||||
| ^^^^^^^^^^^ PLC2401
|
||||
19 | pass
|
||||
|
|
||||
|
||||
non_ascii_name.py:21:1: PLC2401 Annotation name `ápple_count` contains a non-ASCII character, consider renaming it
|
||||
|
|
||||
19 | pass
|
||||
20 |
|
||||
21 | ápple_count: int
|
||||
| ^^^^^^^^^^^ PLC2401
|
||||
22 |
|
||||
23 | try:
|
||||
|
|
||||
|
||||
|
||||
1
ruff.schema.json
generated
1
ruff.schema.json
generated
@@ -2952,6 +2952,7 @@
|
||||
"PLC2",
|
||||
"PLC24",
|
||||
"PLC240",
|
||||
"PLC2401",
|
||||
"PLC2403",
|
||||
"PLC3",
|
||||
"PLC30",
|
||||
|
||||
Reference in New Issue
Block a user