mirror of https://github.com/astral-sh/ruff
Implement unused argument detection (`ARG`) (#1126)
Detect unused arguments
This commit is contained in:
parent
d698c6123e
commit
bb67fbb73a
50
LICENSE
50
LICENSE
|
|
@ -388,6 +388,56 @@ are:
|
||||||
SOFTWARE.
|
SOFTWARE.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
- flake8-import-conventions, licensed as follows:
|
||||||
|
"""
|
||||||
|
MIT License
|
||||||
|
|
||||||
|
Copyright (c) 2021 João Palmeiro
|
||||||
|
|
||||||
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
of this software and associated documentation files (the "Software"), to deal
|
||||||
|
in the Software without restriction, including without limitation the rights
|
||||||
|
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
copies of the Software, and to permit persons to whom the Software is
|
||||||
|
furnished to do so, subject to the following conditions:
|
||||||
|
|
||||||
|
The above copyright notice and this permission notice shall be included in all
|
||||||
|
copies or substantial portions of the Software.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||||
|
SOFTWARE.
|
||||||
|
"""
|
||||||
|
|
||||||
|
- flake8-unused-arguments, licensed as follows:
|
||||||
|
"""
|
||||||
|
MIT License
|
||||||
|
|
||||||
|
Copyright (c) 2019 Nathan Hoad
|
||||||
|
|
||||||
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
of this software and associated documentation files (the "Software"), to deal
|
||||||
|
in the Software without restriction, including without limitation the rights
|
||||||
|
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
copies of the Software, and to permit persons to whom the Software is
|
||||||
|
furnished to do so, subject to the following conditions:
|
||||||
|
|
||||||
|
The above copyright notice and this permission notice shall be included in all
|
||||||
|
copies or substantial portions of the Software.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||||
|
SOFTWARE.
|
||||||
|
"""
|
||||||
|
|
||||||
- isort, licensed as follows:
|
- isort, licensed as follows:
|
||||||
"""
|
"""
|
||||||
The MIT License (MIT)
|
The MIT License (MIT)
|
||||||
|
|
|
||||||
13
README.md
13
README.md
|
|
@ -90,6 +90,7 @@ of [Conda](https://docs.conda.io/en/latest/):
|
||||||
1. [flake8-quotes (Q)](#flake8-quotes-q)
|
1. [flake8-quotes (Q)](#flake8-quotes-q)
|
||||||
1. [flake8-return (RET)](#flake8-return-ret)
|
1. [flake8-return (RET)](#flake8-return-ret)
|
||||||
1. [flake8-tidy-imports (I25)](#flake8-tidy-imports-i25)
|
1. [flake8-tidy-imports (I25)](#flake8-tidy-imports-i25)
|
||||||
|
1. [flake8-unused-arguments (ARG)](#flake8-unused-arguments-arg)
|
||||||
1. [eradicate (ERA)](#eradicate-era)
|
1. [eradicate (ERA)](#eradicate-era)
|
||||||
1. [pygrep-hooks (PGH)](#pygrep-hooks-pgh)
|
1. [pygrep-hooks (PGH)](#pygrep-hooks-pgh)
|
||||||
1. [Pylint (PLC, PLE, PLR, PLW)](#pylint-plc-ple-plr-plw)
|
1. [Pylint (PLC, PLE, PLR, PLW)](#pylint-plc-ple-plr-plw)
|
||||||
|
|
@ -768,6 +769,18 @@ For more, see [flake8-tidy-imports](https://pypi.org/project/flake8-tidy-imports
|
||||||
| ---- | ---- | ------- | --- |
|
| ---- | ---- | ------- | --- |
|
||||||
| I252 | BannedRelativeImport | Relative imports are banned | |
|
| I252 | BannedRelativeImport | Relative imports are banned | |
|
||||||
|
|
||||||
|
### flake8-unused-arguments (ARG)
|
||||||
|
|
||||||
|
For more, see [flake8-unused-arguments](https://pypi.org/project/flake8-unused-arguments/0.0.12/) on PyPI.
|
||||||
|
|
||||||
|
| Code | Name | Message | Fix |
|
||||||
|
| ---- | ---- | ------- | --- |
|
||||||
|
| ARG001 | UnusedFunctionArgument | Unused function argument: `...` | |
|
||||||
|
| ARG002 | UnusedMethodArgument | Unused method argument: `...` | |
|
||||||
|
| ARG003 | UnusedClassMethodArgument | Unused class method argument: `...` | |
|
||||||
|
| ARG004 | UnusedStaticMethodArgument | Unused static method argument: `...` | |
|
||||||
|
| ARG005 | UnusedLambdaArgument | Unused lambda argument: `...` | |
|
||||||
|
|
||||||
### eradicate (ERA)
|
### eradicate (ERA)
|
||||||
|
|
||||||
For more, see [eradicate](https://pypi.org/project/eradicate/2.1.0/) on PyPI.
|
For more, see [eradicate](https://pypi.org/project/eradicate/2.1.0/) on PyPI.
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,137 @@
|
||||||
|
from abc import abstractmethod
|
||||||
|
from typing_extensions import override
|
||||||
|
|
||||||
|
|
||||||
|
###
|
||||||
|
# Unused arguments on functions.
|
||||||
|
###
|
||||||
|
def f(self, x):
|
||||||
|
print("Hello, world!")
|
||||||
|
|
||||||
|
|
||||||
|
def f(cls, x):
|
||||||
|
print("Hello, world!")
|
||||||
|
|
||||||
|
|
||||||
|
def f(self, x):
|
||||||
|
...
|
||||||
|
|
||||||
|
|
||||||
|
def f(cls, x):
|
||||||
|
...
|
||||||
|
|
||||||
|
|
||||||
|
###
|
||||||
|
# Unused arguments on lambdas.
|
||||||
|
###
|
||||||
|
lambda x: print("Hello, world!")
|
||||||
|
|
||||||
|
|
||||||
|
class X:
|
||||||
|
###
|
||||||
|
# Unused arguments.
|
||||||
|
###
|
||||||
|
def f(self, x):
|
||||||
|
print("Hello, world!")
|
||||||
|
|
||||||
|
def f(self, /, x):
|
||||||
|
print("Hello, world!")
|
||||||
|
|
||||||
|
def f(cls, x):
|
||||||
|
print("Hello, world!")
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def f(cls, x):
|
||||||
|
print("Hello, world!")
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def f(cls, x):
|
||||||
|
print("Hello, world!")
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def f(x):
|
||||||
|
print("Hello, world!")
|
||||||
|
|
||||||
|
###
|
||||||
|
# Unused arguments attached to empty functions (OK).
|
||||||
|
###
|
||||||
|
def f(self, x):
|
||||||
|
...
|
||||||
|
|
||||||
|
def f(self, /, x):
|
||||||
|
...
|
||||||
|
|
||||||
|
def f(cls, x):
|
||||||
|
...
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def f(cls, x):
|
||||||
|
...
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def f(cls, x):
|
||||||
|
...
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def f(x):
|
||||||
|
...
|
||||||
|
|
||||||
|
###
|
||||||
|
# Unused functions attached to abstract methods (OK).
|
||||||
|
###
|
||||||
|
@abstractmethod
|
||||||
|
def f(self, x):
|
||||||
|
print("Hello, world!")
|
||||||
|
|
||||||
|
@abstractmethod
|
||||||
|
def f(self, /, x):
|
||||||
|
print("Hello, world!")
|
||||||
|
|
||||||
|
@abstractmethod
|
||||||
|
def f(cls, x):
|
||||||
|
print("Hello, world!")
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
@abstractmethod
|
||||||
|
def f(cls, x):
|
||||||
|
print("Hello, world!")
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
@abstractmethod
|
||||||
|
def f(cls, x):
|
||||||
|
print("Hello, world!")
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
@abstractmethod
|
||||||
|
def f(x):
|
||||||
|
print("Hello, world!")
|
||||||
|
|
||||||
|
###
|
||||||
|
# Unused functions attached to overrides (OK).
|
||||||
|
###
|
||||||
|
@override
|
||||||
|
def f(self, x):
|
||||||
|
print("Hello, world!")
|
||||||
|
|
||||||
|
@override
|
||||||
|
def f(self, /, x):
|
||||||
|
print("Hello, world!")
|
||||||
|
|
||||||
|
@override
|
||||||
|
def f(cls, x):
|
||||||
|
print("Hello, world!")
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
@override
|
||||||
|
def f(cls, x):
|
||||||
|
print("Hello, world!")
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
@override
|
||||||
|
def f(cls, x):
|
||||||
|
print("Hello, world!")
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
@override
|
||||||
|
def f(x):
|
||||||
|
print("Hello, world!")
|
||||||
|
|
@ -0,0 +1,9 @@
|
||||||
|
use rustpython_ast::{Expr, Stmt, StmtKind};
|
||||||
|
|
||||||
|
pub fn decorator_list(stmt: &Stmt) -> &Vec<Expr> {
|
||||||
|
match &stmt.node {
|
||||||
|
StmtKind::FunctionDef { decorator_list, .. }
|
||||||
|
| StmtKind::AsyncFunctionDef { decorator_list, .. } => decorator_list,
|
||||||
|
_ => panic!("Expected StmtKind::FunctionDef | StmtKind::AsyncFunctionDef"),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,65 @@
|
||||||
|
use rustc_hash::{FxHashMap, FxHashSet};
|
||||||
|
use rustpython_ast::Expr;
|
||||||
|
|
||||||
|
use crate::ast::helpers::{
|
||||||
|
collect_call_paths, dealias_call_path, match_call_path, to_module_and_member,
|
||||||
|
};
|
||||||
|
use crate::ast::types::{Scope, ScopeKind};
|
||||||
|
|
||||||
|
const CLASS_METHODS: [&str; 3] = ["__new__", "__init_subclass__", "__class_getitem__"];
|
||||||
|
const METACLASS_BASES: [(&str, &str); 2] = [("", "type"), ("abc", "ABCMeta")];
|
||||||
|
|
||||||
|
pub enum FunctionType {
|
||||||
|
Function,
|
||||||
|
Method,
|
||||||
|
ClassMethod,
|
||||||
|
StaticMethod,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Classify a function based on its scope, name, and decorators.
|
||||||
|
pub fn classify(
|
||||||
|
scope: &Scope,
|
||||||
|
name: &str,
|
||||||
|
decorator_list: &[Expr],
|
||||||
|
from_imports: &FxHashMap<&str, FxHashSet<&str>>,
|
||||||
|
import_aliases: &FxHashMap<&str, &str>,
|
||||||
|
classmethod_decorators: &[String],
|
||||||
|
staticmethod_decorators: &[String],
|
||||||
|
) -> FunctionType {
|
||||||
|
let ScopeKind::Class(scope) = &scope.kind else {
|
||||||
|
return FunctionType::Function;
|
||||||
|
};
|
||||||
|
// Special-case class method, like `__new__`.
|
||||||
|
if CLASS_METHODS.contains(&name)
|
||||||
|
|| scope.bases.iter().any(|expr| {
|
||||||
|
// The class itself extends a known metaclass, so all methods are class methods.
|
||||||
|
let call_path = dealias_call_path(collect_call_paths(expr), import_aliases);
|
||||||
|
METACLASS_BASES
|
||||||
|
.iter()
|
||||||
|
.any(|(module, member)| match_call_path(&call_path, module, member, from_imports))
|
||||||
|
})
|
||||||
|
|| decorator_list.iter().any(|expr| {
|
||||||
|
// The method is decorated with a class method decorator (like `@classmethod`).
|
||||||
|
let call_path = dealias_call_path(collect_call_paths(expr), import_aliases);
|
||||||
|
classmethod_decorators.iter().any(|decorator| {
|
||||||
|
let (module, member) = to_module_and_member(decorator);
|
||||||
|
match_call_path(&call_path, module, member, from_imports)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
{
|
||||||
|
FunctionType::ClassMethod
|
||||||
|
} else if decorator_list.iter().any(|expr| {
|
||||||
|
// The method is decorated with a static method decorator (like
|
||||||
|
// `@staticmethod`).
|
||||||
|
let call_path = dealias_call_path(collect_call_paths(expr), import_aliases);
|
||||||
|
staticmethod_decorators.iter().any(|decorator| {
|
||||||
|
let (module, member) = to_module_and_member(decorator);
|
||||||
|
match_call_path(&call_path, module, member, from_imports)
|
||||||
|
})
|
||||||
|
}) {
|
||||||
|
FunctionType::StaticMethod
|
||||||
|
} else {
|
||||||
|
// It's an instance method.
|
||||||
|
FunctionType::Method
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -1,3 +1,5 @@
|
||||||
|
pub mod cast;
|
||||||
|
pub mod function_type;
|
||||||
pub mod helpers;
|
pub mod helpers;
|
||||||
pub mod operations;
|
pub mod operations;
|
||||||
pub mod relocate;
|
pub mod relocate;
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,7 @@
|
||||||
use std::sync::atomic::{AtomicUsize, Ordering};
|
use std::sync::atomic::{AtomicUsize, Ordering};
|
||||||
|
|
||||||
use rustc_hash::FxHashMap;
|
use rustc_hash::FxHashMap;
|
||||||
use rustpython_ast::{Expr, Keyword, Stmt};
|
use rustpython_ast::{Arguments, Expr, Keyword, Stmt};
|
||||||
use rustpython_parser::ast::{Located, Location};
|
use rustpython_parser::ast::{Located, Location};
|
||||||
|
|
||||||
fn id() -> usize {
|
fn id() -> usize {
|
||||||
|
|
@ -30,35 +30,49 @@ impl Range {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Debug)]
|
#[derive(Debug)]
|
||||||
pub struct FunctionScope {
|
pub struct FunctionDef<'a> {
|
||||||
|
pub name: &'a str,
|
||||||
|
pub args: &'a Arguments,
|
||||||
|
pub body: &'a [Stmt],
|
||||||
|
pub decorator_list: &'a [Expr],
|
||||||
|
// pub returns: Option<&'a Expr>,
|
||||||
|
// pub type_comment: Option<&'a str>,
|
||||||
|
// TODO(charlie): Create AsyncFunctionDef to mirror the AST.
|
||||||
pub async_: bool,
|
pub async_: bool,
|
||||||
pub uses_locals: bool,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Debug)]
|
#[derive(Debug)]
|
||||||
pub struct ClassScope<'a> {
|
pub struct ClassDef<'a> {
|
||||||
pub name: &'a str,
|
pub name: &'a str,
|
||||||
pub bases: &'a [Expr],
|
pub bases: &'a [Expr],
|
||||||
pub keywords: &'a [Keyword],
|
pub keywords: &'a [Keyword],
|
||||||
|
// pub body: &'a [Stmt],
|
||||||
pub decorator_list: &'a [Expr],
|
pub decorator_list: &'a [Expr],
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Debug)]
|
#[derive(Debug)]
|
||||||
|
pub struct Lambda<'a> {
|
||||||
|
pub args: &'a Arguments,
|
||||||
|
pub body: &'a Expr,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
pub enum ScopeKind<'a> {
|
pub enum ScopeKind<'a> {
|
||||||
Class(ClassScope<'a>),
|
Class(ClassDef<'a>),
|
||||||
Function(FunctionScope),
|
Function(FunctionDef<'a>),
|
||||||
Generator,
|
Generator,
|
||||||
Module,
|
Module,
|
||||||
Arg,
|
Arg,
|
||||||
Lambda,
|
Lambda(Lambda<'a>),
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Debug)]
|
#[derive(Debug)]
|
||||||
pub struct Scope<'a> {
|
pub struct Scope<'a> {
|
||||||
pub id: usize,
|
pub id: usize,
|
||||||
pub kind: ScopeKind<'a>,
|
pub kind: ScopeKind<'a>,
|
||||||
pub import_starred: bool,
|
pub import_starred: bool,
|
||||||
|
pub uses_locals: bool,
|
||||||
pub values: FxHashMap<&'a str, Binding>,
|
pub values: FxHashMap<&'a str, Binding>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -68,6 +82,7 @@ impl<'a> Scope<'a> {
|
||||||
id: id(),
|
id: id(),
|
||||||
kind,
|
kind,
|
||||||
import_starred: false,
|
import_starred: false,
|
||||||
|
uses_locals: false,
|
||||||
values: FxHashMap::default(),
|
values: FxHashMap::default(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
112
src/check_ast.rs
112
src/check_ast.rs
|
|
@ -17,7 +17,8 @@ use crate::ast::helpers::{
|
||||||
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, ClassScope, FunctionScope, Node, Range, Scope, ScopeKind,
|
Binding, BindingContext, BindingKind, ClassDef, FunctionDef, Lambda, Node, 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};
|
||||||
|
|
@ -35,8 +36,9 @@ use crate::visibility::{module_visibility, transition_scope, Modifier, Visibilit
|
||||||
use crate::{
|
use crate::{
|
||||||
docstrings, flake8_2020, flake8_annotations, flake8_bandit, flake8_blind_except,
|
docstrings, flake8_2020, flake8_annotations, flake8_bandit, flake8_blind_except,
|
||||||
flake8_boolean_trap, flake8_bugbear, flake8_builtins, flake8_comprehensions, flake8_debugger,
|
flake8_boolean_trap, flake8_bugbear, flake8_builtins, flake8_comprehensions, flake8_debugger,
|
||||||
flake8_import_conventions, flake8_print, flake8_return, flake8_tidy_imports, mccabe,
|
flake8_import_conventions, flake8_print, flake8_return, flake8_tidy_imports,
|
||||||
pep8_naming, pycodestyle, pydocstyle, pyflakes, pygrep_hooks, pylint, pyupgrade,
|
flake8_unused_arguments, mccabe, pep8_naming, pycodestyle, pydocstyle, pyflakes, pygrep_hooks,
|
||||||
|
pylint, pyupgrade,
|
||||||
};
|
};
|
||||||
|
|
||||||
const GLOBAL_SCOPE_INDEX: usize = 0;
|
const GLOBAL_SCOPE_INDEX: usize = 0;
|
||||||
|
|
@ -71,7 +73,7 @@ pub struct Checker<'a> {
|
||||||
deferred_type_definitions: Vec<(&'a Expr, bool, DeferralContext)>,
|
deferred_type_definitions: Vec<(&'a Expr, bool, DeferralContext)>,
|
||||||
deferred_functions: Vec<(&'a Stmt, DeferralContext, VisibleScope)>,
|
deferred_functions: Vec<(&'a Stmt, DeferralContext, VisibleScope)>,
|
||||||
deferred_lambdas: Vec<(&'a Expr, DeferralContext)>,
|
deferred_lambdas: Vec<(&'a Expr, DeferralContext)>,
|
||||||
deferred_assignments: Vec<usize>,
|
deferred_assignments: Vec<(usize, DeferralContext)>,
|
||||||
// Internal, derivative state.
|
// Internal, derivative state.
|
||||||
visible_scope: VisibleScope,
|
visible_scope: VisibleScope,
|
||||||
in_f_string: Option<Range>,
|
in_f_string: Option<Range>,
|
||||||
|
|
@ -580,7 +582,7 @@ 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(ClassScope {
|
self.push_scope(Scope::new(ScopeKind::Class(ClassDef {
|
||||||
name,
|
name,
|
||||||
bases,
|
bases,
|
||||||
keywords,
|
keywords,
|
||||||
|
|
@ -1406,20 +1408,18 @@ where
|
||||||
}
|
}
|
||||||
Ok(summary) => {
|
Ok(summary) => {
|
||||||
if self.settings.enabled.contains(&CheckCode::F522) {
|
if self.settings.enabled.contains(&CheckCode::F522) {
|
||||||
if let Some(check) =
|
if let Some(check) = pyflakes::checks::string_dot_format_extra_named_arguments(
|
||||||
pyflakes::checks::string_dot_format_extra_named_arguments(
|
&summary, keywords, location,
|
||||||
&summary, keywords, location,
|
)
|
||||||
)
|
|
||||||
{
|
{
|
||||||
self.add_check(check);
|
self.add_check(check);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if self.settings.enabled.contains(&CheckCode::F523) {
|
if self.settings.enabled.contains(&CheckCode::F523) {
|
||||||
if let Some(check) =
|
if let Some(check) = pyflakes::checks::string_dot_format_extra_positional_arguments(
|
||||||
pyflakes::checks::string_dot_format_extra_positional_arguments(
|
&summary, args, location,
|
||||||
&summary, args, location,
|
)
|
||||||
)
|
|
||||||
{
|
{
|
||||||
self.add_check(check);
|
self.add_check(check);
|
||||||
}
|
}
|
||||||
|
|
@ -1486,7 +1486,7 @@ where
|
||||||
.scope_stack
|
.scope_stack
|
||||||
.iter()
|
.iter()
|
||||||
.rev()
|
.rev()
|
||||||
.any(|index| matches!(self.scopes[*index].kind, ScopeKind::Lambda))
|
.any(|index| matches!(self.scopes[*index].kind, ScopeKind::Lambda(..)))
|
||||||
{
|
{
|
||||||
flake8_bugbear::plugins::setattr_with_constant(self, expr, func, args);
|
flake8_bugbear::plugins::setattr_with_constant(self, expr, func, args);
|
||||||
}
|
}
|
||||||
|
|
@ -1747,9 +1747,7 @@ where
|
||||||
if id == "locals" && matches!(ctx, ExprContext::Load) {
|
if id == "locals" && matches!(ctx, ExprContext::Load) {
|
||||||
let scope = &mut self.scopes
|
let scope = &mut self.scopes
|
||||||
[*(self.scope_stack.last().expect("No current scope found"))];
|
[*(self.scope_stack.last().expect("No current scope found"))];
|
||||||
if let ScopeKind::Function(inner) = &mut scope.kind {
|
scope.uses_locals = true;
|
||||||
inner.uses_locals = true;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -2070,7 +2068,7 @@ where
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
ExprKind::Lambda { args, .. } => {
|
ExprKind::Lambda { args, body, .. } => {
|
||||||
// Visit the arguments, but avoid the body, which will be deferred.
|
// Visit the arguments, but avoid the body, which will be deferred.
|
||||||
for arg in &args.posonlyargs {
|
for arg in &args.posonlyargs {
|
||||||
if let Some(expr) = &arg.node.annotation {
|
if let Some(expr) = &arg.node.annotation {
|
||||||
|
|
@ -2103,7 +2101,7 @@ where
|
||||||
for expr in &args.defaults {
|
for expr in &args.defaults {
|
||||||
self.visit_expr(expr);
|
self.visit_expr(expr);
|
||||||
}
|
}
|
||||||
self.push_scope(Scope::new(ScopeKind::Lambda));
|
self.push_scope(Scope::new(ScopeKind::Lambda(Lambda { args, body })));
|
||||||
}
|
}
|
||||||
ExprKind::ListComp { elt, generators } | ExprKind::SetComp { elt, generators } => {
|
ExprKind::ListComp { elt, generators } | ExprKind::SetComp { elt, generators } => {
|
||||||
if self.settings.enabled.contains(&CheckCode::C416) {
|
if self.settings.enabled.contains(&CheckCode::C416) {
|
||||||
|
|
@ -2958,27 +2956,44 @@ impl<'a> Checker<'a> {
|
||||||
|
|
||||||
fn check_deferred_functions(&mut self) {
|
fn check_deferred_functions(&mut self) {
|
||||||
while let Some((stmt, (scopes, parents), visibility)) = self.deferred_functions.pop() {
|
while let Some((stmt, (scopes, parents), visibility)) = self.deferred_functions.pop() {
|
||||||
self.scope_stack = scopes;
|
self.scope_stack = scopes.clone();
|
||||||
self.parent_stack = parents;
|
self.parent_stack = parents.clone();
|
||||||
self.visible_scope = visibility;
|
self.visible_scope = visibility;
|
||||||
self.push_scope(Scope::new(ScopeKind::Function(FunctionScope {
|
|
||||||
async_: matches!(stmt.node, StmtKind::AsyncFunctionDef { .. }),
|
|
||||||
uses_locals: false,
|
|
||||||
})));
|
|
||||||
|
|
||||||
match &stmt.node {
|
match &stmt.node {
|
||||||
StmtKind::FunctionDef { body, args, .. }
|
StmtKind::FunctionDef {
|
||||||
| StmtKind::AsyncFunctionDef { body, args, .. } => {
|
name,
|
||||||
|
body,
|
||||||
|
args,
|
||||||
|
decorator_list,
|
||||||
|
..
|
||||||
|
}
|
||||||
|
| StmtKind::AsyncFunctionDef {
|
||||||
|
name,
|
||||||
|
body,
|
||||||
|
args,
|
||||||
|
decorator_list,
|
||||||
|
..
|
||||||
|
} => {
|
||||||
|
self.push_scope(Scope::new(ScopeKind::Function(FunctionDef {
|
||||||
|
name,
|
||||||
|
body,
|
||||||
|
args,
|
||||||
|
decorator_list,
|
||||||
|
async_: matches!(stmt.node, StmtKind::AsyncFunctionDef { .. }),
|
||||||
|
})));
|
||||||
self.visit_arguments(args);
|
self.visit_arguments(args);
|
||||||
for stmt in body {
|
for stmt in body {
|
||||||
self.visit_stmt(stmt);
|
self.visit_stmt(stmt);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
_ => {}
|
_ => unreachable!("Expected StmtKind::FunctionDef | StmtKind::AsyncFunctionDef"),
|
||||||
}
|
}
|
||||||
|
|
||||||
self.deferred_assignments
|
self.deferred_assignments.push((
|
||||||
.push(*self.scope_stack.last().expect("No current scope found"));
|
*self.scope_stack.last().expect("No current scope found"),
|
||||||
|
(scopes, parents),
|
||||||
|
));
|
||||||
|
|
||||||
self.pop_scope();
|
self.pop_scope();
|
||||||
}
|
}
|
||||||
|
|
@ -2986,25 +3001,29 @@ impl<'a> Checker<'a> {
|
||||||
|
|
||||||
fn check_deferred_lambdas(&mut self) {
|
fn check_deferred_lambdas(&mut self) {
|
||||||
while let Some((expr, (scopes, parents))) = self.deferred_lambdas.pop() {
|
while let Some((expr, (scopes, parents))) = self.deferred_lambdas.pop() {
|
||||||
self.scope_stack = scopes;
|
self.scope_stack = scopes.clone();
|
||||||
self.parent_stack = parents;
|
self.parent_stack = parents.clone();
|
||||||
self.push_scope(Scope::new(ScopeKind::Lambda));
|
|
||||||
|
|
||||||
if let ExprKind::Lambda { args, body } = &expr.node {
|
if let ExprKind::Lambda { args, body } = &expr.node {
|
||||||
|
self.push_scope(Scope::new(ScopeKind::Lambda(Lambda { args, body })));
|
||||||
self.visit_arguments(args);
|
self.visit_arguments(args);
|
||||||
self.visit_expr(body);
|
self.visit_expr(body);
|
||||||
|
} else {
|
||||||
|
unreachable!("Expected ExprKind::Lambda");
|
||||||
}
|
}
|
||||||
|
|
||||||
self.deferred_assignments
|
self.deferred_assignments.push((
|
||||||
.push(*self.scope_stack.last().expect("No current scope found"));
|
*self.scope_stack.last().expect("No current scope found"),
|
||||||
|
(scopes, parents),
|
||||||
|
));
|
||||||
|
|
||||||
self.pop_scope();
|
self.pop_scope();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn check_deferred_assignments(&mut self) {
|
fn check_deferred_assignments(&mut self) {
|
||||||
if self.settings.enabled.contains(&CheckCode::F841) {
|
while let Some((index, (scopes, _parents))) = self.deferred_assignments.pop() {
|
||||||
while let Some(index) = self.deferred_assignments.pop() {
|
if self.settings.enabled.contains(&CheckCode::F841) {
|
||||||
self.add_checks(
|
self.add_checks(
|
||||||
pyflakes::checks::unused_variables(
|
pyflakes::checks::unused_variables(
|
||||||
&self.scopes[index],
|
&self.scopes[index],
|
||||||
|
|
@ -3013,6 +3032,23 @@ impl<'a> Checker<'a> {
|
||||||
.into_iter(),
|
.into_iter(),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
if self.settings.enabled.contains(&CheckCode::ARG001)
|
||||||
|
|| self.settings.enabled.contains(&CheckCode::ARG002)
|
||||||
|
|| self.settings.enabled.contains(&CheckCode::ARG003)
|
||||||
|
|| self.settings.enabled.contains(&CheckCode::ARG004)
|
||||||
|
|| self.settings.enabled.contains(&CheckCode::ARG005)
|
||||||
|
{
|
||||||
|
self.add_checks(
|
||||||
|
flake8_unused_arguments::plugins::unused_arguments(
|
||||||
|
self,
|
||||||
|
&self.scopes[*scopes
|
||||||
|
.last()
|
||||||
|
.expect("Expected parent scope above function scope")],
|
||||||
|
&self.scopes[index],
|
||||||
|
)
|
||||||
|
.into_iter(),
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -3092,7 +3128,7 @@ impl<'a> Checker<'a> {
|
||||||
for (name, binding) in &scope.values {
|
for (name, binding) in &scope.values {
|
||||||
let (BindingKind::Importation(_, full_name, context)
|
let (BindingKind::Importation(_, full_name, context)
|
||||||
| BindingKind::SubmoduleImportation(_, full_name, context)
|
| BindingKind::SubmoduleImportation(_, full_name, context)
|
||||||
| BindingKind::FromImportation(_, full_name, context)) = &binding.kind else { continue };
|
| BindingKind::FromImportation(_, full_name, context)) = &binding.kind else { continue; };
|
||||||
|
|
||||||
// Skip used exports from `__all__`
|
// Skip used exports from `__all__`
|
||||||
if binding.used.is_some()
|
if binding.used.is_some()
|
||||||
|
|
|
||||||
|
|
@ -292,6 +292,12 @@ pub enum CheckCode {
|
||||||
FBT001,
|
FBT001,
|
||||||
FBT002,
|
FBT002,
|
||||||
FBT003,
|
FBT003,
|
||||||
|
// flake8-unused-arguments
|
||||||
|
ARG001,
|
||||||
|
ARG002,
|
||||||
|
ARG003,
|
||||||
|
ARG004,
|
||||||
|
ARG005,
|
||||||
// flake8-import-conventions
|
// flake8-import-conventions
|
||||||
ICN001,
|
ICN001,
|
||||||
// Ruff
|
// Ruff
|
||||||
|
|
@ -326,6 +332,7 @@ pub enum CheckCategory {
|
||||||
Flake8Quotes,
|
Flake8Quotes,
|
||||||
Flake8Return,
|
Flake8Return,
|
||||||
Flake8TidyImports,
|
Flake8TidyImports,
|
||||||
|
Flake8UnusedArguments,
|
||||||
Eradicate,
|
Eradicate,
|
||||||
PygrepHooks,
|
PygrepHooks,
|
||||||
Pylint,
|
Pylint,
|
||||||
|
|
@ -364,6 +371,7 @@ impl CheckCategory {
|
||||||
CheckCategory::Flake8Quotes => "flake8-quotes",
|
CheckCategory::Flake8Quotes => "flake8-quotes",
|
||||||
CheckCategory::Flake8Return => "flake8-return",
|
CheckCategory::Flake8Return => "flake8-return",
|
||||||
CheckCategory::Flake8TidyImports => "flake8-tidy-imports",
|
CheckCategory::Flake8TidyImports => "flake8-tidy-imports",
|
||||||
|
CheckCategory::Flake8UnusedArguments => "flake8-unused-arguments",
|
||||||
CheckCategory::Isort => "isort",
|
CheckCategory::Isort => "isort",
|
||||||
CheckCategory::McCabe => "mccabe",
|
CheckCategory::McCabe => "mccabe",
|
||||||
CheckCategory::PEP8Naming => "pep8-naming",
|
CheckCategory::PEP8Naming => "pep8-naming",
|
||||||
|
|
@ -393,6 +401,7 @@ impl CheckCategory {
|
||||||
CheckCategory::Flake8Quotes => vec![CheckCodePrefix::Q],
|
CheckCategory::Flake8Quotes => vec![CheckCodePrefix::Q],
|
||||||
CheckCategory::Flake8Return => vec![CheckCodePrefix::RET],
|
CheckCategory::Flake8Return => vec![CheckCodePrefix::RET],
|
||||||
CheckCategory::Flake8TidyImports => vec![CheckCodePrefix::I25],
|
CheckCategory::Flake8TidyImports => vec![CheckCodePrefix::I25],
|
||||||
|
CheckCategory::Flake8UnusedArguments => vec![CheckCodePrefix::ARG],
|
||||||
CheckCategory::Isort => vec![CheckCodePrefix::I00],
|
CheckCategory::Isort => vec![CheckCodePrefix::I00],
|
||||||
CheckCategory::McCabe => vec![CheckCodePrefix::C90],
|
CheckCategory::McCabe => vec![CheckCodePrefix::C90],
|
||||||
CheckCategory::PEP8Naming => vec![CheckCodePrefix::N],
|
CheckCategory::PEP8Naming => vec![CheckCodePrefix::N],
|
||||||
|
|
@ -470,6 +479,10 @@ impl CheckCategory {
|
||||||
"https://pypi.org/project/flake8-tidy-imports/4.8.0/",
|
"https://pypi.org/project/flake8-tidy-imports/4.8.0/",
|
||||||
&Platform::PyPI,
|
&Platform::PyPI,
|
||||||
)),
|
)),
|
||||||
|
CheckCategory::Flake8UnusedArguments => Some((
|
||||||
|
"https://pypi.org/project/flake8-unused-arguments/0.0.12/",
|
||||||
|
&Platform::PyPI,
|
||||||
|
)),
|
||||||
CheckCategory::Isort => {
|
CheckCategory::Isort => {
|
||||||
Some(("https://pypi.org/project/isort/5.10.1/", &Platform::PyPI))
|
Some(("https://pypi.org/project/isort/5.10.1/", &Platform::PyPI))
|
||||||
}
|
}
|
||||||
|
|
@ -817,6 +830,12 @@ pub enum CheckKind {
|
||||||
BooleanPositionalValueInFunctionCall,
|
BooleanPositionalValueInFunctionCall,
|
||||||
// pygrep-hooks
|
// pygrep-hooks
|
||||||
NoEval,
|
NoEval,
|
||||||
|
// flake8-unused-arguments
|
||||||
|
UnusedFunctionArgument(String),
|
||||||
|
UnusedMethodArgument(String),
|
||||||
|
UnusedClassMethodArgument(String),
|
||||||
|
UnusedStaticMethodArgument(String),
|
||||||
|
UnusedLambdaArgument(String),
|
||||||
// flake8-import-conventions
|
// flake8-import-conventions
|
||||||
ImportAliasIsNotConventional(String, String),
|
ImportAliasIsNotConventional(String, String),
|
||||||
// Ruff
|
// Ruff
|
||||||
|
|
@ -1159,6 +1178,12 @@ impl CheckCode {
|
||||||
CheckCode::FBT003 => CheckKind::BooleanPositionalValueInFunctionCall,
|
CheckCode::FBT003 => CheckKind::BooleanPositionalValueInFunctionCall,
|
||||||
// pygrep-hooks
|
// pygrep-hooks
|
||||||
CheckCode::PGH001 => CheckKind::NoEval,
|
CheckCode::PGH001 => CheckKind::NoEval,
|
||||||
|
// flake8-unused-arguments
|
||||||
|
CheckCode::ARG001 => CheckKind::UnusedFunctionArgument("...".to_string()),
|
||||||
|
CheckCode::ARG002 => CheckKind::UnusedMethodArgument("...".to_string()),
|
||||||
|
CheckCode::ARG003 => CheckKind::UnusedClassMethodArgument("...".to_string()),
|
||||||
|
CheckCode::ARG004 => CheckKind::UnusedStaticMethodArgument("...".to_string()),
|
||||||
|
CheckCode::ARG005 => CheckKind::UnusedLambdaArgument("...".to_string()),
|
||||||
// flake8-import-conventions
|
// flake8-import-conventions
|
||||||
CheckCode::ICN001 => {
|
CheckCode::ICN001 => {
|
||||||
CheckKind::ImportAliasIsNotConventional("...".to_string(), "...".to_string())
|
CheckKind::ImportAliasIsNotConventional("...".to_string(), "...".to_string())
|
||||||
|
|
@ -1188,6 +1213,11 @@ impl CheckCode {
|
||||||
CheckCode::ANN205 => CheckCategory::Flake8Annotations,
|
CheckCode::ANN205 => CheckCategory::Flake8Annotations,
|
||||||
CheckCode::ANN206 => CheckCategory::Flake8Annotations,
|
CheckCode::ANN206 => CheckCategory::Flake8Annotations,
|
||||||
CheckCode::ANN401 => CheckCategory::Flake8Annotations,
|
CheckCode::ANN401 => CheckCategory::Flake8Annotations,
|
||||||
|
CheckCode::ARG001 => CheckCategory::Flake8UnusedArguments,
|
||||||
|
CheckCode::ARG002 => CheckCategory::Flake8UnusedArguments,
|
||||||
|
CheckCode::ARG003 => CheckCategory::Flake8UnusedArguments,
|
||||||
|
CheckCode::ARG004 => CheckCategory::Flake8UnusedArguments,
|
||||||
|
CheckCode::ARG005 => CheckCategory::Flake8UnusedArguments,
|
||||||
CheckCode::B002 => CheckCategory::Flake8Bugbear,
|
CheckCode::B002 => CheckCategory::Flake8Bugbear,
|
||||||
CheckCode::B003 => CheckCategory::Flake8Bugbear,
|
CheckCode::B003 => CheckCategory::Flake8Bugbear,
|
||||||
CheckCode::B004 => CheckCategory::Flake8Bugbear,
|
CheckCode::B004 => CheckCategory::Flake8Bugbear,
|
||||||
|
|
@ -1339,8 +1369,8 @@ impl CheckCode {
|
||||||
CheckCode::FBT002 => CheckCategory::Flake8BooleanTrap,
|
CheckCode::FBT002 => CheckCategory::Flake8BooleanTrap,
|
||||||
CheckCode::FBT003 => CheckCategory::Flake8BooleanTrap,
|
CheckCode::FBT003 => CheckCategory::Flake8BooleanTrap,
|
||||||
CheckCode::I001 => CheckCategory::Isort,
|
CheckCode::I001 => CheckCategory::Isort,
|
||||||
CheckCode::ICN001 => CheckCategory::Flake8ImportConventions,
|
|
||||||
CheckCode::I252 => CheckCategory::Flake8TidyImports,
|
CheckCode::I252 => CheckCategory::Flake8TidyImports,
|
||||||
|
CheckCode::ICN001 => CheckCategory::Flake8ImportConventions,
|
||||||
CheckCode::N801 => CheckCategory::PEP8Naming,
|
CheckCode::N801 => CheckCategory::PEP8Naming,
|
||||||
CheckCode::N802 => CheckCategory::PEP8Naming,
|
CheckCode::N802 => CheckCategory::PEP8Naming,
|
||||||
CheckCode::N803 => CheckCategory::PEP8Naming,
|
CheckCode::N803 => CheckCategory::PEP8Naming,
|
||||||
|
|
@ -1686,6 +1716,12 @@ impl CheckKind {
|
||||||
CheckKind::BooleanPositionalValueInFunctionCall => &CheckCode::FBT003,
|
CheckKind::BooleanPositionalValueInFunctionCall => &CheckCode::FBT003,
|
||||||
// pygrep-hooks
|
// pygrep-hooks
|
||||||
CheckKind::NoEval => &CheckCode::PGH001,
|
CheckKind::NoEval => &CheckCode::PGH001,
|
||||||
|
// flake8-unused-arguments
|
||||||
|
CheckKind::UnusedFunctionArgument(..) => &CheckCode::ARG001,
|
||||||
|
CheckKind::UnusedMethodArgument(..) => &CheckCode::ARG002,
|
||||||
|
CheckKind::UnusedClassMethodArgument(..) => &CheckCode::ARG003,
|
||||||
|
CheckKind::UnusedStaticMethodArgument(..) => &CheckCode::ARG004,
|
||||||
|
CheckKind::UnusedLambdaArgument(..) => &CheckCode::ARG005,
|
||||||
// flake8-import-conventions
|
// flake8-import-conventions
|
||||||
CheckKind::ImportAliasIsNotConventional(..) => &CheckCode::ICN001,
|
CheckKind::ImportAliasIsNotConventional(..) => &CheckCode::ICN001,
|
||||||
// Ruff
|
// Ruff
|
||||||
|
|
@ -2477,6 +2513,18 @@ impl CheckKind {
|
||||||
}
|
}
|
||||||
// pygrep-hooks
|
// pygrep-hooks
|
||||||
CheckKind::NoEval => "No builtin `eval()` allowed".to_string(),
|
CheckKind::NoEval => "No builtin `eval()` allowed".to_string(),
|
||||||
|
// flake8-unused-arguments
|
||||||
|
CheckKind::UnusedFunctionArgument(name) => {
|
||||||
|
format!("Unused function argument: `{name}`")
|
||||||
|
}
|
||||||
|
CheckKind::UnusedMethodArgument(name) => format!("Unused method argument: `{name}`"),
|
||||||
|
CheckKind::UnusedClassMethodArgument(name) => {
|
||||||
|
format!("Unused class method argument: `{name}`")
|
||||||
|
}
|
||||||
|
CheckKind::UnusedStaticMethodArgument(name) => {
|
||||||
|
format!("Unused static method argument: `{name}`")
|
||||||
|
}
|
||||||
|
CheckKind::UnusedLambdaArgument(name) => format!("Unused lambda argument: `{name}`"),
|
||||||
// flake8-import-conventions
|
// flake8-import-conventions
|
||||||
CheckKind::ImportAliasIsNotConventional(name, asname) => {
|
CheckKind::ImportAliasIsNotConventional(name, asname) => {
|
||||||
format!("`{name}` should be imported as `{asname}`")
|
format!("`{name}` should be imported as `{asname}`")
|
||||||
|
|
|
||||||
|
|
@ -36,6 +36,14 @@ pub enum CheckCodePrefix {
|
||||||
ANN4,
|
ANN4,
|
||||||
ANN40,
|
ANN40,
|
||||||
ANN401,
|
ANN401,
|
||||||
|
ARG,
|
||||||
|
ARG0,
|
||||||
|
ARG00,
|
||||||
|
ARG001,
|
||||||
|
ARG002,
|
||||||
|
ARG003,
|
||||||
|
ARG004,
|
||||||
|
ARG005,
|
||||||
B,
|
B,
|
||||||
B0,
|
B0,
|
||||||
B00,
|
B00,
|
||||||
|
|
@ -492,6 +500,32 @@ impl CheckCodePrefix {
|
||||||
CheckCodePrefix::ANN4 => vec![CheckCode::ANN401],
|
CheckCodePrefix::ANN4 => vec![CheckCode::ANN401],
|
||||||
CheckCodePrefix::ANN40 => vec![CheckCode::ANN401],
|
CheckCodePrefix::ANN40 => vec![CheckCode::ANN401],
|
||||||
CheckCodePrefix::ANN401 => vec![CheckCode::ANN401],
|
CheckCodePrefix::ANN401 => vec![CheckCode::ANN401],
|
||||||
|
CheckCodePrefix::ARG => vec![
|
||||||
|
CheckCode::ARG001,
|
||||||
|
CheckCode::ARG002,
|
||||||
|
CheckCode::ARG003,
|
||||||
|
CheckCode::ARG004,
|
||||||
|
CheckCode::ARG005,
|
||||||
|
],
|
||||||
|
CheckCodePrefix::ARG0 => vec![
|
||||||
|
CheckCode::ARG001,
|
||||||
|
CheckCode::ARG002,
|
||||||
|
CheckCode::ARG003,
|
||||||
|
CheckCode::ARG004,
|
||||||
|
CheckCode::ARG005,
|
||||||
|
],
|
||||||
|
CheckCodePrefix::ARG00 => vec![
|
||||||
|
CheckCode::ARG001,
|
||||||
|
CheckCode::ARG002,
|
||||||
|
CheckCode::ARG003,
|
||||||
|
CheckCode::ARG004,
|
||||||
|
CheckCode::ARG005,
|
||||||
|
],
|
||||||
|
CheckCodePrefix::ARG001 => vec![CheckCode::ARG001],
|
||||||
|
CheckCodePrefix::ARG002 => vec![CheckCode::ARG002],
|
||||||
|
CheckCodePrefix::ARG003 => vec![CheckCode::ARG003],
|
||||||
|
CheckCodePrefix::ARG004 => vec![CheckCode::ARG004],
|
||||||
|
CheckCodePrefix::ARG005 => vec![CheckCode::ARG005],
|
||||||
CheckCodePrefix::B => vec![
|
CheckCodePrefix::B => vec![
|
||||||
CheckCode::B002,
|
CheckCode::B002,
|
||||||
CheckCode::B003,
|
CheckCode::B003,
|
||||||
|
|
@ -1698,6 +1732,14 @@ impl CheckCodePrefix {
|
||||||
CheckCodePrefix::ANN4 => SuffixLength::One,
|
CheckCodePrefix::ANN4 => SuffixLength::One,
|
||||||
CheckCodePrefix::ANN40 => SuffixLength::Two,
|
CheckCodePrefix::ANN40 => SuffixLength::Two,
|
||||||
CheckCodePrefix::ANN401 => SuffixLength::Three,
|
CheckCodePrefix::ANN401 => SuffixLength::Three,
|
||||||
|
CheckCodePrefix::ARG => SuffixLength::Zero,
|
||||||
|
CheckCodePrefix::ARG0 => SuffixLength::One,
|
||||||
|
CheckCodePrefix::ARG00 => SuffixLength::Two,
|
||||||
|
CheckCodePrefix::ARG001 => SuffixLength::Three,
|
||||||
|
CheckCodePrefix::ARG002 => SuffixLength::Three,
|
||||||
|
CheckCodePrefix::ARG003 => SuffixLength::Three,
|
||||||
|
CheckCodePrefix::ARG004 => SuffixLength::Three,
|
||||||
|
CheckCodePrefix::ARG005 => SuffixLength::Three,
|
||||||
CheckCodePrefix::B => SuffixLength::Zero,
|
CheckCodePrefix::B => SuffixLength::Zero,
|
||||||
CheckCodePrefix::B0 => SuffixLength::One,
|
CheckCodePrefix::B0 => SuffixLength::One,
|
||||||
CheckCodePrefix::B00 => SuffixLength::Two,
|
CheckCodePrefix::B00 => SuffixLength::Two,
|
||||||
|
|
@ -2096,6 +2138,7 @@ impl CheckCodePrefix {
|
||||||
pub const CATEGORIES: &[CheckCodePrefix] = &[
|
pub const CATEGORIES: &[CheckCodePrefix] = &[
|
||||||
CheckCodePrefix::A,
|
CheckCodePrefix::A,
|
||||||
CheckCodePrefix::ANN,
|
CheckCodePrefix::ANN,
|
||||||
|
CheckCodePrefix::ARG,
|
||||||
CheckCodePrefix::B,
|
CheckCodePrefix::B,
|
||||||
CheckCodePrefix::BLE,
|
CheckCodePrefix::BLE,
|
||||||
CheckCodePrefix::C,
|
CheckCodePrefix::C,
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,6 @@
|
||||||
use rustpython_ast::{Arguments, Expr, Stmt, StmtKind};
|
use rustpython_ast::{Arguments, Expr, Stmt, StmtKind};
|
||||||
|
|
||||||
|
use crate::ast::cast;
|
||||||
use crate::check_ast::Checker;
|
use crate::check_ast::Checker;
|
||||||
use crate::docstrings::definition::{Definition, DefinitionKind};
|
use crate::docstrings::definition::{Definition, DefinitionKind};
|
||||||
use crate::visibility;
|
use crate::visibility;
|
||||||
|
|
@ -32,7 +33,7 @@ pub fn overloaded_name(checker: &Checker, definition: &Definition) -> Option<Str
|
||||||
| DefinitionKind::NestedFunction(stmt)
|
| DefinitionKind::NestedFunction(stmt)
|
||||||
| DefinitionKind::Method(stmt) = definition.kind
|
| DefinitionKind::Method(stmt) = definition.kind
|
||||||
{
|
{
|
||||||
if visibility::is_overload(checker, stmt) {
|
if visibility::is_overload(checker, cast::decorator_list(stmt)) {
|
||||||
let (name, ..) = match_function_def(stmt);
|
let (name, ..) = match_function_def(stmt);
|
||||||
Some(name.to_string())
|
Some(name.to_string())
|
||||||
} else {
|
} else {
|
||||||
|
|
@ -50,7 +51,7 @@ pub fn is_overload_impl(checker: &Checker, definition: &Definition, overloaded_n
|
||||||
| DefinitionKind::NestedFunction(stmt)
|
| DefinitionKind::NestedFunction(stmt)
|
||||||
| DefinitionKind::Method(stmt) = definition.kind
|
| DefinitionKind::Method(stmt) = definition.kind
|
||||||
{
|
{
|
||||||
if visibility::is_overload(checker, stmt) {
|
if visibility::is_overload(checker, cast::decorator_list(stmt)) {
|
||||||
false
|
false
|
||||||
} else {
|
} else {
|
||||||
let (name, ..) = match_function_def(stmt);
|
let (name, ..) = match_function_def(stmt);
|
||||||
|
|
|
||||||
|
|
@ -1,8 +1,8 @@
|
||||||
use rustpython_ast::{Constant, Expr, ExprKind, Stmt, StmtKind};
|
use rustpython_ast::{Constant, Expr, ExprKind, Stmt, StmtKind};
|
||||||
|
|
||||||
use crate::ast::types::Range;
|
use crate::ast::types::Range;
|
||||||
use crate::ast::visitor;
|
|
||||||
use crate::ast::visitor::Visitor;
|
use crate::ast::visitor::Visitor;
|
||||||
|
use crate::ast::{cast, visitor};
|
||||||
use crate::check_ast::Checker;
|
use crate::check_ast::Checker;
|
||||||
use crate::checks::{CheckCode, CheckKind};
|
use crate::checks::{CheckCode, CheckKind};
|
||||||
use crate::docstrings::definition::{Definition, DefinitionKind};
|
use crate::docstrings::definition::{Definition, DefinitionKind};
|
||||||
|
|
@ -192,7 +192,10 @@ pub fn definition(checker: &mut Checker, definition: &Definition, visibility: &V
|
||||||
.chain(args.kwonlyargs.iter())
|
.chain(args.kwonlyargs.iter())
|
||||||
.skip(
|
.skip(
|
||||||
// If this is a non-static method, skip `cls` or `self`.
|
// If this is a non-static method, skip `cls` or `self`.
|
||||||
usize::from(!visibility::is_staticmethod(checker, stmt)),
|
usize::from(!visibility::is_staticmethod(
|
||||||
|
checker,
|
||||||
|
cast::decorator_list(stmt),
|
||||||
|
)),
|
||||||
)
|
)
|
||||||
{
|
{
|
||||||
// ANN401 for dynamically typed arguments
|
// ANN401 for dynamically typed arguments
|
||||||
|
|
@ -264,10 +267,10 @@ pub fn definition(checker: &mut Checker, definition: &Definition, visibility: &V
|
||||||
}
|
}
|
||||||
|
|
||||||
// ANN101, ANN102
|
// ANN101, ANN102
|
||||||
if !visibility::is_staticmethod(checker, stmt) {
|
if !visibility::is_staticmethod(checker, cast::decorator_list(stmt)) {
|
||||||
if let Some(arg) = args.args.first() {
|
if let Some(arg) = args.args.first() {
|
||||||
if arg.node.annotation.is_none() {
|
if arg.node.annotation.is_none() {
|
||||||
if visibility::is_classmethod(checker, stmt) {
|
if visibility::is_classmethod(checker, cast::decorator_list(stmt)) {
|
||||||
if checker.settings.enabled.contains(&CheckCode::ANN102) {
|
if checker.settings.enabled.contains(&CheckCode::ANN102) {
|
||||||
checker.add_check(Check::new(
|
checker.add_check(Check::new(
|
||||||
CheckKind::MissingTypeCls(arg.node.arg.to_string()),
|
CheckKind::MissingTypeCls(arg.node.arg.to_string()),
|
||||||
|
|
@ -300,14 +303,14 @@ pub fn definition(checker: &mut Checker, definition: &Definition, visibility: &V
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if visibility::is_classmethod(checker, stmt) {
|
if visibility::is_classmethod(checker, cast::decorator_list(stmt)) {
|
||||||
if checker.settings.enabled.contains(&CheckCode::ANN206) {
|
if checker.settings.enabled.contains(&CheckCode::ANN206) {
|
||||||
checker.add_check(Check::new(
|
checker.add_check(Check::new(
|
||||||
CheckKind::MissingReturnTypeClassMethod(name.to_string()),
|
CheckKind::MissingReturnTypeClassMethod(name.to_string()),
|
||||||
Range::from_located(stmt),
|
Range::from_located(stmt),
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
} else if visibility::is_staticmethod(checker, stmt) {
|
} else if visibility::is_staticmethod(checker, cast::decorator_list(stmt)) {
|
||||||
if checker.settings.enabled.contains(&CheckCode::ANN205) {
|
if checker.settings.enabled.contains(&CheckCode::ANN205) {
|
||||||
checker.add_check(Check::new(
|
checker.add_check(Check::new(
|
||||||
CheckKind::MissingReturnTypeStaticMethod(name.to_string()),
|
CheckKind::MissingReturnTypeStaticMethod(name.to_string()),
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,19 @@
|
||||||
|
use rustpython_ast::{Constant, ExprKind, Stmt, StmtKind};
|
||||||
|
|
||||||
|
pub fn is_empty(body: &[Stmt]) -> bool {
|
||||||
|
match &body {
|
||||||
|
[] => true,
|
||||||
|
// Also allow: raise NotImplementedError, raise NotImplemented
|
||||||
|
[stmt] => match &stmt.node {
|
||||||
|
StmtKind::Pass => true,
|
||||||
|
StmtKind::Expr { value } => match &value.node {
|
||||||
|
ExprKind::Constant { value, .. } => {
|
||||||
|
matches!(value, Constant::Str(_) | Constant::Ellipsis)
|
||||||
|
}
|
||||||
|
_ => false,
|
||||||
|
},
|
||||||
|
_ => false,
|
||||||
|
},
|
||||||
|
_ => false,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,35 @@
|
||||||
|
mod helpers;
|
||||||
|
pub mod plugins;
|
||||||
|
mod types;
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
use std::convert::AsRef;
|
||||||
|
use std::path::Path;
|
||||||
|
|
||||||
|
use anyhow::Result;
|
||||||
|
use test_case::test_case;
|
||||||
|
|
||||||
|
use crate::checks::CheckCode;
|
||||||
|
use crate::linter::test_path;
|
||||||
|
use crate::settings;
|
||||||
|
|
||||||
|
#[test_case(CheckCode::ARG001, Path::new("ARG.py"); "ARG001")]
|
||||||
|
#[test_case(CheckCode::ARG002, Path::new("ARG.py"); "ARG002")]
|
||||||
|
#[test_case(CheckCode::ARG003, Path::new("ARG.py"); "ARG003")]
|
||||||
|
#[test_case(CheckCode::ARG004, Path::new("ARG.py"); "ARG004")]
|
||||||
|
#[test_case(CheckCode::ARG005, Path::new("ARG.py"); "ARG005")]
|
||||||
|
fn checks(check_code: CheckCode, path: &Path) -> Result<()> {
|
||||||
|
let snapshot = format!("{}_{}", check_code.as_ref(), path.to_string_lossy());
|
||||||
|
let mut checks = test_path(
|
||||||
|
Path::new("./resources/test/fixtures/flake8_unused_arguments")
|
||||||
|
.join(path)
|
||||||
|
.as_path(),
|
||||||
|
&settings::Settings::for_rule(check_code),
|
||||||
|
true,
|
||||||
|
)?;
|
||||||
|
checks.sort_by_key(|check| check.location);
|
||||||
|
insta::assert_yaml_snapshot!(snapshot, checks);
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,184 @@
|
||||||
|
use std::iter;
|
||||||
|
|
||||||
|
use regex::Regex;
|
||||||
|
use rustc_hash::FxHashMap;
|
||||||
|
use rustpython_ast::{Arg, Arguments};
|
||||||
|
|
||||||
|
use crate::ast::function_type;
|
||||||
|
use crate::ast::function_type::FunctionType;
|
||||||
|
use crate::ast::helpers::collect_arg_names;
|
||||||
|
use crate::ast::types::{Binding, BindingKind, FunctionDef, Lambda, Scope, ScopeKind};
|
||||||
|
use crate::check_ast::Checker;
|
||||||
|
use crate::flake8_unused_arguments::helpers;
|
||||||
|
use crate::flake8_unused_arguments::types::Argumentable;
|
||||||
|
use crate::{visibility, Check};
|
||||||
|
|
||||||
|
/// Check a plain function for unused arguments.
|
||||||
|
fn function(
|
||||||
|
argumentable: &Argumentable,
|
||||||
|
args: &Arguments,
|
||||||
|
bindings: &FxHashMap<&str, Binding>,
|
||||||
|
dummy_variable_rgx: &Regex,
|
||||||
|
) -> Vec<Check> {
|
||||||
|
let mut checks: Vec<Check> = vec![];
|
||||||
|
for arg_name in collect_arg_names(args) {
|
||||||
|
if let Some(binding) = bindings.get(arg_name) {
|
||||||
|
if binding.used.is_none()
|
||||||
|
&& matches!(binding.kind, BindingKind::Argument)
|
||||||
|
&& !dummy_variable_rgx.is_match(arg_name)
|
||||||
|
{
|
||||||
|
checks.push(Check::new(
|
||||||
|
argumentable.check_for(arg_name.to_string()),
|
||||||
|
binding.range,
|
||||||
|
));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
checks
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Check a method for unused arguments.
|
||||||
|
fn method(
|
||||||
|
argumentable: &Argumentable,
|
||||||
|
args: &Arguments,
|
||||||
|
bindings: &FxHashMap<&str, Binding>,
|
||||||
|
dummy_variable_rgx: &Regex,
|
||||||
|
) -> Vec<Check> {
|
||||||
|
let mut checks: Vec<Check> = vec![];
|
||||||
|
for arg in args
|
||||||
|
.posonlyargs
|
||||||
|
.iter()
|
||||||
|
.chain(args.args.iter())
|
||||||
|
.skip(1)
|
||||||
|
.chain(args.kwonlyargs.iter())
|
||||||
|
.chain(iter::once::<Option<&Arg>>(args.vararg.as_deref()).flatten())
|
||||||
|
.chain(iter::once::<Option<&Arg>>(args.kwarg.as_deref()).flatten())
|
||||||
|
{
|
||||||
|
if let Some(binding) = bindings.get(&arg.node.arg.as_str()) {
|
||||||
|
if binding.used.is_none()
|
||||||
|
&& matches!(binding.kind, BindingKind::Argument)
|
||||||
|
&& !dummy_variable_rgx.is_match(arg.node.arg.as_str())
|
||||||
|
{
|
||||||
|
checks.push(Check::new(
|
||||||
|
argumentable.check_for(arg.node.arg.to_string()),
|
||||||
|
binding.range,
|
||||||
|
));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
checks
|
||||||
|
}
|
||||||
|
|
||||||
|
/// ARG001, ARG002, ARG003, ARG004, ARG005
|
||||||
|
pub fn unused_arguments(checker: &Checker, parent: &Scope, scope: &Scope) -> Vec<Check> {
|
||||||
|
match &scope.kind {
|
||||||
|
ScopeKind::Function(FunctionDef {
|
||||||
|
name,
|
||||||
|
args,
|
||||||
|
body,
|
||||||
|
decorator_list,
|
||||||
|
..
|
||||||
|
}) => {
|
||||||
|
match function_type::classify(
|
||||||
|
parent,
|
||||||
|
name,
|
||||||
|
decorator_list,
|
||||||
|
&checker.from_imports,
|
||||||
|
&checker.import_aliases,
|
||||||
|
&checker.settings.pep8_naming.classmethod_decorators,
|
||||||
|
&checker.settings.pep8_naming.staticmethod_decorators,
|
||||||
|
) {
|
||||||
|
FunctionType::Function => {
|
||||||
|
if checker
|
||||||
|
.settings
|
||||||
|
.enabled
|
||||||
|
.contains(Argumentable::Function.check_code())
|
||||||
|
{
|
||||||
|
function(
|
||||||
|
&Argumentable::Function,
|
||||||
|
args,
|
||||||
|
&scope.values,
|
||||||
|
&checker.settings.dummy_variable_rgx,
|
||||||
|
)
|
||||||
|
} else {
|
||||||
|
vec![]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
FunctionType::Method => {
|
||||||
|
if checker
|
||||||
|
.settings
|
||||||
|
.enabled
|
||||||
|
.contains(Argumentable::Method.check_code())
|
||||||
|
&& !helpers::is_empty(body)
|
||||||
|
&& !visibility::is_abstract(checker, decorator_list)
|
||||||
|
&& !visibility::is_override(checker, decorator_list)
|
||||||
|
{
|
||||||
|
method(
|
||||||
|
&Argumentable::Method,
|
||||||
|
args,
|
||||||
|
&scope.values,
|
||||||
|
&checker.settings.dummy_variable_rgx,
|
||||||
|
)
|
||||||
|
} else {
|
||||||
|
vec![]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
FunctionType::ClassMethod => {
|
||||||
|
if checker
|
||||||
|
.settings
|
||||||
|
.enabled
|
||||||
|
.contains(Argumentable::ClassMethod.check_code())
|
||||||
|
&& !helpers::is_empty(body)
|
||||||
|
&& !visibility::is_abstract(checker, decorator_list)
|
||||||
|
&& !visibility::is_override(checker, decorator_list)
|
||||||
|
{
|
||||||
|
method(
|
||||||
|
&Argumentable::ClassMethod,
|
||||||
|
args,
|
||||||
|
&scope.values,
|
||||||
|
&checker.settings.dummy_variable_rgx,
|
||||||
|
)
|
||||||
|
} else {
|
||||||
|
vec![]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
FunctionType::StaticMethod => {
|
||||||
|
if checker
|
||||||
|
.settings
|
||||||
|
.enabled
|
||||||
|
.contains(Argumentable::StaticMethod.check_code())
|
||||||
|
&& !helpers::is_empty(body)
|
||||||
|
&& !visibility::is_abstract(checker, decorator_list)
|
||||||
|
&& !visibility::is_override(checker, decorator_list)
|
||||||
|
{
|
||||||
|
function(
|
||||||
|
&Argumentable::StaticMethod,
|
||||||
|
args,
|
||||||
|
&scope.values,
|
||||||
|
&checker.settings.dummy_variable_rgx,
|
||||||
|
)
|
||||||
|
} else {
|
||||||
|
vec![]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ScopeKind::Lambda(Lambda { args, .. }) => {
|
||||||
|
if checker
|
||||||
|
.settings
|
||||||
|
.enabled
|
||||||
|
.contains(Argumentable::Lambda.check_code())
|
||||||
|
{
|
||||||
|
function(
|
||||||
|
&Argumentable::Lambda,
|
||||||
|
args,
|
||||||
|
&scope.values,
|
||||||
|
&checker.settings.dummy_variable_rgx,
|
||||||
|
)
|
||||||
|
} else {
|
||||||
|
vec![]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
_ => unreachable!("Expected ScopeKind::Function | ScopeKind::Lambda"),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,77 @@
|
||||||
|
---
|
||||||
|
source: src/flake8_unused_arguments/mod.rs
|
||||||
|
expression: checks
|
||||||
|
---
|
||||||
|
- kind:
|
||||||
|
UnusedFunctionArgument: self
|
||||||
|
location:
|
||||||
|
row: 8
|
||||||
|
column: 6
|
||||||
|
end_location:
|
||||||
|
row: 8
|
||||||
|
column: 10
|
||||||
|
fix: ~
|
||||||
|
- kind:
|
||||||
|
UnusedFunctionArgument: x
|
||||||
|
location:
|
||||||
|
row: 8
|
||||||
|
column: 12
|
||||||
|
end_location:
|
||||||
|
row: 8
|
||||||
|
column: 13
|
||||||
|
fix: ~
|
||||||
|
- kind:
|
||||||
|
UnusedFunctionArgument: cls
|
||||||
|
location:
|
||||||
|
row: 12
|
||||||
|
column: 6
|
||||||
|
end_location:
|
||||||
|
row: 12
|
||||||
|
column: 9
|
||||||
|
fix: ~
|
||||||
|
- kind:
|
||||||
|
UnusedFunctionArgument: x
|
||||||
|
location:
|
||||||
|
row: 12
|
||||||
|
column: 11
|
||||||
|
end_location:
|
||||||
|
row: 12
|
||||||
|
column: 12
|
||||||
|
fix: ~
|
||||||
|
- kind:
|
||||||
|
UnusedFunctionArgument: self
|
||||||
|
location:
|
||||||
|
row: 16
|
||||||
|
column: 6
|
||||||
|
end_location:
|
||||||
|
row: 16
|
||||||
|
column: 10
|
||||||
|
fix: ~
|
||||||
|
- kind:
|
||||||
|
UnusedFunctionArgument: x
|
||||||
|
location:
|
||||||
|
row: 16
|
||||||
|
column: 12
|
||||||
|
end_location:
|
||||||
|
row: 16
|
||||||
|
column: 13
|
||||||
|
fix: ~
|
||||||
|
- kind:
|
||||||
|
UnusedFunctionArgument: cls
|
||||||
|
location:
|
||||||
|
row: 20
|
||||||
|
column: 6
|
||||||
|
end_location:
|
||||||
|
row: 20
|
||||||
|
column: 9
|
||||||
|
fix: ~
|
||||||
|
- kind:
|
||||||
|
UnusedFunctionArgument: x
|
||||||
|
location:
|
||||||
|
row: 20
|
||||||
|
column: 11
|
||||||
|
end_location:
|
||||||
|
row: 20
|
||||||
|
column: 12
|
||||||
|
fix: ~
|
||||||
|
|
||||||
|
|
@ -0,0 +1,32 @@
|
||||||
|
---
|
||||||
|
source: src/flake8_unused_arguments/mod.rs
|
||||||
|
expression: checks
|
||||||
|
---
|
||||||
|
- kind:
|
||||||
|
UnusedMethodArgument: x
|
||||||
|
location:
|
||||||
|
row: 34
|
||||||
|
column: 16
|
||||||
|
end_location:
|
||||||
|
row: 34
|
||||||
|
column: 17
|
||||||
|
fix: ~
|
||||||
|
- kind:
|
||||||
|
UnusedMethodArgument: x
|
||||||
|
location:
|
||||||
|
row: 37
|
||||||
|
column: 19
|
||||||
|
end_location:
|
||||||
|
row: 37
|
||||||
|
column: 20
|
||||||
|
fix: ~
|
||||||
|
- kind:
|
||||||
|
UnusedMethodArgument: x
|
||||||
|
location:
|
||||||
|
row: 40
|
||||||
|
column: 15
|
||||||
|
end_location:
|
||||||
|
row: 40
|
||||||
|
column: 16
|
||||||
|
fix: ~
|
||||||
|
|
||||||
|
|
@ -0,0 +1,14 @@
|
||||||
|
---
|
||||||
|
source: src/flake8_unused_arguments/mod.rs
|
||||||
|
expression: checks
|
||||||
|
---
|
||||||
|
- kind:
|
||||||
|
UnusedClassMethodArgument: x
|
||||||
|
location:
|
||||||
|
row: 44
|
||||||
|
column: 15
|
||||||
|
end_location:
|
||||||
|
row: 44
|
||||||
|
column: 16
|
||||||
|
fix: ~
|
||||||
|
|
||||||
|
|
@ -0,0 +1,32 @@
|
||||||
|
---
|
||||||
|
source: src/flake8_unused_arguments/mod.rs
|
||||||
|
expression: checks
|
||||||
|
---
|
||||||
|
- kind:
|
||||||
|
UnusedStaticMethodArgument: cls
|
||||||
|
location:
|
||||||
|
row: 48
|
||||||
|
column: 10
|
||||||
|
end_location:
|
||||||
|
row: 48
|
||||||
|
column: 13
|
||||||
|
fix: ~
|
||||||
|
- kind:
|
||||||
|
UnusedStaticMethodArgument: x
|
||||||
|
location:
|
||||||
|
row: 48
|
||||||
|
column: 15
|
||||||
|
end_location:
|
||||||
|
row: 48
|
||||||
|
column: 16
|
||||||
|
fix: ~
|
||||||
|
- kind:
|
||||||
|
UnusedStaticMethodArgument: x
|
||||||
|
location:
|
||||||
|
row: 52
|
||||||
|
column: 10
|
||||||
|
end_location:
|
||||||
|
row: 52
|
||||||
|
column: 11
|
||||||
|
fix: ~
|
||||||
|
|
||||||
|
|
@ -0,0 +1,14 @@
|
||||||
|
---
|
||||||
|
source: src/flake8_unused_arguments/mod.rs
|
||||||
|
expression: checks
|
||||||
|
---
|
||||||
|
- kind:
|
||||||
|
UnusedLambdaArgument: x
|
||||||
|
location:
|
||||||
|
row: 27
|
||||||
|
column: 7
|
||||||
|
end_location:
|
||||||
|
row: 27
|
||||||
|
column: 8
|
||||||
|
fix: ~
|
||||||
|
|
||||||
|
|
@ -0,0 +1,32 @@
|
||||||
|
use crate::checks::{CheckCode, CheckKind};
|
||||||
|
|
||||||
|
/// An AST node that can contain arguments.
|
||||||
|
pub enum Argumentable {
|
||||||
|
Function,
|
||||||
|
Method,
|
||||||
|
ClassMethod,
|
||||||
|
StaticMethod,
|
||||||
|
Lambda,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Argumentable {
|
||||||
|
pub fn check_for(&self, name: String) -> CheckKind {
|
||||||
|
match self {
|
||||||
|
Argumentable::Function => CheckKind::UnusedFunctionArgument(name),
|
||||||
|
Argumentable::Method => CheckKind::UnusedMethodArgument(name),
|
||||||
|
Argumentable::ClassMethod => CheckKind::UnusedClassMethodArgument(name),
|
||||||
|
Argumentable::StaticMethod => CheckKind::UnusedStaticMethodArgument(name),
|
||||||
|
Argumentable::Lambda => CheckKind::UnusedLambdaArgument(name),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn check_code(&self) -> &CheckCode {
|
||||||
|
match self {
|
||||||
|
Argumentable::Function => &CheckCode::ARG001,
|
||||||
|
Argumentable::Method => &CheckCode::ARG002,
|
||||||
|
Argumentable::ClassMethod => &CheckCode::ARG003,
|
||||||
|
Argumentable::StaticMethod => &CheckCode::ARG004,
|
||||||
|
Argumentable::Lambda => &CheckCode::ARG005,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -54,6 +54,7 @@ mod flake8_print;
|
||||||
pub mod flake8_quotes;
|
pub mod flake8_quotes;
|
||||||
mod flake8_return;
|
mod flake8_return;
|
||||||
pub mod flake8_tidy_imports;
|
pub mod flake8_tidy_imports;
|
||||||
|
mod flake8_unused_arguments;
|
||||||
pub mod fs;
|
pub mod fs;
|
||||||
mod isort;
|
mod isort;
|
||||||
mod lex;
|
mod lex;
|
||||||
|
|
|
||||||
|
|
@ -1,10 +1,10 @@
|
||||||
use rustc_hash::{FxHashMap, FxHashSet};
|
use rustc_hash::{FxHashMap, FxHashSet};
|
||||||
use rustpython_ast::{Arguments, Expr, ExprKind, Stmt};
|
use rustpython_ast::{Arguments, Expr, ExprKind, Stmt};
|
||||||
|
|
||||||
|
use crate::ast::function_type;
|
||||||
use crate::ast::types::{Range, Scope, ScopeKind};
|
use crate::ast::types::{Range, Scope, ScopeKind};
|
||||||
use crate::checks::{Check, CheckKind};
|
use crate::checks::{Check, CheckKind};
|
||||||
use crate::pep8_naming::helpers;
|
use crate::pep8_naming::helpers;
|
||||||
use crate::pep8_naming::helpers::FunctionType;
|
|
||||||
use crate::pep8_naming::settings::Settings;
|
use crate::pep8_naming::settings::Settings;
|
||||||
use crate::python::string::{self};
|
use crate::python::string::{self};
|
||||||
|
|
||||||
|
|
@ -58,15 +58,16 @@ pub fn invalid_first_argument_name_for_class_method(
|
||||||
settings: &Settings,
|
settings: &Settings,
|
||||||
) -> Option<Check> {
|
) -> Option<Check> {
|
||||||
if !matches!(
|
if !matches!(
|
||||||
helpers::function_type(
|
function_type::classify(
|
||||||
scope,
|
scope,
|
||||||
name,
|
name,
|
||||||
decorator_list,
|
decorator_list,
|
||||||
from_imports,
|
from_imports,
|
||||||
import_aliases,
|
import_aliases,
|
||||||
settings,
|
&settings.classmethod_decorators,
|
||||||
|
&settings.staticmethod_decorators,
|
||||||
),
|
),
|
||||||
FunctionType::ClassMethod
|
function_type::FunctionType::ClassMethod
|
||||||
) {
|
) {
|
||||||
return None;
|
return None;
|
||||||
}
|
}
|
||||||
|
|
@ -99,15 +100,16 @@ pub fn invalid_first_argument_name_for_method(
|
||||||
settings: &Settings,
|
settings: &Settings,
|
||||||
) -> Option<Check> {
|
) -> Option<Check> {
|
||||||
if !matches!(
|
if !matches!(
|
||||||
helpers::function_type(
|
function_type::classify(
|
||||||
scope,
|
scope,
|
||||||
name,
|
name,
|
||||||
decorator_list,
|
decorator_list,
|
||||||
from_imports,
|
from_imports,
|
||||||
import_aliases,
|
import_aliases,
|
||||||
settings,
|
&settings.classmethod_decorators,
|
||||||
|
&settings.staticmethod_decorators,
|
||||||
),
|
),
|
||||||
FunctionType::Method
|
function_type::FunctionType::Method
|
||||||
) {
|
) {
|
||||||
return None;
|
return None;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,71 +1,10 @@
|
||||||
use itertools::Itertools;
|
use itertools::Itertools;
|
||||||
use rustc_hash::{FxHashMap, FxHashSet};
|
use rustc_hash::{FxHashMap, FxHashSet};
|
||||||
use rustpython_ast::{Expr, Stmt, StmtKind};
|
use rustpython_ast::{Stmt, StmtKind};
|
||||||
|
|
||||||
use crate::ast::helpers::{
|
use crate::ast::helpers::{collect_call_paths, match_call_path};
|
||||||
collect_call_paths, dealias_call_path, match_call_path, to_module_and_member,
|
|
||||||
};
|
|
||||||
use crate::ast::types::{Scope, ScopeKind};
|
|
||||||
use crate::pep8_naming::settings::Settings;
|
|
||||||
use crate::python::string::{is_lower, is_upper};
|
use crate::python::string::{is_lower, is_upper};
|
||||||
|
|
||||||
const CLASS_METHODS: [&str; 3] = ["__new__", "__init_subclass__", "__class_getitem__"];
|
|
||||||
const METACLASS_BASES: [(&str, &str); 2] = [("", "type"), ("abc", "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],
|
|
||||||
from_imports: &FxHashMap<&str, FxHashSet<&str>>,
|
|
||||||
import_aliases: &FxHashMap<&str, &str>,
|
|
||||||
settings: &Settings,
|
|
||||||
) -> FunctionType {
|
|
||||||
let ScopeKind::Class(scope) = &scope.kind else {
|
|
||||||
return FunctionType::Function;
|
|
||||||
};
|
|
||||||
// Special-case class method, like `__new__`.
|
|
||||||
if CLASS_METHODS.contains(&name)
|
|
||||||
|| scope.bases.iter().any(|expr| {
|
|
||||||
// The class itself extends a known metaclass, so all methods are class methods.
|
|
||||||
let call_path = dealias_call_path(collect_call_paths(expr), import_aliases);
|
|
||||||
METACLASS_BASES
|
|
||||||
.iter()
|
|
||||||
.any(|(module, member)| match_call_path(&call_path, module, member, from_imports))
|
|
||||||
})
|
|
||||||
|| decorator_list.iter().any(|expr| {
|
|
||||||
// The method is decorated with a class method decorator (like `@classmethod`).
|
|
||||||
let call_path = dealias_call_path(collect_call_paths(expr), import_aliases);
|
|
||||||
settings.classmethod_decorators.iter().any(|decorator| {
|
|
||||||
let (module, member) = to_module_and_member(decorator);
|
|
||||||
match_call_path(&call_path, module, member, from_imports)
|
|
||||||
})
|
|
||||||
})
|
|
||||||
{
|
|
||||||
FunctionType::ClassMethod
|
|
||||||
} else if decorator_list.iter().any(|expr| {
|
|
||||||
// The method is decorated with a static method decorator (like
|
|
||||||
// `@staticmethod`).
|
|
||||||
let call_path = dealias_call_path(collect_call_paths(expr), import_aliases);
|
|
||||||
settings.staticmethod_decorators.iter().any(|decorator| {
|
|
||||||
let (module, member) = to_module_and_member(decorator);
|
|
||||||
match_call_path(&call_path, module, member, from_imports)
|
|
||||||
})
|
|
||||||
}) {
|
|
||||||
FunctionType::StaticMethod
|
|
||||||
} else {
|
|
||||||
// It's an instance method.
|
|
||||||
FunctionType::Method
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn is_camelcase(name: &str) -> bool {
|
pub fn is_camelcase(name: &str) -> bool {
|
||||||
!is_lower(name) && !is_upper(name) && !name.contains('_')
|
!is_lower(name) && !is_upper(name) && !name.contains('_')
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -7,8 +7,8 @@ use rustc_hash::FxHashSet;
|
||||||
use rustpython_ast::{Constant, ExprKind, Location, StmtKind};
|
use rustpython_ast::{Constant, ExprKind, Location, StmtKind};
|
||||||
|
|
||||||
use crate::ast::types::Range;
|
use crate::ast::types::Range;
|
||||||
use crate::ast::whitespace;
|
|
||||||
use crate::ast::whitespace::LinesWithTrailingNewline;
|
use crate::ast::whitespace::LinesWithTrailingNewline;
|
||||||
|
use crate::ast::{cast, whitespace};
|
||||||
use crate::autofix::Fix;
|
use crate::autofix::Fix;
|
||||||
use crate::check_ast::Checker;
|
use crate::check_ast::Checker;
|
||||||
use crate::checks::{Check, CheckCode, CheckKind};
|
use crate::checks::{Check, CheckCode, CheckKind};
|
||||||
|
|
@ -77,7 +77,7 @@ pub fn not_missing(
|
||||||
false
|
false
|
||||||
}
|
}
|
||||||
DefinitionKind::Function(stmt) | DefinitionKind::NestedFunction(stmt) => {
|
DefinitionKind::Function(stmt) | DefinitionKind::NestedFunction(stmt) => {
|
||||||
if is_overload(checker, stmt) {
|
if is_overload(checker, cast::decorator_list(stmt)) {
|
||||||
true
|
true
|
||||||
} else {
|
} else {
|
||||||
if checker.settings.enabled.contains(&CheckCode::D103) {
|
if checker.settings.enabled.contains(&CheckCode::D103) {
|
||||||
|
|
@ -90,7 +90,9 @@ pub fn not_missing(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
DefinitionKind::Method(stmt) => {
|
DefinitionKind::Method(stmt) => {
|
||||||
if is_overload(checker, stmt) || is_override(checker, stmt) {
|
if is_overload(checker, cast::decorator_list(stmt))
|
||||||
|
|| is_override(checker, cast::decorator_list(stmt))
|
||||||
|
{
|
||||||
true
|
true
|
||||||
} else if is_magic(stmt) {
|
} else if is_magic(stmt) {
|
||||||
if checker.settings.enabled.contains(&CheckCode::D105) {
|
if checker.settings.enabled.contains(&CheckCode::D105) {
|
||||||
|
|
@ -916,7 +918,7 @@ pub fn if_needed(checker: &mut Checker, definition: &Definition) {
|
||||||
) = definition.kind else {
|
) = definition.kind else {
|
||||||
return
|
return
|
||||||
};
|
};
|
||||||
if !is_overload(checker, stmt) {
|
if !is_overload(checker, cast::decorator_list(stmt)) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
checker.add_check(Check::new(
|
checker.add_check(Check::new(
|
||||||
|
|
@ -1410,7 +1412,7 @@ fn missing_args(checker: &mut Checker, definition: &Definition, docstrings_args:
|
||||||
// If this is a non-static method, skip `cls` or `self`.
|
// If this is a non-static method, skip `cls` or `self`.
|
||||||
usize::from(
|
usize::from(
|
||||||
matches!(definition.kind, DefinitionKind::Method(_))
|
matches!(definition.kind, DefinitionKind::Method(_))
|
||||||
&& !is_staticmethod(checker, parent),
|
&& !is_staticmethod(checker, cast::decorator_list(parent)),
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
{
|
{
|
||||||
|
|
|
||||||
|
|
@ -7,7 +7,7 @@ use rustpython_parser::ast::{
|
||||||
Arg, Arguments, Constant, Excepthandler, ExcepthandlerKind, Expr, ExprKind, Stmt, StmtKind,
|
Arg, Arguments, Constant, Excepthandler, ExcepthandlerKind, Expr, ExprKind, Stmt, StmtKind,
|
||||||
};
|
};
|
||||||
|
|
||||||
use crate::ast::types::{BindingKind, FunctionScope, Range, Scope, ScopeKind};
|
use crate::ast::types::{BindingKind, Range, Scope, ScopeKind};
|
||||||
use crate::checks::{Check, CheckKind};
|
use crate::checks::{Check, CheckKind};
|
||||||
use crate::pyflakes::cformat::CFormatSummary;
|
use crate::pyflakes::cformat::CFormatSummary;
|
||||||
use crate::pyflakes::format::FormatSummary;
|
use crate::pyflakes::format::FormatSummary;
|
||||||
|
|
@ -391,13 +391,7 @@ pub fn undefined_local(scopes: &[&Scope], name: &str) -> Option<Check> {
|
||||||
pub fn unused_variables(scope: &Scope, dummy_variable_rgx: &Regex) -> Vec<Check> {
|
pub fn unused_variables(scope: &Scope, dummy_variable_rgx: &Regex) -> Vec<Check> {
|
||||||
let mut checks: Vec<Check> = vec![];
|
let mut checks: Vec<Check> = vec![];
|
||||||
|
|
||||||
if matches!(
|
if scope.uses_locals && matches!(scope.kind, ScopeKind::Function(..)) {
|
||||||
scope.kind,
|
|
||||||
ScopeKind::Function(FunctionScope {
|
|
||||||
uses_locals: true,
|
|
||||||
..
|
|
||||||
})
|
|
||||||
) {
|
|
||||||
return checks;
|
return checks;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
use rustpython_ast::Expr;
|
use rustpython_ast::Expr;
|
||||||
|
|
||||||
use crate::ast::types::{FunctionScope, Range, ScopeKind};
|
use crate::ast::types::{FunctionDef, Range, ScopeKind};
|
||||||
use crate::check_ast::Checker;
|
use crate::check_ast::Checker;
|
||||||
use crate::checks::CheckKind;
|
use crate::checks::CheckKind;
|
||||||
use crate::Check;
|
use crate::Check;
|
||||||
|
|
@ -10,7 +10,7 @@ pub fn await_outside_async(checker: &mut Checker, expr: &Expr) {
|
||||||
if !checker
|
if !checker
|
||||||
.current_scopes()
|
.current_scopes()
|
||||||
.find_map(|scope| {
|
.find_map(|scope| {
|
||||||
if let ScopeKind::Function(FunctionScope { async_, .. }) = &scope.kind {
|
if let ScopeKind::Function(FunctionDef { async_, .. }) = &scope.kind {
|
||||||
Some(*async_)
|
Some(*async_)
|
||||||
} else {
|
} else {
|
||||||
None
|
None
|
||||||
|
|
|
||||||
|
|
@ -3,7 +3,7 @@
|
||||||
|
|
||||||
use std::path::Path;
|
use std::path::Path;
|
||||||
|
|
||||||
use rustpython_ast::{Stmt, StmtKind};
|
use rustpython_ast::{Expr, Stmt, StmtKind};
|
||||||
|
|
||||||
use crate::ast::helpers::match_module_member;
|
use crate::ast::helpers::match_module_member;
|
||||||
use crate::check_ast::Checker;
|
use crate::check_ast::Checker;
|
||||||
|
|
@ -29,59 +29,56 @@ pub struct VisibleScope {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns `true` if a function is a "static method".
|
/// Returns `true` if a function is a "static method".
|
||||||
pub fn is_staticmethod(checker: &Checker, stmt: &Stmt) -> bool {
|
pub fn is_staticmethod(checker: &Checker, decorator_list: &[Expr]) -> bool {
|
||||||
match &stmt.node {
|
decorator_list.iter().any(|expr| {
|
||||||
StmtKind::FunctionDef { decorator_list, .. }
|
match_module_member(
|
||||||
| StmtKind::AsyncFunctionDef { decorator_list, .. } => decorator_list.iter().any(|expr| {
|
expr,
|
||||||
match_module_member(
|
"",
|
||||||
expr,
|
"staticmethod",
|
||||||
"",
|
&checker.from_imports,
|
||||||
"staticmethod",
|
&checker.import_aliases,
|
||||||
&checker.from_imports,
|
)
|
||||||
&checker.import_aliases,
|
})
|
||||||
)
|
|
||||||
}),
|
|
||||||
_ => panic!("Found non-FunctionDef in is_staticmethod"),
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns `true` if a function is a "class method".
|
/// Returns `true` if a function is a "class method".
|
||||||
pub fn is_classmethod(checker: &Checker, stmt: &Stmt) -> bool {
|
pub fn is_classmethod(checker: &Checker, decorator_list: &[Expr]) -> bool {
|
||||||
match &stmt.node {
|
decorator_list.iter().any(|expr| {
|
||||||
StmtKind::FunctionDef { decorator_list, .. }
|
match_module_member(
|
||||||
| StmtKind::AsyncFunctionDef { decorator_list, .. } => decorator_list.iter().any(|expr| {
|
expr,
|
||||||
match_module_member(
|
"",
|
||||||
expr,
|
"classmethod",
|
||||||
"",
|
&checker.from_imports,
|
||||||
"classmethod",
|
&checker.import_aliases,
|
||||||
&checker.from_imports,
|
)
|
||||||
&checker.import_aliases,
|
})
|
||||||
)
|
|
||||||
}),
|
|
||||||
_ => panic!("Found non-FunctionDef in is_classmethod"),
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns `true` if a function definition is an `@overload`.
|
/// Returns `true` if a function definition is an `@overload`.
|
||||||
pub fn is_overload(checker: &Checker, stmt: &Stmt) -> bool {
|
pub fn is_overload(checker: &Checker, decorator_list: &[Expr]) -> bool {
|
||||||
match &stmt.node {
|
decorator_list
|
||||||
StmtKind::FunctionDef { decorator_list, .. }
|
.iter()
|
||||||
| StmtKind::AsyncFunctionDef { decorator_list, .. } => decorator_list
|
.any(|expr| checker.match_typing_expr(expr, "overload"))
|
||||||
.iter()
|
|
||||||
.any(|expr| checker.match_typing_expr(expr, "overload")),
|
|
||||||
_ => panic!("Found non-FunctionDef in is_overload"),
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns `true` if a function definition is an `@override` (PEP 698).
|
/// Returns `true` if a function definition is an `@override` (PEP 698).
|
||||||
pub fn is_override(checker: &Checker, stmt: &Stmt) -> bool {
|
pub fn is_override(checker: &Checker, decorator_list: &[Expr]) -> bool {
|
||||||
match &stmt.node {
|
decorator_list
|
||||||
StmtKind::FunctionDef { decorator_list, .. }
|
.iter()
|
||||||
| StmtKind::AsyncFunctionDef { decorator_list, .. } => decorator_list
|
.any(|expr| checker.match_typing_expr(expr, "override"))
|
||||||
.iter()
|
}
|
||||||
.any(|expr| checker.match_typing_expr(expr, "override")),
|
|
||||||
_ => panic!("Found non-FunctionDef in is_override"),
|
/// Returns `true` if a function definition is an `@abstractmethod`.
|
||||||
}
|
pub fn is_abstract(checker: &Checker, decorator_list: &[Expr]) -> bool {
|
||||||
|
decorator_list.iter().any(|expr| {
|
||||||
|
match_module_member(
|
||||||
|
expr,
|
||||||
|
"abc",
|
||||||
|
"abstractmethod",
|
||||||
|
&checker.from_imports,
|
||||||
|
&checker.import_aliases,
|
||||||
|
)
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns `true` if a function is a "magic method".
|
/// Returns `true` if a function is a "magic method".
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue