mirror of
https://github.com/astral-sh/ruff.git
synced 2025-10-02 14:52:01 +00:00
Categorize functions in pep8-naming (#624)
This commit is contained in:
parent
cea9e34942
commit
1cd82d588b
8 changed files with 292 additions and 173 deletions
36
resources/test/fixtures/N804.py
vendored
36
resources/test/fixtures/N804.py
vendored
|
@ -1,19 +1,43 @@
|
||||||
|
from abc import ABCMeta
|
||||||
|
|
||||||
|
|
||||||
class Class:
|
class Class:
|
||||||
@classmethod
|
def bad_method(this):
|
||||||
def bad_class_method(this):
|
pass
|
||||||
|
|
||||||
|
if False:
|
||||||
|
|
||||||
|
def extra_bad_method(this):
|
||||||
|
pass
|
||||||
|
|
||||||
|
def good_method(self):
|
||||||
pass
|
pass
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def good_class_method(cls):
|
def class_method(cls):
|
||||||
pass
|
|
||||||
|
|
||||||
def method(self):
|
|
||||||
pass
|
pass
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def static_method(x):
|
def static_method(x):
|
||||||
return x
|
return x
|
||||||
|
|
||||||
|
def __init__(self):
|
||||||
|
...
|
||||||
|
|
||||||
|
def __new__(cls, *args, **kwargs):
|
||||||
|
...
|
||||||
|
|
||||||
|
def __init_subclass__(self, default_name, **kwargs):
|
||||||
|
...
|
||||||
|
|
||||||
|
|
||||||
|
class MetaClass(ABCMeta):
|
||||||
|
def bad_method(self):
|
||||||
|
pass
|
||||||
|
|
||||||
|
def good_method(cls):
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
def func(x):
|
def func(x):
|
||||||
return x
|
return x
|
||||||
|
|
21
resources/test/fixtures/N805.py
vendored
21
resources/test/fixtures/N805.py
vendored
|
@ -1,11 +1,11 @@
|
||||||
import random
|
from abc import ABCMeta
|
||||||
|
|
||||||
|
|
||||||
class Class:
|
class Class:
|
||||||
def bad_method(this):
|
def bad_method(this):
|
||||||
pass
|
pass
|
||||||
|
|
||||||
if random.random(0, 2) == 0:
|
if False:
|
||||||
|
|
||||||
def extra_bad_method(this):
|
def extra_bad_method(this):
|
||||||
pass
|
pass
|
||||||
|
@ -21,6 +21,23 @@ class Class:
|
||||||
def static_method(x):
|
def static_method(x):
|
||||||
return x
|
return x
|
||||||
|
|
||||||
|
def __init__(self):
|
||||||
|
...
|
||||||
|
|
||||||
|
def __new__(cls, *args, **kwargs):
|
||||||
|
...
|
||||||
|
|
||||||
|
def __init_subclass__(self, default_name, **kwargs):
|
||||||
|
...
|
||||||
|
|
||||||
|
|
||||||
|
class MetaClass(ABCMeta):
|
||||||
|
def bad_method(self):
|
||||||
|
pass
|
||||||
|
|
||||||
|
def good_method(cls):
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
def func(x):
|
def func(x):
|
||||||
return x
|
return x
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
use std::collections::BTreeMap;
|
use std::collections::BTreeMap;
|
||||||
use std::sync::atomic::{AtomicUsize, Ordering};
|
use std::sync::atomic::{AtomicUsize, Ordering};
|
||||||
|
|
||||||
|
use rustpython_ast::{Expr, Keyword};
|
||||||
use rustpython_parser::ast::{Located, Location};
|
use rustpython_parser::ast::{Located, Location};
|
||||||
|
|
||||||
fn id() -> usize {
|
fn id() -> usize {
|
||||||
|
@ -30,9 +31,17 @@ pub struct FunctionScope {
|
||||||
pub uses_locals: bool,
|
pub uses_locals: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Debug, Default)]
|
||||||
|
pub struct ClassScope<'a> {
|
||||||
|
pub name: &'a str,
|
||||||
|
pub bases: &'a [Expr],
|
||||||
|
pub keywords: &'a [Keyword],
|
||||||
|
pub decorator_list: &'a [Expr],
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Clone, Debug)]
|
#[derive(Clone, Debug)]
|
||||||
pub enum ScopeKind {
|
pub enum ScopeKind<'a> {
|
||||||
Class,
|
Class(ClassScope<'a>),
|
||||||
Function(FunctionScope),
|
Function(FunctionScope),
|
||||||
Generator,
|
Generator,
|
||||||
Module,
|
Module,
|
||||||
|
@ -40,15 +49,15 @@ pub enum ScopeKind {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Debug)]
|
#[derive(Clone, Debug)]
|
||||||
pub struct Scope {
|
pub struct Scope<'a> {
|
||||||
pub id: usize,
|
pub id: usize,
|
||||||
pub kind: ScopeKind,
|
pub kind: ScopeKind<'a>,
|
||||||
pub import_starred: bool,
|
pub import_starred: bool,
|
||||||
pub values: BTreeMap<String, Binding>,
|
pub values: BTreeMap<String, Binding>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Scope {
|
impl<'a> Scope<'a> {
|
||||||
pub fn new(kind: ScopeKind) -> Self {
|
pub fn new(kind: ScopeKind<'a>) -> Self {
|
||||||
Scope {
|
Scope {
|
||||||
id: id(),
|
id: id(),
|
||||||
kind,
|
kind,
|
||||||
|
|
|
@ -15,8 +15,8 @@ use crate::ast::helpers::{extract_handler_names, match_name_or_attr_from_module}
|
||||||
use crate::ast::operations::extract_all_names;
|
use crate::ast::operations::extract_all_names;
|
||||||
use crate::ast::relocate::relocate_expr;
|
use crate::ast::relocate::relocate_expr;
|
||||||
use crate::ast::types::{
|
use crate::ast::types::{
|
||||||
Binding, BindingContext, BindingKind, CheckLocator, FunctionScope, ImportKind, Range, Scope,
|
Binding, BindingContext, BindingKind, CheckLocator, ClassScope, FunctionScope, ImportKind,
|
||||||
ScopeKind,
|
Range, Scope, ScopeKind,
|
||||||
};
|
};
|
||||||
use crate::ast::visitor::{walk_excepthandler, Visitor};
|
use crate::ast::visitor::{walk_excepthandler, Visitor};
|
||||||
use crate::ast::{helpers, operations, visitor};
|
use crate::ast::{helpers, operations, visitor};
|
||||||
|
@ -66,7 +66,7 @@ pub struct Checker<'a> {
|
||||||
// at various points in time.
|
// at various points in time.
|
||||||
pub(crate) parents: Vec<&'a Stmt>,
|
pub(crate) parents: Vec<&'a Stmt>,
|
||||||
pub(crate) parent_stack: Vec<usize>,
|
pub(crate) parent_stack: Vec<usize>,
|
||||||
scopes: Vec<Scope>,
|
scopes: Vec<Scope<'a>>,
|
||||||
scope_stack: Vec<usize>,
|
scope_stack: Vec<usize>,
|
||||||
dead_scopes: Vec<usize>,
|
dead_scopes: Vec<usize>,
|
||||||
deferred_string_annotations: Vec<(Range, &'a str)>,
|
deferred_string_annotations: Vec<(Range, &'a str)>,
|
||||||
|
@ -274,6 +274,7 @@ where
|
||||||
if let Some(check) =
|
if let Some(check) =
|
||||||
pep8_naming::checks::invalid_first_argument_name_for_class_method(
|
pep8_naming::checks::invalid_first_argument_name_for_class_method(
|
||||||
self.current_scope(),
|
self.current_scope(),
|
||||||
|
name,
|
||||||
decorator_list,
|
decorator_list,
|
||||||
args,
|
args,
|
||||||
&self.settings.pep8_naming,
|
&self.settings.pep8_naming,
|
||||||
|
@ -286,6 +287,7 @@ where
|
||||||
if self.settings.enabled.contains(&CheckCode::N805) {
|
if self.settings.enabled.contains(&CheckCode::N805) {
|
||||||
if let Some(check) = pep8_naming::checks::invalid_first_argument_name_for_method(
|
if let Some(check) = pep8_naming::checks::invalid_first_argument_name_for_method(
|
||||||
self.current_scope(),
|
self.current_scope(),
|
||||||
|
name,
|
||||||
decorator_list,
|
decorator_list,
|
||||||
args,
|
args,
|
||||||
&self.settings.pep8_naming,
|
&self.settings.pep8_naming,
|
||||||
|
@ -296,7 +298,7 @@ where
|
||||||
|
|
||||||
if self.settings.enabled.contains(&CheckCode::N807) {
|
if self.settings.enabled.contains(&CheckCode::N807) {
|
||||||
if let Some(check) =
|
if let Some(check) =
|
||||||
pep8_naming::checks::dunder_function_name(stmt, self.current_scope(), name)
|
pep8_naming::checks::dunder_function_name(self.current_scope(), stmt, name)
|
||||||
{
|
{
|
||||||
self.checks.push(check);
|
self.checks.push(check);
|
||||||
}
|
}
|
||||||
|
@ -360,7 +362,7 @@ where
|
||||||
if self.settings.enabled.contains(&CheckCode::F706) {
|
if self.settings.enabled.contains(&CheckCode::F706) {
|
||||||
if let Some(scope_index) = self.scope_stack.last().cloned() {
|
if let Some(scope_index) = self.scope_stack.last().cloned() {
|
||||||
match self.scopes[scope_index].kind {
|
match self.scopes[scope_index].kind {
|
||||||
ScopeKind::Class | ScopeKind::Module => {
|
ScopeKind::Class(_) | ScopeKind::Module => {
|
||||||
self.checks.push(Check::new(
|
self.checks.push(Check::new(
|
||||||
CheckKind::ReturnOutsideFunction,
|
CheckKind::ReturnOutsideFunction,
|
||||||
self.locate_check(Range::from_located(stmt)),
|
self.locate_check(Range::from_located(stmt)),
|
||||||
|
@ -377,7 +379,6 @@ where
|
||||||
keywords,
|
keywords,
|
||||||
decorator_list,
|
decorator_list,
|
||||||
body,
|
body,
|
||||||
..
|
|
||||||
} => {
|
} => {
|
||||||
if self.settings.enabled.contains(&CheckCode::U004) {
|
if self.settings.enabled.contains(&CheckCode::U004) {
|
||||||
pyupgrade::plugins::useless_object_inheritance(
|
pyupgrade::plugins::useless_object_inheritance(
|
||||||
|
@ -427,7 +428,12 @@ where
|
||||||
for expr in decorator_list {
|
for expr in decorator_list {
|
||||||
self.visit_expr(expr)
|
self.visit_expr(expr)
|
||||||
}
|
}
|
||||||
self.push_scope(Scope::new(ScopeKind::Class))
|
self.push_scope(Scope::new(ScopeKind::Class(ClassScope {
|
||||||
|
name,
|
||||||
|
bases,
|
||||||
|
keywords,
|
||||||
|
decorator_list,
|
||||||
|
})))
|
||||||
}
|
}
|
||||||
StmtKind::Import { names } => {
|
StmtKind::Import { names } => {
|
||||||
if self.settings.enabled.contains(&CheckCode::E402) {
|
if self.settings.enabled.contains(&CheckCode::E402) {
|
||||||
|
@ -1256,7 +1262,7 @@ where
|
||||||
ExprKind::Yield { .. } | ExprKind::YieldFrom { .. } | ExprKind::Await { .. } => {
|
ExprKind::Yield { .. } | ExprKind::YieldFrom { .. } | ExprKind::Await { .. } => {
|
||||||
let scope = self.current_scope();
|
let scope = self.current_scope();
|
||||||
if self.settings.enabled.contains(&CheckCode::F704) {
|
if self.settings.enabled.contains(&CheckCode::F704) {
|
||||||
if matches!(scope.kind, ScopeKind::Class | ScopeKind::Module) {
|
if matches!(scope.kind, ScopeKind::Class(_) | ScopeKind::Module) {
|
||||||
self.checks.push(Check::new(
|
self.checks.push(Check::new(
|
||||||
CheckKind::YieldOutsideFunction,
|
CheckKind::YieldOutsideFunction,
|
||||||
self.locate_check(Range::from_located(expr)),
|
self.locate_check(Range::from_located(expr)),
|
||||||
|
@ -1791,7 +1797,7 @@ impl<'a> Checker<'a> {
|
||||||
.expect("Attempted to pop without scope.");
|
.expect("Attempted to pop without scope.");
|
||||||
}
|
}
|
||||||
|
|
||||||
fn push_scope(&mut self, scope: Scope) {
|
fn push_scope(&mut self, scope: Scope<'a>) {
|
||||||
self.scope_stack.push(self.scopes.len());
|
self.scope_stack.push(self.scopes.len());
|
||||||
self.scopes.push(scope);
|
self.scopes.push(scope);
|
||||||
}
|
}
|
||||||
|
@ -1896,7 +1902,7 @@ impl<'a> Checker<'a> {
|
||||||
let mut import_starred = false;
|
let mut import_starred = false;
|
||||||
for scope_index in self.scope_stack.iter().rev() {
|
for scope_index in self.scope_stack.iter().rev() {
|
||||||
let scope = &mut self.scopes[*scope_index];
|
let scope = &mut self.scopes[*scope_index];
|
||||||
if matches!(scope.kind, ScopeKind::Class) {
|
if matches!(scope.kind, ScopeKind::Class(_)) {
|
||||||
if id == "__class__" {
|
if id == "__class__" {
|
||||||
return;
|
return;
|
||||||
} else if !first_iter && !in_generator {
|
} else if !first_iter && !in_generator {
|
||||||
|
@ -2433,7 +2439,7 @@ impl<'a> Checker<'a> {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn check_builtin_shadowing(&mut self, name: &str, location: Range, is_attribute: bool) {
|
fn check_builtin_shadowing(&mut self, name: &str, location: Range, is_attribute: bool) {
|
||||||
if is_attribute && matches!(self.current_scope().kind, ScopeKind::Class) {
|
if is_attribute && matches!(self.current_scope().kind, ScopeKind::Class(_)) {
|
||||||
if self.settings.enabled.contains(&CheckCode::A003) {
|
if self.settings.enabled.contains(&CheckCode::A003) {
|
||||||
if let Some(check) = flake8_builtins::checks::builtin_shadowing(
|
if let Some(check) = flake8_builtins::checks::builtin_shadowing(
|
||||||
name,
|
name,
|
||||||
|
|
|
@ -1,8 +1,9 @@
|
||||||
use itertools::Itertools;
|
|
||||||
use rustpython_ast::{Arguments, Expr, ExprKind, Stmt};
|
use rustpython_ast::{Arguments, Expr, ExprKind, Stmt};
|
||||||
|
|
||||||
use crate::ast::types::{FunctionScope, Range, Scope, ScopeKind};
|
use crate::ast::types::{FunctionScope, Range, Scope, ScopeKind};
|
||||||
use crate::checks::{Check, CheckKind};
|
use crate::checks::{Check, CheckKind};
|
||||||
|
use crate::pep8_naming::helpers;
|
||||||
|
use crate::pep8_naming::helpers::FunctionType;
|
||||||
use crate::pep8_naming::settings::Settings;
|
use crate::pep8_naming::settings::Settings;
|
||||||
|
|
||||||
/// N801
|
/// N801
|
||||||
|
@ -53,21 +54,15 @@ pub fn invalid_argument_name(name: &str, location: Range) -> Option<Check> {
|
||||||
/// N804
|
/// N804
|
||||||
pub fn invalid_first_argument_name_for_class_method(
|
pub fn invalid_first_argument_name_for_class_method(
|
||||||
scope: &Scope,
|
scope: &Scope,
|
||||||
|
name: &str,
|
||||||
decorator_list: &[Expr],
|
decorator_list: &[Expr],
|
||||||
args: &Arguments,
|
args: &Arguments,
|
||||||
settings: &Settings,
|
settings: &Settings,
|
||||||
) -> Option<Check> {
|
) -> Option<Check> {
|
||||||
if !matches!(scope.kind, ScopeKind::Class) {
|
if matches!(
|
||||||
return None;
|
helpers::function_type(scope, name, decorator_list, settings),
|
||||||
}
|
FunctionType::ClassMethod
|
||||||
|
) {
|
||||||
if decorator_list.iter().any(|decorator| {
|
|
||||||
if let ExprKind::Name { id, .. } = &decorator.node {
|
|
||||||
settings.classmethod_decorators.contains(id)
|
|
||||||
} else {
|
|
||||||
false
|
|
||||||
}
|
|
||||||
}) {
|
|
||||||
if let Some(arg) = args.args.first() {
|
if let Some(arg) = args.args.first() {
|
||||||
if arg.node.arg != "cls" {
|
if arg.node.arg != "cls" {
|
||||||
return Some(Check::new(
|
return Some(Check::new(
|
||||||
|
@ -83,31 +78,22 @@ pub fn invalid_first_argument_name_for_class_method(
|
||||||
/// N805
|
/// N805
|
||||||
pub fn invalid_first_argument_name_for_method(
|
pub fn invalid_first_argument_name_for_method(
|
||||||
scope: &Scope,
|
scope: &Scope,
|
||||||
|
name: &str,
|
||||||
decorator_list: &[Expr],
|
decorator_list: &[Expr],
|
||||||
args: &Arguments,
|
args: &Arguments,
|
||||||
settings: &Settings,
|
settings: &Settings,
|
||||||
) -> Option<Check> {
|
) -> Option<Check> {
|
||||||
if !matches!(scope.kind, ScopeKind::Class) {
|
if matches!(
|
||||||
return None;
|
helpers::function_type(scope, name, decorator_list, settings),
|
||||||
}
|
FunctionType::Method
|
||||||
|
) {
|
||||||
if decorator_list.iter().any(|decorator| {
|
if let Some(arg) = args.args.first() {
|
||||||
if let ExprKind::Name { id, .. } = &decorator.node {
|
if arg.node.arg != "self" {
|
||||||
settings.classmethod_decorators.contains(id)
|
return Some(Check::new(
|
||||||
|| settings.staticmethod_decorators.contains(id)
|
CheckKind::InvalidFirstArgumentNameForMethod,
|
||||||
} else {
|
Range::from_located(arg),
|
||||||
false
|
));
|
||||||
}
|
}
|
||||||
}) {
|
|
||||||
return None;
|
|
||||||
}
|
|
||||||
|
|
||||||
if let Some(arg) = args.args.first() {
|
|
||||||
if arg.node.arg != "self" {
|
|
||||||
return Some(Check::new(
|
|
||||||
CheckKind::InvalidFirstArgumentNameForMethod,
|
|
||||||
Range::from_located(arg),
|
|
||||||
));
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
None
|
None
|
||||||
|
@ -128,18 +114,16 @@ pub fn non_lowercase_variable_in_function(scope: &Scope, expr: &Expr, name: &str
|
||||||
}
|
}
|
||||||
|
|
||||||
/// N807
|
/// N807
|
||||||
pub fn dunder_function_name(func_def: &Stmt, scope: &Scope, name: &str) -> Option<Check> {
|
pub fn dunder_function_name(scope: &Scope, stmt: &Stmt, name: &str) -> Option<Check> {
|
||||||
if matches!(scope.kind, ScopeKind::Class) {
|
if matches!(scope.kind, ScopeKind::Class(_)) {
|
||||||
return None;
|
return None;
|
||||||
}
|
}
|
||||||
|
|
||||||
if name.starts_with("__") && name.ends_with("__") {
|
if name.starts_with("__") && name.ends_with("__") {
|
||||||
return Some(Check::new(
|
return Some(Check::new(
|
||||||
CheckKind::DunderFunctionName,
|
CheckKind::DunderFunctionName,
|
||||||
Range::from_located(func_def),
|
Range::from_located(stmt),
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
|
|
||||||
None
|
None
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -149,7 +133,7 @@ pub fn constant_imported_as_non_constant(
|
||||||
name: &str,
|
name: &str,
|
||||||
asname: &str,
|
asname: &str,
|
||||||
) -> Option<Check> {
|
) -> Option<Check> {
|
||||||
if is_upper(name) && !is_upper(asname) {
|
if helpers::is_upper(name) && !helpers::is_upper(asname) {
|
||||||
return Some(Check::new(
|
return Some(Check::new(
|
||||||
CheckKind::ConstantImportedAsNonConstant(name.to_string(), asname.to_string()),
|
CheckKind::ConstantImportedAsNonConstant(name.to_string(), asname.to_string()),
|
||||||
Range::from_located(import_from),
|
Range::from_located(import_from),
|
||||||
|
@ -164,7 +148,7 @@ pub fn lowercase_imported_as_non_lowercase(
|
||||||
name: &str,
|
name: &str,
|
||||||
asname: &str,
|
asname: &str,
|
||||||
) -> Option<Check> {
|
) -> Option<Check> {
|
||||||
if !is_upper(name) && is_lower(name) && asname.to_lowercase() != asname {
|
if !helpers::is_upper(name) && helpers::is_lower(name) && asname.to_lowercase() != asname {
|
||||||
return Some(Check::new(
|
return Some(Check::new(
|
||||||
CheckKind::LowercaseImportedAsNonLowercase(name.to_string(), asname.to_string()),
|
CheckKind::LowercaseImportedAsNonLowercase(name.to_string(), asname.to_string()),
|
||||||
Range::from_located(import_from),
|
Range::from_located(import_from),
|
||||||
|
@ -179,7 +163,7 @@ pub fn camelcase_imported_as_lowercase(
|
||||||
name: &str,
|
name: &str,
|
||||||
asname: &str,
|
asname: &str,
|
||||||
) -> Option<Check> {
|
) -> Option<Check> {
|
||||||
if is_camelcase(name) && is_lower(asname) {
|
if helpers::is_camelcase(name) && helpers::is_lower(asname) {
|
||||||
return Some(Check::new(
|
return Some(Check::new(
|
||||||
CheckKind::CamelcaseImportedAsLowercase(name.to_string(), asname.to_string()),
|
CheckKind::CamelcaseImportedAsLowercase(name.to_string(), asname.to_string()),
|
||||||
Range::from_located(import_from),
|
Range::from_located(import_from),
|
||||||
|
@ -194,7 +178,11 @@ pub fn camelcase_imported_as_constant(
|
||||||
name: &str,
|
name: &str,
|
||||||
asname: &str,
|
asname: &str,
|
||||||
) -> Option<Check> {
|
) -> Option<Check> {
|
||||||
if is_camelcase(name) && !is_lower(asname) && is_upper(asname) && !is_acronym(name, asname) {
|
if helpers::is_camelcase(name)
|
||||||
|
&& !helpers::is_lower(asname)
|
||||||
|
&& helpers::is_upper(asname)
|
||||||
|
&& !helpers::is_acronym(name, asname)
|
||||||
|
{
|
||||||
return Some(Check::new(
|
return Some(Check::new(
|
||||||
CheckKind::CamelcaseImportedAsConstant(name.to_string(), asname.to_string()),
|
CheckKind::CamelcaseImportedAsConstant(name.to_string(), asname.to_string()),
|
||||||
Range::from_located(import_from),
|
Range::from_located(import_from),
|
||||||
|
@ -205,10 +193,10 @@ pub fn camelcase_imported_as_constant(
|
||||||
|
|
||||||
/// N815
|
/// N815
|
||||||
pub fn mixed_case_variable_in_class_scope(scope: &Scope, expr: &Expr, name: &str) -> Option<Check> {
|
pub fn mixed_case_variable_in_class_scope(scope: &Scope, expr: &Expr, name: &str) -> Option<Check> {
|
||||||
if !matches!(scope.kind, ScopeKind::Class) {
|
if !matches!(scope.kind, ScopeKind::Class(_)) {
|
||||||
return None;
|
return None;
|
||||||
}
|
}
|
||||||
if is_mixed_case(name) {
|
if helpers::is_mixed_case(name) {
|
||||||
return Some(Check::new(
|
return Some(Check::new(
|
||||||
CheckKind::MixedCaseVariableInClassScope(name.to_string()),
|
CheckKind::MixedCaseVariableInClassScope(name.to_string()),
|
||||||
Range::from_located(expr),
|
Range::from_located(expr),
|
||||||
|
@ -226,7 +214,7 @@ pub fn mixed_case_variable_in_global_scope(
|
||||||
if !matches!(scope.kind, ScopeKind::Module) {
|
if !matches!(scope.kind, ScopeKind::Module) {
|
||||||
return None;
|
return None;
|
||||||
}
|
}
|
||||||
if is_mixed_case(name) {
|
if helpers::is_mixed_case(name) {
|
||||||
return Some(Check::new(
|
return Some(Check::new(
|
||||||
CheckKind::MixedCaseVariableInGlobalScope(name.to_string()),
|
CheckKind::MixedCaseVariableInGlobalScope(name.to_string()),
|
||||||
Range::from_located(expr),
|
Range::from_located(expr),
|
||||||
|
@ -241,7 +229,11 @@ pub fn camelcase_imported_as_acronym(
|
||||||
name: &str,
|
name: &str,
|
||||||
asname: &str,
|
asname: &str,
|
||||||
) -> Option<Check> {
|
) -> Option<Check> {
|
||||||
if is_camelcase(name) && !is_lower(asname) && is_upper(asname) && is_acronym(name, asname) {
|
if helpers::is_camelcase(name)
|
||||||
|
&& !helpers::is_lower(asname)
|
||||||
|
&& helpers::is_upper(asname)
|
||||||
|
&& helpers::is_acronym(name, asname)
|
||||||
|
{
|
||||||
return Some(Check::new(
|
return Some(Check::new(
|
||||||
CheckKind::CamelcaseImportedAsAcronym(name.to_string(), asname.to_string()),
|
CheckKind::CamelcaseImportedAsAcronym(name.to_string(), asname.to_string()),
|
||||||
Range::from_located(import_from),
|
Range::from_located(import_from),
|
||||||
|
@ -272,101 +264,3 @@ pub fn error_suffix_on_exception_name(
|
||||||
}
|
}
|
||||||
None
|
None
|
||||||
}
|
}
|
||||||
|
|
||||||
fn is_lower(s: &str) -> bool {
|
|
||||||
let mut cased = false;
|
|
||||||
for c in s.chars() {
|
|
||||||
if c.is_uppercase() {
|
|
||||||
return false;
|
|
||||||
} else if !cased && c.is_lowercase() {
|
|
||||||
cased = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
cased
|
|
||||||
}
|
|
||||||
|
|
||||||
fn is_upper(s: &str) -> bool {
|
|
||||||
let mut cased = false;
|
|
||||||
for c in s.chars() {
|
|
||||||
if c.is_lowercase() {
|
|
||||||
return false;
|
|
||||||
} else if !cased && c.is_uppercase() {
|
|
||||||
cased = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
cased
|
|
||||||
}
|
|
||||||
|
|
||||||
fn is_camelcase(name: &str) -> bool {
|
|
||||||
!is_lower(name) && !is_upper(name) && !name.contains('_')
|
|
||||||
}
|
|
||||||
|
|
||||||
fn is_mixed_case(name: &str) -> bool {
|
|
||||||
!is_lower(name)
|
|
||||||
&& name
|
|
||||||
.strip_prefix('_')
|
|
||||||
.unwrap_or(name)
|
|
||||||
.chars()
|
|
||||||
.next()
|
|
||||||
.map_or_else(|| false, |c| c.is_lowercase())
|
|
||||||
}
|
|
||||||
|
|
||||||
fn is_acronym(name: &str, asname: &str) -> bool {
|
|
||||||
name.chars().filter(|c| c.is_uppercase()).join("") == asname
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(test)]
|
|
||||||
mod tests {
|
|
||||||
use super::{is_acronym, is_camelcase, is_lower, is_mixed_case, is_upper};
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_is_lower() -> () {
|
|
||||||
assert!(is_lower("abc"));
|
|
||||||
assert!(is_lower("a_b_c"));
|
|
||||||
assert!(is_lower("a2c"));
|
|
||||||
assert!(!is_lower("aBc"));
|
|
||||||
assert!(!is_lower("ABC"));
|
|
||||||
assert!(!is_lower(""));
|
|
||||||
assert!(!is_lower("_"));
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_is_upper() -> () {
|
|
||||||
assert!(is_upper("ABC"));
|
|
||||||
assert!(is_upper("A_B_C"));
|
|
||||||
assert!(is_upper("A2C"));
|
|
||||||
assert!(!is_upper("aBc"));
|
|
||||||
assert!(!is_upper("abc"));
|
|
||||||
assert!(!is_upper(""));
|
|
||||||
assert!(!is_upper("_"));
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_is_camelcase() -> () {
|
|
||||||
assert!(is_camelcase("Camel"));
|
|
||||||
assert!(is_camelcase("CamelCase"));
|
|
||||||
assert!(!is_camelcase("camel"));
|
|
||||||
assert!(!is_camelcase("camel_case"));
|
|
||||||
assert!(!is_camelcase("CAMEL"));
|
|
||||||
assert!(!is_camelcase("CAMEL_CASE"));
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_is_mixed_case() -> () {
|
|
||||||
assert!(is_mixed_case("mixedCase"));
|
|
||||||
assert!(is_mixed_case("mixed_Case"));
|
|
||||||
assert!(is_mixed_case("_mixed_Case"));
|
|
||||||
assert!(!is_mixed_case("mixed_case"));
|
|
||||||
assert!(!is_mixed_case("MIXED_CASE"));
|
|
||||||
assert!(!is_mixed_case(""));
|
|
||||||
assert!(!is_mixed_case("_"));
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_is_acronym() -> () {
|
|
||||||
assert!(is_acronym("AB", "AB"));
|
|
||||||
assert!(is_acronym("AbcDef", "AD"));
|
|
||||||
assert!(!is_acronym("AbcDef", "Ad"));
|
|
||||||
assert!(!is_acronym("AbcDef", "AB"));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
160
src/pep8_naming/helpers.rs
Normal file
160
src/pep8_naming/helpers.rs
Normal file
|
@ -0,0 +1,160 @@
|
||||||
|
use itertools::Itertools;
|
||||||
|
use rustpython_ast::{Expr, ExprKind};
|
||||||
|
|
||||||
|
use crate::ast::helpers::match_name_or_attr;
|
||||||
|
use crate::ast::types::{Scope, ScopeKind};
|
||||||
|
use crate::pep8_naming::settings::Settings;
|
||||||
|
|
||||||
|
const CLASS_METHODS: [&str; 3] = ["__new__", "__init_subclass__", "__class_getitem__"];
|
||||||
|
const METACLASS_BASES: [&str; 2] = ["type", "ABCMeta"];
|
||||||
|
|
||||||
|
pub enum FunctionType {
|
||||||
|
Function,
|
||||||
|
Method,
|
||||||
|
ClassMethod,
|
||||||
|
StaticMethod,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Classify a function based on its scope, name, and decorators.
|
||||||
|
pub fn function_type(
|
||||||
|
scope: &Scope,
|
||||||
|
name: &str,
|
||||||
|
decorator_list: &[Expr],
|
||||||
|
settings: &Settings,
|
||||||
|
) -> FunctionType {
|
||||||
|
if let ScopeKind::Class(scope) = &scope.kind {
|
||||||
|
// Special-case class method, like `__new__`.
|
||||||
|
if CLASS_METHODS.contains(&name)
|
||||||
|
// The class itself extends a known metaclass, so all methods are class methods.
|
||||||
|
|| scope.bases.iter().any(|expr| {
|
||||||
|
METACLASS_BASES
|
||||||
|
.iter()
|
||||||
|
.any(|target| match_name_or_attr(expr, target))
|
||||||
|
})
|
||||||
|
// The method is decorated with a class method decorator (like `@classmethod`).
|
||||||
|
|| decorator_list.iter().any(|expr| {
|
||||||
|
if let ExprKind::Name { id, .. } = &expr.node {
|
||||||
|
settings.classmethod_decorators.contains(id)
|
||||||
|
} else {
|
||||||
|
false
|
||||||
|
}
|
||||||
|
}) {
|
||||||
|
FunctionType::ClassMethod
|
||||||
|
} else if decorator_list.iter().any(|expr| {
|
||||||
|
if let ExprKind::Name { id, .. } = &expr.node {
|
||||||
|
settings.staticmethod_decorators.contains(id)
|
||||||
|
} else {
|
||||||
|
false
|
||||||
|
}
|
||||||
|
}) {
|
||||||
|
// The method is decorated with a static method decorator (like
|
||||||
|
// `@staticmethod`).
|
||||||
|
FunctionType::StaticMethod
|
||||||
|
} else {
|
||||||
|
// It's an instance method.
|
||||||
|
FunctionType::Method
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
FunctionType::Function
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn is_lower(s: &str) -> bool {
|
||||||
|
let mut cased = false;
|
||||||
|
for c in s.chars() {
|
||||||
|
if c.is_uppercase() {
|
||||||
|
return false;
|
||||||
|
} else if !cased && c.is_lowercase() {
|
||||||
|
cased = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
cased
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn is_upper(s: &str) -> bool {
|
||||||
|
let mut cased = false;
|
||||||
|
for c in s.chars() {
|
||||||
|
if c.is_lowercase() {
|
||||||
|
return false;
|
||||||
|
} else if !cased && c.is_uppercase() {
|
||||||
|
cased = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
cased
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn is_camelcase(name: &str) -> bool {
|
||||||
|
!is_lower(name) && !is_upper(name) && !name.contains('_')
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn is_mixed_case(name: &str) -> bool {
|
||||||
|
!is_lower(name)
|
||||||
|
&& name
|
||||||
|
.strip_prefix('_')
|
||||||
|
.unwrap_or(name)
|
||||||
|
.chars()
|
||||||
|
.next()
|
||||||
|
.map_or_else(|| false, |c| c.is_lowercase())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn is_acronym(name: &str, asname: &str) -> bool {
|
||||||
|
name.chars().filter(|c| c.is_uppercase()).join("") == asname
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
use crate::pep8_naming::helpers::{
|
||||||
|
is_acronym, is_camelcase, is_lower, is_mixed_case, is_upper,
|
||||||
|
};
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_is_lower() -> () {
|
||||||
|
assert!(is_lower("abc"));
|
||||||
|
assert!(is_lower("a_b_c"));
|
||||||
|
assert!(is_lower("a2c"));
|
||||||
|
assert!(!is_lower("aBc"));
|
||||||
|
assert!(!is_lower("ABC"));
|
||||||
|
assert!(!is_lower(""));
|
||||||
|
assert!(!is_lower("_"));
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_is_upper() -> () {
|
||||||
|
assert!(is_upper("ABC"));
|
||||||
|
assert!(is_upper("A_B_C"));
|
||||||
|
assert!(is_upper("A2C"));
|
||||||
|
assert!(!is_upper("aBc"));
|
||||||
|
assert!(!is_upper("abc"));
|
||||||
|
assert!(!is_upper(""));
|
||||||
|
assert!(!is_upper("_"));
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_is_camelcase() -> () {
|
||||||
|
assert!(is_camelcase("Camel"));
|
||||||
|
assert!(is_camelcase("CamelCase"));
|
||||||
|
assert!(!is_camelcase("camel"));
|
||||||
|
assert!(!is_camelcase("camel_case"));
|
||||||
|
assert!(!is_camelcase("CAMEL"));
|
||||||
|
assert!(!is_camelcase("CAMEL_CASE"));
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_is_mixed_case() -> () {
|
||||||
|
assert!(is_mixed_case("mixedCase"));
|
||||||
|
assert!(is_mixed_case("mixed_Case"));
|
||||||
|
assert!(is_mixed_case("_mixed_Case"));
|
||||||
|
assert!(!is_mixed_case("mixed_case"));
|
||||||
|
assert!(!is_mixed_case("MIXED_CASE"));
|
||||||
|
assert!(!is_mixed_case(""));
|
||||||
|
assert!(!is_mixed_case("_"));
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_is_acronym() -> () {
|
||||||
|
assert!(is_acronym("AB", "AB"));
|
||||||
|
assert!(is_acronym("AbcDef", "AD"));
|
||||||
|
assert!(!is_acronym("AbcDef", "Ad"));
|
||||||
|
assert!(!is_acronym("AbcDef", "AB"));
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,2 +1,3 @@
|
||||||
pub mod checks;
|
pub mod checks;
|
||||||
|
mod helpers;
|
||||||
pub mod settings;
|
pub mod settings;
|
||||||
|
|
|
@ -4,10 +4,18 @@ expression: checks
|
||||||
---
|
---
|
||||||
- kind: InvalidFirstArgumentNameForClassMethod
|
- kind: InvalidFirstArgumentNameForClassMethod
|
||||||
location:
|
location:
|
||||||
row: 3
|
row: 30
|
||||||
column: 25
|
column: 26
|
||||||
end_location:
|
end_location:
|
||||||
row: 3
|
row: 30
|
||||||
column: 29
|
column: 30
|
||||||
|
fix: ~
|
||||||
|
- kind: InvalidFirstArgumentNameForClassMethod
|
||||||
|
location:
|
||||||
|
row: 35
|
||||||
|
column: 19
|
||||||
|
end_location:
|
||||||
|
row: 35
|
||||||
|
column: 23
|
||||||
fix: ~
|
fix: ~
|
||||||
|
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue