mirror of
https://github.com/astral-sh/ruff.git
synced 2025-09-30 22:01:47 +00:00
Implement C901 (mccabe) (#765)
This commit is contained in:
parent
6a6f4651aa
commit
f44fada446
22 changed files with 555 additions and 11 deletions
|
@ -98,6 +98,13 @@ _and_ a `pyproject.toml` parameter to `src/pyproject.rs`. If you want to pattern
|
||||||
existing example, grep for `dummy_variable_rgx`, which defines a regular expression to match against
|
existing example, grep for `dummy_variable_rgx`, which defines a regular expression to match against
|
||||||
acceptable unused variables (e.g., `_`).
|
acceptable unused variables (e.g., `_`).
|
||||||
|
|
||||||
|
If the new plugin's configuration should be cached between runs, you'll need to add it to the
|
||||||
|
`Hash` implementation for `Settings` in `src/settings/mod.rs`.
|
||||||
|
|
||||||
|
You may also want to add the new configuration option to the `flake8-to-ruff` tool, which is
|
||||||
|
responsible for converting `flake8` configuration files to Ruff's TOML format. This logic
|
||||||
|
lives in `flake8_to_ruff/src/converter.rs`.
|
||||||
|
|
||||||
## Release process
|
## Release process
|
||||||
|
|
||||||
As of now, Ruff has an ad hoc release process: releases are cut with high frequency via GitHub
|
As of now, Ruff has an ad hoc release process: releases are cut with high frequency via GitHub
|
||||||
|
|
14
README.md
14
README.md
|
@ -240,6 +240,8 @@ Options:
|
||||||
The minimum Python version that should be supported
|
The minimum Python version that should be supported
|
||||||
--line-length <LINE_LENGTH>
|
--line-length <LINE_LENGTH>
|
||||||
Set the line-length for length-associated checks and automatic formatting
|
Set the line-length for length-associated checks and automatic formatting
|
||||||
|
--max-complexity <MAX_COMPLEXITY>
|
||||||
|
Set the maximum cyclomatic complexity for complexity-associated checks
|
||||||
--stdin-filename <STDIN_FILENAME>
|
--stdin-filename <STDIN_FILENAME>
|
||||||
The name of the file when passing it through stdin
|
The name of the file when passing it through stdin
|
||||||
-h, --help
|
-h, --help
|
||||||
|
@ -357,8 +359,8 @@ For more, see [pycodestyle](https://pypi.org/project/pycodestyle/2.9.1/) on PyPI
|
||||||
| ---- | ---- | ------- | --- |
|
| ---- | ---- | ------- | --- |
|
||||||
| E402 | ModuleImportNotAtTopOfFile | Module level import not at top of file | |
|
| E402 | ModuleImportNotAtTopOfFile | Module level import not at top of file | |
|
||||||
| E501 | LineTooLong | Line too long (89 > 88 characters) | |
|
| E501 | LineTooLong | Line too long (89 > 88 characters) | |
|
||||||
| E711 | NoneComparison | Comparison to `None` should be `cond is None` | 🛠 |
|
| E711 | NoneComparison | Comparison to `None` should be `cond is None` | |
|
||||||
| E712 | TrueFalseComparison | Comparison to `True` should be `cond is True` | 🛠 |
|
| E712 | TrueFalseComparison | Comparison to `True` should be `cond is True` | |
|
||||||
| E713 | NotInTest | Test for membership should be `not in` | |
|
| E713 | NotInTest | Test for membership should be `not in` | |
|
||||||
| E714 | NotIsTest | Test for object identity should be `is not` | |
|
| E714 | NotIsTest | Test for object identity should be `is not` | |
|
||||||
| E721 | TypeComparison | Do not compare types, use `isinstance()` | |
|
| E721 | TypeComparison | Do not compare types, use `isinstance()` | |
|
||||||
|
@ -612,6 +614,14 @@ For more, see [flake8-2020](https://pypi.org/project/flake8-2020/1.7.0/) on PyPI
|
||||||
| YTT302 | SysVersionCmpStr10 | `sys.version` compared to string (python10), use `sys.version_info` | |
|
| YTT302 | SysVersionCmpStr10 | `sys.version` compared to string (python10), use `sys.version_info` | |
|
||||||
| YTT303 | SysVersionSlice1Referenced | `sys.version[:1]` referenced (python10), use `sys.version_info` | |
|
| YTT303 | SysVersionSlice1Referenced | `sys.version[:1]` referenced (python10), use `sys.version_info` | |
|
||||||
|
|
||||||
|
### mccabe
|
||||||
|
|
||||||
|
For more, see [mccabe](https://pypi.org/project/mccabe/0.7.0/) on PyPI.
|
||||||
|
|
||||||
|
| Code | Name | Message | Fix |
|
||||||
|
| ---- | ---- | ------- | --- |
|
||||||
|
| C901 | FunctionIsTooComplex | `...` is too complex (10) | |
|
||||||
|
|
||||||
### Ruff-specific rules
|
### Ruff-specific rules
|
||||||
|
|
||||||
| Code | Name | Message | Fix |
|
| Code | Name | Message | Fix |
|
||||||
|
|
|
@ -6,7 +6,9 @@ use ruff::flake8_quotes::settings::Quote;
|
||||||
use ruff::flake8_tidy_imports::settings::Strictness;
|
use ruff::flake8_tidy_imports::settings::Strictness;
|
||||||
use ruff::settings::options::Options;
|
use ruff::settings::options::Options;
|
||||||
use ruff::settings::pyproject::Pyproject;
|
use ruff::settings::pyproject::Pyproject;
|
||||||
use ruff::{flake8_annotations, flake8_bugbear, flake8_quotes, flake8_tidy_imports, pep8_naming};
|
use ruff::{
|
||||||
|
flake8_annotations, flake8_bugbear, flake8_quotes, flake8_tidy_imports, mccabe, pep8_naming,
|
||||||
|
};
|
||||||
|
|
||||||
use crate::plugin::Plugin;
|
use crate::plugin::Plugin;
|
||||||
use crate::{parser, plugin};
|
use crate::{parser, plugin};
|
||||||
|
@ -73,6 +75,7 @@ pub fn convert(
|
||||||
let mut flake8_bugbear: flake8_bugbear::settings::Options = Default::default();
|
let mut flake8_bugbear: flake8_bugbear::settings::Options = Default::default();
|
||||||
let mut flake8_quotes: flake8_quotes::settings::Options = Default::default();
|
let mut flake8_quotes: flake8_quotes::settings::Options = Default::default();
|
||||||
let mut flake8_tidy_imports: flake8_tidy_imports::settings::Options = Default::default();
|
let mut flake8_tidy_imports: flake8_tidy_imports::settings::Options = Default::default();
|
||||||
|
let mut mccabe: mccabe::settings::Options = Default::default();
|
||||||
let mut pep8_naming: pep8_naming::settings::Options = Default::default();
|
let mut pep8_naming: pep8_naming::settings::Options = Default::default();
|
||||||
for (key, value) in flake8 {
|
for (key, value) in flake8 {
|
||||||
if let Some(value) = value {
|
if let Some(value) = value {
|
||||||
|
@ -186,6 +189,11 @@ pub fn convert(
|
||||||
"docstring-convention" => {
|
"docstring-convention" => {
|
||||||
// No-op (handled above).
|
// No-op (handled above).
|
||||||
}
|
}
|
||||||
|
// mccabe
|
||||||
|
"max-complexity" | "max_complexity" => match value.clone().parse::<usize>() {
|
||||||
|
Ok(max_complexity) => mccabe.max_complexity = Some(max_complexity),
|
||||||
|
Err(e) => eprintln!("Unable to parse '{key}' property: {e}"),
|
||||||
|
},
|
||||||
// Unknown
|
// Unknown
|
||||||
_ => eprintln!("Skipping unsupported property: {key}"),
|
_ => eprintln!("Skipping unsupported property: {key}"),
|
||||||
}
|
}
|
||||||
|
@ -207,6 +215,9 @@ pub fn convert(
|
||||||
if flake8_tidy_imports != Default::default() {
|
if flake8_tidy_imports != Default::default() {
|
||||||
options.flake8_tidy_imports = Some(flake8_tidy_imports);
|
options.flake8_tidy_imports = Some(flake8_tidy_imports);
|
||||||
}
|
}
|
||||||
|
if mccabe != Default::default() {
|
||||||
|
options.mccabe = Some(mccabe);
|
||||||
|
}
|
||||||
if pep8_naming != Default::default() {
|
if pep8_naming != Default::default() {
|
||||||
options.pep8_naming = Some(pep8_naming);
|
options.pep8_naming = Some(pep8_naming);
|
||||||
}
|
}
|
||||||
|
@ -253,6 +264,7 @@ mod tests {
|
||||||
flake8_quotes: None,
|
flake8_quotes: None,
|
||||||
flake8_tidy_imports: None,
|
flake8_tidy_imports: None,
|
||||||
isort: None,
|
isort: None,
|
||||||
|
mccabe: None,
|
||||||
pep8_naming: None,
|
pep8_naming: None,
|
||||||
});
|
});
|
||||||
assert_eq!(actual, expected);
|
assert_eq!(actual, expected);
|
||||||
|
@ -288,6 +300,7 @@ mod tests {
|
||||||
flake8_quotes: None,
|
flake8_quotes: None,
|
||||||
flake8_tidy_imports: None,
|
flake8_tidy_imports: None,
|
||||||
isort: None,
|
isort: None,
|
||||||
|
mccabe: None,
|
||||||
pep8_naming: None,
|
pep8_naming: None,
|
||||||
});
|
});
|
||||||
assert_eq!(actual, expected);
|
assert_eq!(actual, expected);
|
||||||
|
@ -323,6 +336,7 @@ mod tests {
|
||||||
flake8_quotes: None,
|
flake8_quotes: None,
|
||||||
flake8_tidy_imports: None,
|
flake8_tidy_imports: None,
|
||||||
isort: None,
|
isort: None,
|
||||||
|
mccabe: None,
|
||||||
pep8_naming: None,
|
pep8_naming: None,
|
||||||
});
|
});
|
||||||
assert_eq!(actual, expected);
|
assert_eq!(actual, expected);
|
||||||
|
@ -358,6 +372,7 @@ mod tests {
|
||||||
flake8_quotes: None,
|
flake8_quotes: None,
|
||||||
flake8_tidy_imports: None,
|
flake8_tidy_imports: None,
|
||||||
isort: None,
|
isort: None,
|
||||||
|
mccabe: None,
|
||||||
pep8_naming: None,
|
pep8_naming: None,
|
||||||
});
|
});
|
||||||
assert_eq!(actual, expected);
|
assert_eq!(actual, expected);
|
||||||
|
@ -398,6 +413,7 @@ mod tests {
|
||||||
}),
|
}),
|
||||||
flake8_tidy_imports: None,
|
flake8_tidy_imports: None,
|
||||||
isort: None,
|
isort: None,
|
||||||
|
mccabe: None,
|
||||||
pep8_naming: None,
|
pep8_naming: None,
|
||||||
});
|
});
|
||||||
assert_eq!(actual, expected);
|
assert_eq!(actual, expected);
|
||||||
|
@ -471,6 +487,7 @@ mod tests {
|
||||||
flake8_quotes: None,
|
flake8_quotes: None,
|
||||||
flake8_tidy_imports: None,
|
flake8_tidy_imports: None,
|
||||||
isort: None,
|
isort: None,
|
||||||
|
mccabe: None,
|
||||||
pep8_naming: None,
|
pep8_naming: None,
|
||||||
});
|
});
|
||||||
assert_eq!(actual, expected);
|
assert_eq!(actual, expected);
|
||||||
|
@ -512,6 +529,7 @@ mod tests {
|
||||||
}),
|
}),
|
||||||
flake8_tidy_imports: None,
|
flake8_tidy_imports: None,
|
||||||
isort: None,
|
isort: None,
|
||||||
|
mccabe: None,
|
||||||
pep8_naming: None,
|
pep8_naming: None,
|
||||||
});
|
});
|
||||||
assert_eq!(actual, expected);
|
assert_eq!(actual, expected);
|
||||||
|
|
|
@ -15,6 +15,7 @@ pub enum Plugin {
|
||||||
Flake8Print,
|
Flake8Print,
|
||||||
Flake8Quotes,
|
Flake8Quotes,
|
||||||
Flake8Annotations,
|
Flake8Annotations,
|
||||||
|
McCabe,
|
||||||
PEP8Naming,
|
PEP8Naming,
|
||||||
Pyupgrade,
|
Pyupgrade,
|
||||||
}
|
}
|
||||||
|
@ -33,6 +34,7 @@ impl FromStr for Plugin {
|
||||||
"flake8-print" => Ok(Plugin::Flake8Print),
|
"flake8-print" => Ok(Plugin::Flake8Print),
|
||||||
"flake8-quotes" => Ok(Plugin::Flake8Quotes),
|
"flake8-quotes" => Ok(Plugin::Flake8Quotes),
|
||||||
"flake8-annotations" => Ok(Plugin::Flake8Annotations),
|
"flake8-annotations" => Ok(Plugin::Flake8Annotations),
|
||||||
|
"mccabe" => Ok(Plugin::McCabe),
|
||||||
"pep8-naming" => Ok(Plugin::PEP8Naming),
|
"pep8-naming" => Ok(Plugin::PEP8Naming),
|
||||||
"pyupgrade" => Ok(Plugin::Pyupgrade),
|
"pyupgrade" => Ok(Plugin::Pyupgrade),
|
||||||
_ => Err(anyhow!("Unknown plugin: {}", string)),
|
_ => Err(anyhow!("Unknown plugin: {}", string)),
|
||||||
|
@ -46,12 +48,13 @@ impl Plugin {
|
||||||
Plugin::Flake8Bandit => CheckCodePrefix::S,
|
Plugin::Flake8Bandit => CheckCodePrefix::S,
|
||||||
Plugin::Flake8Bugbear => CheckCodePrefix::B,
|
Plugin::Flake8Bugbear => CheckCodePrefix::B,
|
||||||
Plugin::Flake8Builtins => CheckCodePrefix::A,
|
Plugin::Flake8Builtins => CheckCodePrefix::A,
|
||||||
Plugin::Flake8Comprehensions => CheckCodePrefix::C,
|
Plugin::Flake8Comprehensions => CheckCodePrefix::C4,
|
||||||
Plugin::Flake8Docstrings => CheckCodePrefix::D,
|
Plugin::Flake8Docstrings => CheckCodePrefix::D,
|
||||||
Plugin::Flake8TidyImports => CheckCodePrefix::I25,
|
Plugin::Flake8TidyImports => CheckCodePrefix::I25,
|
||||||
Plugin::Flake8Print => CheckCodePrefix::T,
|
Plugin::Flake8Print => CheckCodePrefix::T,
|
||||||
Plugin::Flake8Quotes => CheckCodePrefix::Q,
|
Plugin::Flake8Quotes => CheckCodePrefix::Q,
|
||||||
Plugin::Flake8Annotations => CheckCodePrefix::ANN,
|
Plugin::Flake8Annotations => CheckCodePrefix::ANN,
|
||||||
|
Plugin::McCabe => CheckCodePrefix::C9,
|
||||||
Plugin::PEP8Naming => CheckCodePrefix::N,
|
Plugin::PEP8Naming => CheckCodePrefix::N,
|
||||||
Plugin::Pyupgrade => CheckCodePrefix::U,
|
Plugin::Pyupgrade => CheckCodePrefix::U,
|
||||||
}
|
}
|
||||||
|
@ -62,7 +65,7 @@ impl Plugin {
|
||||||
Plugin::Flake8Bandit => vec![CheckCodePrefix::S],
|
Plugin::Flake8Bandit => vec![CheckCodePrefix::S],
|
||||||
Plugin::Flake8Bugbear => vec![CheckCodePrefix::B],
|
Plugin::Flake8Bugbear => vec![CheckCodePrefix::B],
|
||||||
Plugin::Flake8Builtins => vec![CheckCodePrefix::A],
|
Plugin::Flake8Builtins => vec![CheckCodePrefix::A],
|
||||||
Plugin::Flake8Comprehensions => vec![CheckCodePrefix::C],
|
Plugin::Flake8Comprehensions => vec![CheckCodePrefix::C4],
|
||||||
Plugin::Flake8Docstrings => {
|
Plugin::Flake8Docstrings => {
|
||||||
// Use the user-provided docstring.
|
// Use the user-provided docstring.
|
||||||
for key in ["docstring-convention", "docstring_convention"] {
|
for key in ["docstring-convention", "docstring_convention"] {
|
||||||
|
@ -83,6 +86,7 @@ impl Plugin {
|
||||||
Plugin::Flake8Print => vec![CheckCodePrefix::T],
|
Plugin::Flake8Print => vec![CheckCodePrefix::T],
|
||||||
Plugin::Flake8Quotes => vec![CheckCodePrefix::Q],
|
Plugin::Flake8Quotes => vec![CheckCodePrefix::Q],
|
||||||
Plugin::Flake8Annotations => vec![CheckCodePrefix::ANN],
|
Plugin::Flake8Annotations => vec![CheckCodePrefix::ANN],
|
||||||
|
Plugin::McCabe => vec![CheckCodePrefix::C9],
|
||||||
Plugin::PEP8Naming => vec![CheckCodePrefix::N],
|
Plugin::PEP8Naming => vec![CheckCodePrefix::N],
|
||||||
Plugin::Pyupgrade => vec![CheckCodePrefix::U],
|
Plugin::Pyupgrade => vec![CheckCodePrefix::U],
|
||||||
}
|
}
|
||||||
|
@ -326,6 +330,10 @@ pub fn infer_plugins_from_options(flake8: &HashMap<String, Option<String>>) -> V
|
||||||
"banned-modules" | "banned_modules" => {
|
"banned-modules" | "banned_modules" => {
|
||||||
plugins.insert(Plugin::Flake8TidyImports);
|
plugins.insert(Plugin::Flake8TidyImports);
|
||||||
}
|
}
|
||||||
|
// mccabe
|
||||||
|
"max-complexity" | "max_complexity" => {
|
||||||
|
plugins.insert(Plugin::McCabe);
|
||||||
|
}
|
||||||
// pep8-naming
|
// pep8-naming
|
||||||
"ignore-names" | "ignore_names" => {
|
"ignore-names" | "ignore_names" => {
|
||||||
plugins.insert(Plugin::PEP8Naming);
|
plugins.insert(Plugin::PEP8Naming);
|
||||||
|
|
108
resources/test/fixtures/C901.py
vendored
Normal file
108
resources/test/fixtures/C901.py
vendored
Normal file
|
@ -0,0 +1,108 @@
|
||||||
|
# Complexity = 1
|
||||||
|
def trivial():
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
# Complexity = 1
|
||||||
|
def expr_as_statement():
|
||||||
|
0xF00D
|
||||||
|
|
||||||
|
|
||||||
|
# Complexity = 1
|
||||||
|
def sequential(n):
|
||||||
|
k = n + 4
|
||||||
|
s = k + n
|
||||||
|
return s
|
||||||
|
|
||||||
|
|
||||||
|
# Complexity = 3
|
||||||
|
def if_elif_else_dead_path(n):
|
||||||
|
if n > 3:
|
||||||
|
return "bigger than three"
|
||||||
|
elif n > 4:
|
||||||
|
return "is never executed"
|
||||||
|
else:
|
||||||
|
return "smaller than or equal to three"
|
||||||
|
|
||||||
|
|
||||||
|
# Complexity = 3
|
||||||
|
def nested_ifs():
|
||||||
|
if n > 3:
|
||||||
|
if n > 4:
|
||||||
|
return "bigger than four"
|
||||||
|
else:
|
||||||
|
return "bigger than three"
|
||||||
|
else:
|
||||||
|
return "smaller than or equal to three"
|
||||||
|
|
||||||
|
|
||||||
|
# Complexity = 2
|
||||||
|
def for_loop():
|
||||||
|
for i in range(10):
|
||||||
|
print(i)
|
||||||
|
|
||||||
|
|
||||||
|
# Complexity = 2
|
||||||
|
def for_else(mylist):
|
||||||
|
for i in mylist:
|
||||||
|
print(i)
|
||||||
|
else:
|
||||||
|
print(None)
|
||||||
|
|
||||||
|
|
||||||
|
# Complexity = 2
|
||||||
|
def recursive(n):
|
||||||
|
if n > 4:
|
||||||
|
return f(n - 1)
|
||||||
|
else:
|
||||||
|
return n
|
||||||
|
|
||||||
|
|
||||||
|
# Complexity = 3
|
||||||
|
def nested_functions():
|
||||||
|
def a():
|
||||||
|
def b():
|
||||||
|
pass
|
||||||
|
|
||||||
|
b()
|
||||||
|
|
||||||
|
a()
|
||||||
|
|
||||||
|
|
||||||
|
# Complexity = 4
|
||||||
|
def try_else():
|
||||||
|
try:
|
||||||
|
print(1)
|
||||||
|
except TypeA:
|
||||||
|
print(2)
|
||||||
|
except TypeB:
|
||||||
|
print(3)
|
||||||
|
else:
|
||||||
|
print(4)
|
||||||
|
|
||||||
|
|
||||||
|
# Complexity = 3
|
||||||
|
def nested_try_finally():
|
||||||
|
try:
|
||||||
|
try:
|
||||||
|
print(1)
|
||||||
|
finally:
|
||||||
|
print(2)
|
||||||
|
finally:
|
||||||
|
print(3)
|
||||||
|
|
||||||
|
|
||||||
|
# Complexity = 3
|
||||||
|
async def foobar(a, b, c):
|
||||||
|
await whatever(a, b, c)
|
||||||
|
if await b:
|
||||||
|
pass
|
||||||
|
async with c:
|
||||||
|
pass
|
||||||
|
async for x in a:
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
# Complexity = 1
|
||||||
|
def annotated_assign():
|
||||||
|
x: Any = None
|
3
resources/test/fixtures/pyproject.toml
vendored
3
resources/test/fixtures/pyproject.toml
vendored
|
@ -16,6 +16,9 @@ multiline-quotes = "double"
|
||||||
docstring-quotes = "double"
|
docstring-quotes = "double"
|
||||||
avoid-escape = true
|
avoid-escape = true
|
||||||
|
|
||||||
|
[tool.ruff.mccabe]
|
||||||
|
max-complexity = 10
|
||||||
|
|
||||||
[tool.ruff.pep8-naming]
|
[tool.ruff.pep8-naming]
|
||||||
ignore-names = [
|
ignore-names = [
|
||||||
"setUp",
|
"setUp",
|
||||||
|
|
|
@ -36,8 +36,8 @@ use crate::source_code_locator::SourceCodeLocator;
|
||||||
use crate::visibility::{module_visibility, transition_scope, Modifier, Visibility, VisibleScope};
|
use crate::visibility::{module_visibility, transition_scope, Modifier, Visibility, VisibleScope};
|
||||||
use crate::{
|
use crate::{
|
||||||
docstrings, flake8_2020, flake8_annotations, flake8_bandit, flake8_bugbear, flake8_builtins,
|
docstrings, flake8_2020, flake8_annotations, flake8_bandit, flake8_bugbear, flake8_builtins,
|
||||||
flake8_comprehensions, flake8_print, flake8_tidy_imports, pep8_naming, pycodestyle, pydocstyle,
|
flake8_comprehensions, flake8_print, flake8_tidy_imports, mccabe, pep8_naming, pycodestyle,
|
||||||
pyflakes, pyupgrade,
|
pydocstyle, pyflakes, pyupgrade,
|
||||||
};
|
};
|
||||||
|
|
||||||
const GLOBAL_SCOPE_INDEX: usize = 0;
|
const GLOBAL_SCOPE_INDEX: usize = 0;
|
||||||
|
@ -349,6 +349,16 @@ where
|
||||||
if self.settings.enabled.contains(&CheckCode::B019) {
|
if self.settings.enabled.contains(&CheckCode::B019) {
|
||||||
flake8_bugbear::plugins::cached_instance_method(self, decorator_list);
|
flake8_bugbear::plugins::cached_instance_method(self, decorator_list);
|
||||||
}
|
}
|
||||||
|
if self.settings.enabled.contains(&CheckCode::C901) {
|
||||||
|
if let Some(check) = mccabe::checks::function_is_too_complex(
|
||||||
|
stmt,
|
||||||
|
name,
|
||||||
|
body,
|
||||||
|
self.settings.mccabe.max_complexity,
|
||||||
|
) {
|
||||||
|
self.add_check(check);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if self.settings.enabled.contains(&CheckCode::S107) {
|
if self.settings.enabled.contains(&CheckCode::S107) {
|
||||||
self.add_checks(
|
self.add_checks(
|
||||||
|
|
|
@ -120,6 +120,8 @@ pub enum CheckCode {
|
||||||
C415,
|
C415,
|
||||||
C416,
|
C416,
|
||||||
C417,
|
C417,
|
||||||
|
// mccabe
|
||||||
|
C901,
|
||||||
// flake8-tidy-imports
|
// flake8-tidy-imports
|
||||||
I252,
|
I252,
|
||||||
// flake8-print
|
// flake8-print
|
||||||
|
@ -260,6 +262,7 @@ pub enum CheckCategory {
|
||||||
Flake8Quotes,
|
Flake8Quotes,
|
||||||
Flake8Annotations,
|
Flake8Annotations,
|
||||||
Flake82020,
|
Flake82020,
|
||||||
|
McCabe,
|
||||||
Ruff,
|
Ruff,
|
||||||
Meta,
|
Meta,
|
||||||
}
|
}
|
||||||
|
@ -282,6 +285,7 @@ impl CheckCategory {
|
||||||
CheckCategory::Pyupgrade => "pyupgrade",
|
CheckCategory::Pyupgrade => "pyupgrade",
|
||||||
CheckCategory::Pydocstyle => "pydocstyle",
|
CheckCategory::Pydocstyle => "pydocstyle",
|
||||||
CheckCategory::PEP8Naming => "pep8-naming",
|
CheckCategory::PEP8Naming => "pep8-naming",
|
||||||
|
CheckCategory::McCabe => "mccabe",
|
||||||
CheckCategory::Ruff => "Ruff-specific rules",
|
CheckCategory::Ruff => "Ruff-specific rules",
|
||||||
CheckCategory::Meta => "Meta rules",
|
CheckCategory::Meta => "Meta rules",
|
||||||
}
|
}
|
||||||
|
@ -314,6 +318,7 @@ impl CheckCategory {
|
||||||
CheckCategory::Pydocstyle => Some("https://pypi.org/project/pydocstyle/6.1.1/"),
|
CheckCategory::Pydocstyle => Some("https://pypi.org/project/pydocstyle/6.1.1/"),
|
||||||
CheckCategory::PEP8Naming => Some("https://pypi.org/project/pep8-naming/0.13.2/"),
|
CheckCategory::PEP8Naming => Some("https://pypi.org/project/pep8-naming/0.13.2/"),
|
||||||
CheckCategory::Flake8Bandit => Some("https://pypi.org/project/flake8-bandit/4.1.1/"),
|
CheckCategory::Flake8Bandit => Some("https://pypi.org/project/flake8-bandit/4.1.1/"),
|
||||||
|
CheckCategory::McCabe => Some("https://pypi.org/project/mccabe/0.7.0/"),
|
||||||
CheckCategory::Ruff => None,
|
CheckCategory::Ruff => None,
|
||||||
CheckCategory::Meta => None,
|
CheckCategory::Meta => None,
|
||||||
}
|
}
|
||||||
|
@ -546,6 +551,8 @@ pub enum CheckKind {
|
||||||
HardcodedPasswordString(String),
|
HardcodedPasswordString(String),
|
||||||
HardcodedPasswordFuncArg(String),
|
HardcodedPasswordFuncArg(String),
|
||||||
HardcodedPasswordDefault(String),
|
HardcodedPasswordDefault(String),
|
||||||
|
// mccabe
|
||||||
|
FunctionIsTooComplex(String, usize),
|
||||||
// Ruff
|
// Ruff
|
||||||
AmbiguousUnicodeCharacterString(char, char),
|
AmbiguousUnicodeCharacterString(char, char),
|
||||||
AmbiguousUnicodeCharacterDocstring(char, char),
|
AmbiguousUnicodeCharacterDocstring(char, char),
|
||||||
|
@ -826,6 +833,7 @@ impl CheckCode {
|
||||||
CheckCode::S105 => CheckKind::HardcodedPasswordString("...".to_string()),
|
CheckCode::S105 => CheckKind::HardcodedPasswordString("...".to_string()),
|
||||||
CheckCode::S106 => CheckKind::HardcodedPasswordFuncArg("...".to_string()),
|
CheckCode::S106 => CheckKind::HardcodedPasswordFuncArg("...".to_string()),
|
||||||
CheckCode::S107 => CheckKind::HardcodedPasswordDefault("...".to_string()),
|
CheckCode::S107 => CheckKind::HardcodedPasswordDefault("...".to_string()),
|
||||||
|
CheckCode::C901 => CheckKind::FunctionIsTooComplex("...".to_string(), 10),
|
||||||
// Ruff
|
// Ruff
|
||||||
CheckCode::RUF001 => CheckKind::AmbiguousUnicodeCharacterString('𝐁', 'B'),
|
CheckCode::RUF001 => CheckKind::AmbiguousUnicodeCharacterString('𝐁', 'B'),
|
||||||
CheckCode::RUF002 => CheckKind::AmbiguousUnicodeCharacterDocstring('𝐁', 'B'),
|
CheckCode::RUF002 => CheckKind::AmbiguousUnicodeCharacterDocstring('𝐁', 'B'),
|
||||||
|
@ -1030,6 +1038,7 @@ impl CheckCode {
|
||||||
CheckCode::S105 => CheckCategory::Flake8Bandit,
|
CheckCode::S105 => CheckCategory::Flake8Bandit,
|
||||||
CheckCode::S106 => CheckCategory::Flake8Bandit,
|
CheckCode::S106 => CheckCategory::Flake8Bandit,
|
||||||
CheckCode::S107 => CheckCategory::Flake8Bandit,
|
CheckCode::S107 => CheckCategory::Flake8Bandit,
|
||||||
|
CheckCode::C901 => CheckCategory::McCabe,
|
||||||
CheckCode::RUF001 => CheckCategory::Ruff,
|
CheckCode::RUF001 => CheckCategory::Ruff,
|
||||||
CheckCode::RUF002 => CheckCategory::Ruff,
|
CheckCode::RUF002 => CheckCategory::Ruff,
|
||||||
CheckCode::RUF003 => CheckCategory::Ruff,
|
CheckCode::RUF003 => CheckCategory::Ruff,
|
||||||
|
@ -1250,6 +1259,8 @@ impl CheckKind {
|
||||||
CheckKind::HardcodedPasswordString(..) => &CheckCode::S105,
|
CheckKind::HardcodedPasswordString(..) => &CheckCode::S105,
|
||||||
CheckKind::HardcodedPasswordFuncArg(..) => &CheckCode::S106,
|
CheckKind::HardcodedPasswordFuncArg(..) => &CheckCode::S106,
|
||||||
CheckKind::HardcodedPasswordDefault(..) => &CheckCode::S107,
|
CheckKind::HardcodedPasswordDefault(..) => &CheckCode::S107,
|
||||||
|
// McCabe
|
||||||
|
CheckKind::FunctionIsTooComplex(..) => &CheckCode::C901,
|
||||||
// Ruff
|
// Ruff
|
||||||
CheckKind::AmbiguousUnicodeCharacterString(..) => &CheckCode::RUF001,
|
CheckKind::AmbiguousUnicodeCharacterString(..) => &CheckCode::RUF001,
|
||||||
CheckKind::AmbiguousUnicodeCharacterDocstring(..) => &CheckCode::RUF002,
|
CheckKind::AmbiguousUnicodeCharacterDocstring(..) => &CheckCode::RUF002,
|
||||||
|
@ -1902,6 +1913,10 @@ impl CheckKind {
|
||||||
CheckKind::HardcodedPasswordDefault(string) => {
|
CheckKind::HardcodedPasswordDefault(string) => {
|
||||||
format!("Possible hardcoded password: `\"{string}\"`")
|
format!("Possible hardcoded password: `\"{string}\"`")
|
||||||
}
|
}
|
||||||
|
// McCabe
|
||||||
|
CheckKind::FunctionIsTooComplex(name, complexity) => {
|
||||||
|
format!("`{name}` is too complex ({complexity})")
|
||||||
|
}
|
||||||
// Ruff
|
// Ruff
|
||||||
CheckKind::AmbiguousUnicodeCharacterString(confusable, representant) => {
|
CheckKind::AmbiguousUnicodeCharacterString(confusable, representant) => {
|
||||||
format!(
|
format!(
|
||||||
|
|
|
@ -83,6 +83,9 @@ pub enum CheckCodePrefix {
|
||||||
C415,
|
C415,
|
||||||
C416,
|
C416,
|
||||||
C417,
|
C417,
|
||||||
|
C9,
|
||||||
|
C90,
|
||||||
|
C901,
|
||||||
D,
|
D,
|
||||||
D1,
|
D1,
|
||||||
D10,
|
D10,
|
||||||
|
@ -497,6 +500,7 @@ impl CheckCodePrefix {
|
||||||
CheckCode::C415,
|
CheckCode::C415,
|
||||||
CheckCode::C416,
|
CheckCode::C416,
|
||||||
CheckCode::C417,
|
CheckCode::C417,
|
||||||
|
CheckCode::C901,
|
||||||
],
|
],
|
||||||
CheckCodePrefix::C4 => vec![
|
CheckCodePrefix::C4 => vec![
|
||||||
CheckCode::C400,
|
CheckCode::C400,
|
||||||
|
@ -552,6 +556,9 @@ impl CheckCodePrefix {
|
||||||
CheckCodePrefix::C415 => vec![CheckCode::C415],
|
CheckCodePrefix::C415 => vec![CheckCode::C415],
|
||||||
CheckCodePrefix::C416 => vec![CheckCode::C416],
|
CheckCodePrefix::C416 => vec![CheckCode::C416],
|
||||||
CheckCodePrefix::C417 => vec![CheckCode::C417],
|
CheckCodePrefix::C417 => vec![CheckCode::C417],
|
||||||
|
CheckCodePrefix::C9 => vec![CheckCode::C901],
|
||||||
|
CheckCodePrefix::C90 => vec![CheckCode::C901],
|
||||||
|
CheckCodePrefix::C901 => vec![CheckCode::C901],
|
||||||
CheckCodePrefix::D => vec![
|
CheckCodePrefix::D => vec![
|
||||||
CheckCode::D100,
|
CheckCode::D100,
|
||||||
CheckCode::D101,
|
CheckCode::D101,
|
||||||
|
@ -1246,6 +1253,9 @@ impl CheckCodePrefix {
|
||||||
CheckCodePrefix::C415 => PrefixSpecificity::Explicit,
|
CheckCodePrefix::C415 => PrefixSpecificity::Explicit,
|
||||||
CheckCodePrefix::C416 => PrefixSpecificity::Explicit,
|
CheckCodePrefix::C416 => PrefixSpecificity::Explicit,
|
||||||
CheckCodePrefix::C417 => PrefixSpecificity::Explicit,
|
CheckCodePrefix::C417 => PrefixSpecificity::Explicit,
|
||||||
|
CheckCodePrefix::C9 => PrefixSpecificity::Hundreds,
|
||||||
|
CheckCodePrefix::C90 => PrefixSpecificity::Tens,
|
||||||
|
CheckCodePrefix::C901 => PrefixSpecificity::Explicit,
|
||||||
CheckCodePrefix::D => PrefixSpecificity::Category,
|
CheckCodePrefix::D => PrefixSpecificity::Category,
|
||||||
CheckCodePrefix::D1 => PrefixSpecificity::Hundreds,
|
CheckCodePrefix::D1 => PrefixSpecificity::Hundreds,
|
||||||
CheckCodePrefix::D10 => PrefixSpecificity::Tens,
|
CheckCodePrefix::D10 => PrefixSpecificity::Tens,
|
||||||
|
|
|
@ -91,6 +91,9 @@ pub struct Cli {
|
||||||
/// formatting.
|
/// formatting.
|
||||||
#[arg(long)]
|
#[arg(long)]
|
||||||
pub line_length: Option<usize>,
|
pub line_length: Option<usize>,
|
||||||
|
/// Max McCabe complexity allowed for a function.
|
||||||
|
#[arg(long)]
|
||||||
|
pub max_complexity: Option<usize>,
|
||||||
/// Round-trip auto-formatting.
|
/// Round-trip auto-formatting.
|
||||||
// TODO(charlie): This should be a sub-command.
|
// TODO(charlie): This should be a sub-command.
|
||||||
#[arg(long, hide = true)]
|
#[arg(long, hide = true)]
|
||||||
|
|
|
@ -41,6 +41,7 @@ mod isort;
|
||||||
mod lex;
|
mod lex;
|
||||||
pub mod linter;
|
pub mod linter;
|
||||||
pub mod logging;
|
pub mod logging;
|
||||||
|
pub mod mccabe;
|
||||||
pub mod message;
|
pub mod message;
|
||||||
mod noqa;
|
mod noqa;
|
||||||
pub mod pep8_naming;
|
pub mod pep8_naming;
|
||||||
|
|
|
@ -269,6 +269,9 @@ fn inner_main() -> Result<ExitCode> {
|
||||||
if let Some(line_length) = cli.line_length {
|
if let Some(line_length) = cli.line_length {
|
||||||
configuration.line_length = line_length;
|
configuration.line_length = line_length;
|
||||||
}
|
}
|
||||||
|
if let Some(max_complexity) = cli.max_complexity {
|
||||||
|
configuration.mccabe.max_complexity = max_complexity;
|
||||||
|
}
|
||||||
if let Some(target_version) = cli.target_version {
|
if let Some(target_version) = cli.target_version {
|
||||||
configuration.target_version = target_version;
|
configuration.target_version = target_version;
|
||||||
}
|
}
|
||||||
|
|
73
src/mccabe/checks.rs
Normal file
73
src/mccabe/checks.rs
Normal file
|
@ -0,0 +1,73 @@
|
||||||
|
use rustpython_ast::{ExcepthandlerKind, ExprKind, Stmt, StmtKind};
|
||||||
|
|
||||||
|
use crate::ast::types::Range;
|
||||||
|
use crate::checks::{Check, CheckKind};
|
||||||
|
|
||||||
|
fn get_complexity_number(stmts: &[Stmt]) -> usize {
|
||||||
|
let mut complexity = 0;
|
||||||
|
for stmt in stmts {
|
||||||
|
match &stmt.node {
|
||||||
|
StmtKind::If { body, orelse, .. } => {
|
||||||
|
complexity += 1;
|
||||||
|
complexity += get_complexity_number(body);
|
||||||
|
complexity += get_complexity_number(orelse);
|
||||||
|
}
|
||||||
|
StmtKind::For { body, orelse, .. } | StmtKind::AsyncFor { body, orelse, .. } => {
|
||||||
|
complexity += 1;
|
||||||
|
complexity += get_complexity_number(body);
|
||||||
|
complexity += get_complexity_number(orelse);
|
||||||
|
}
|
||||||
|
StmtKind::While { test, body, orelse } => {
|
||||||
|
complexity += 1;
|
||||||
|
complexity += get_complexity_number(body);
|
||||||
|
complexity += get_complexity_number(orelse);
|
||||||
|
if let ExprKind::BoolOp { .. } = &test.node {
|
||||||
|
complexity += 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
StmtKind::Try {
|
||||||
|
body,
|
||||||
|
handlers,
|
||||||
|
orelse,
|
||||||
|
finalbody,
|
||||||
|
} => {
|
||||||
|
complexity += 1;
|
||||||
|
complexity += get_complexity_number(body);
|
||||||
|
complexity += get_complexity_number(orelse);
|
||||||
|
complexity += get_complexity_number(finalbody);
|
||||||
|
for handler in handlers {
|
||||||
|
complexity += 1;
|
||||||
|
let ExcepthandlerKind::ExceptHandler { body, .. } = &handler.node;
|
||||||
|
complexity += get_complexity_number(body);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
StmtKind::FunctionDef { body, .. } | StmtKind::AsyncFunctionDef { body, .. } => {
|
||||||
|
complexity += 1;
|
||||||
|
complexity += get_complexity_number(body);
|
||||||
|
}
|
||||||
|
StmtKind::ClassDef { body, .. } => {
|
||||||
|
complexity += 1;
|
||||||
|
complexity += get_complexity_number(body);
|
||||||
|
}
|
||||||
|
_ => {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
complexity
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn function_is_too_complex(
|
||||||
|
stmt: &Stmt,
|
||||||
|
name: &str,
|
||||||
|
body: &[Stmt],
|
||||||
|
max_complexity: usize,
|
||||||
|
) -> Option<Check> {
|
||||||
|
let complexity = get_complexity_number(body) + 1;
|
||||||
|
if complexity > max_complexity {
|
||||||
|
Some(Check::new(
|
||||||
|
CheckKind::FunctionIsTooComplex(name.to_string(), complexity),
|
||||||
|
Range::from_located(stmt),
|
||||||
|
))
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
}
|
33
src/mccabe/mod.rs
Normal file
33
src/mccabe/mod.rs
Normal file
|
@ -0,0 +1,33 @@
|
||||||
|
pub mod checks;
|
||||||
|
pub mod settings;
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
use std::path::Path;
|
||||||
|
|
||||||
|
use anyhow::Result;
|
||||||
|
use test_case::test_case;
|
||||||
|
|
||||||
|
use crate::autofix::fixer;
|
||||||
|
use crate::checks::CheckCode;
|
||||||
|
use crate::linter::test_path;
|
||||||
|
use crate::{mccabe, Settings};
|
||||||
|
|
||||||
|
#[test_case(0)]
|
||||||
|
#[test_case(3)]
|
||||||
|
#[test_case(10)]
|
||||||
|
fn max_complexity_zero(max_complexity: usize) -> Result<()> {
|
||||||
|
let snapshot = format!("max_complexity_{}", max_complexity);
|
||||||
|
let mut checks = test_path(
|
||||||
|
Path::new("./resources/test/fixtures/C901.py"),
|
||||||
|
&Settings {
|
||||||
|
mccabe: mccabe::settings::Settings { max_complexity },
|
||||||
|
..Settings::for_rules(vec![CheckCode::C901])
|
||||||
|
},
|
||||||
|
&fixer::Mode::Generate,
|
||||||
|
)?;
|
||||||
|
checks.sort_by_key(|check| check.location);
|
||||||
|
insta::assert_yaml_snapshot!(snapshot, checks);
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
28
src/mccabe/settings.rs
Normal file
28
src/mccabe/settings.rs
Normal file
|
@ -0,0 +1,28 @@
|
||||||
|
//! Settings for the `mccabe` plugin.
|
||||||
|
|
||||||
|
use serde::{Deserialize, Serialize};
|
||||||
|
|
||||||
|
#[derive(Debug, PartialEq, Eq, Serialize, Deserialize, Default)]
|
||||||
|
#[serde(deny_unknown_fields, rename_all = "kebab-case")]
|
||||||
|
pub struct Options {
|
||||||
|
pub max_complexity: Option<usize>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Hash)]
|
||||||
|
pub struct Settings {
|
||||||
|
pub max_complexity: usize,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Settings {
|
||||||
|
pub fn from_options(options: Options) -> Self {
|
||||||
|
Self {
|
||||||
|
max_complexity: options.max_complexity.unwrap_or_default(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Default for Settings {
|
||||||
|
fn default() -> Self {
|
||||||
|
Self { max_complexity: 10 }
|
||||||
|
}
|
||||||
|
}
|
170
src/mccabe/snapshots/ruff__mccabe__tests__max_complexity_0.snap
Normal file
170
src/mccabe/snapshots/ruff__mccabe__tests__max_complexity_0.snap
Normal file
|
@ -0,0 +1,170 @@
|
||||||
|
---
|
||||||
|
source: src/mccabe/mod.rs
|
||||||
|
expression: checks
|
||||||
|
---
|
||||||
|
- kind:
|
||||||
|
FunctionIsTooComplex:
|
||||||
|
- trivial
|
||||||
|
- 1
|
||||||
|
location:
|
||||||
|
row: 2
|
||||||
|
column: 0
|
||||||
|
end_location:
|
||||||
|
row: 7
|
||||||
|
column: 0
|
||||||
|
fix: ~
|
||||||
|
- kind:
|
||||||
|
FunctionIsTooComplex:
|
||||||
|
- expr_as_statement
|
||||||
|
- 1
|
||||||
|
location:
|
||||||
|
row: 7
|
||||||
|
column: 0
|
||||||
|
end_location:
|
||||||
|
row: 12
|
||||||
|
column: 0
|
||||||
|
fix: ~
|
||||||
|
- kind:
|
||||||
|
FunctionIsTooComplex:
|
||||||
|
- sequential
|
||||||
|
- 1
|
||||||
|
location:
|
||||||
|
row: 12
|
||||||
|
column: 0
|
||||||
|
end_location:
|
||||||
|
row: 19
|
||||||
|
column: 0
|
||||||
|
fix: ~
|
||||||
|
- kind:
|
||||||
|
FunctionIsTooComplex:
|
||||||
|
- if_elif_else_dead_path
|
||||||
|
- 3
|
||||||
|
location:
|
||||||
|
row: 19
|
||||||
|
column: 0
|
||||||
|
end_location:
|
||||||
|
row: 29
|
||||||
|
column: 0
|
||||||
|
fix: ~
|
||||||
|
- kind:
|
||||||
|
FunctionIsTooComplex:
|
||||||
|
- nested_ifs
|
||||||
|
- 3
|
||||||
|
location:
|
||||||
|
row: 29
|
||||||
|
column: 0
|
||||||
|
end_location:
|
||||||
|
row: 40
|
||||||
|
column: 0
|
||||||
|
fix: ~
|
||||||
|
- kind:
|
||||||
|
FunctionIsTooComplex:
|
||||||
|
- for_loop
|
||||||
|
- 2
|
||||||
|
location:
|
||||||
|
row: 40
|
||||||
|
column: 0
|
||||||
|
end_location:
|
||||||
|
row: 46
|
||||||
|
column: 0
|
||||||
|
fix: ~
|
||||||
|
- kind:
|
||||||
|
FunctionIsTooComplex:
|
||||||
|
- for_else
|
||||||
|
- 2
|
||||||
|
location:
|
||||||
|
row: 46
|
||||||
|
column: 0
|
||||||
|
end_location:
|
||||||
|
row: 54
|
||||||
|
column: 0
|
||||||
|
fix: ~
|
||||||
|
- kind:
|
||||||
|
FunctionIsTooComplex:
|
||||||
|
- recursive
|
||||||
|
- 2
|
||||||
|
location:
|
||||||
|
row: 54
|
||||||
|
column: 0
|
||||||
|
end_location:
|
||||||
|
row: 62
|
||||||
|
column: 0
|
||||||
|
fix: ~
|
||||||
|
- kind:
|
||||||
|
FunctionIsTooComplex:
|
||||||
|
- nested_functions
|
||||||
|
- 3
|
||||||
|
location:
|
||||||
|
row: 62
|
||||||
|
column: 0
|
||||||
|
end_location:
|
||||||
|
row: 73
|
||||||
|
column: 0
|
||||||
|
fix: ~
|
||||||
|
- kind:
|
||||||
|
FunctionIsTooComplex:
|
||||||
|
- a
|
||||||
|
- 2
|
||||||
|
location:
|
||||||
|
row: 63
|
||||||
|
column: 4
|
||||||
|
end_location:
|
||||||
|
row: 69
|
||||||
|
column: 4
|
||||||
|
fix: ~
|
||||||
|
- kind:
|
||||||
|
FunctionIsTooComplex:
|
||||||
|
- b
|
||||||
|
- 1
|
||||||
|
location:
|
||||||
|
row: 64
|
||||||
|
column: 8
|
||||||
|
end_location:
|
||||||
|
row: 67
|
||||||
|
column: 8
|
||||||
|
fix: ~
|
||||||
|
- kind:
|
||||||
|
FunctionIsTooComplex:
|
||||||
|
- try_else
|
||||||
|
- 4
|
||||||
|
location:
|
||||||
|
row: 73
|
||||||
|
column: 0
|
||||||
|
end_location:
|
||||||
|
row: 85
|
||||||
|
column: 0
|
||||||
|
fix: ~
|
||||||
|
- kind:
|
||||||
|
FunctionIsTooComplex:
|
||||||
|
- nested_try_finally
|
||||||
|
- 3
|
||||||
|
location:
|
||||||
|
row: 85
|
||||||
|
column: 0
|
||||||
|
end_location:
|
||||||
|
row: 96
|
||||||
|
column: 0
|
||||||
|
fix: ~
|
||||||
|
- kind:
|
||||||
|
FunctionIsTooComplex:
|
||||||
|
- foobar
|
||||||
|
- 3
|
||||||
|
location:
|
||||||
|
row: 96
|
||||||
|
column: 0
|
||||||
|
end_location:
|
||||||
|
row: 107
|
||||||
|
column: 0
|
||||||
|
fix: ~
|
||||||
|
- kind:
|
||||||
|
FunctionIsTooComplex:
|
||||||
|
- annotated_assign
|
||||||
|
- 1
|
||||||
|
location:
|
||||||
|
row: 107
|
||||||
|
column: 0
|
||||||
|
end_location:
|
||||||
|
row: 109
|
||||||
|
column: 0
|
||||||
|
fix: ~
|
||||||
|
|
|
@ -0,0 +1,6 @@
|
||||||
|
---
|
||||||
|
source: src/mccabe/mod.rs
|
||||||
|
expression: checks
|
||||||
|
---
|
||||||
|
[]
|
||||||
|
|
|
@ -0,0 +1,16 @@
|
||||||
|
---
|
||||||
|
source: src/mccabe/mod.rs
|
||||||
|
expression: checks
|
||||||
|
---
|
||||||
|
- kind:
|
||||||
|
FunctionIsTooComplex:
|
||||||
|
- try_else
|
||||||
|
- 4
|
||||||
|
location:
|
||||||
|
row: 73
|
||||||
|
column: 0
|
||||||
|
end_location:
|
||||||
|
row: 85
|
||||||
|
column: 0
|
||||||
|
fix: ~
|
||||||
|
|
|
@ -13,7 +13,8 @@ use crate::checks_gen::CheckCodePrefix;
|
||||||
use crate::settings::pyproject::load_options;
|
use crate::settings::pyproject::load_options;
|
||||||
use crate::settings::types::{FilePattern, PerFileIgnore, PythonVersion};
|
use crate::settings::types::{FilePattern, PerFileIgnore, PythonVersion};
|
||||||
use crate::{
|
use crate::{
|
||||||
flake8_annotations, flake8_bugbear, flake8_quotes, flake8_tidy_imports, fs, isort, pep8_naming,
|
flake8_annotations, flake8_bugbear, flake8_quotes, flake8_tidy_imports, fs, isort, mccabe,
|
||||||
|
pep8_naming,
|
||||||
};
|
};
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
|
@ -36,6 +37,7 @@ pub struct Configuration {
|
||||||
pub flake8_quotes: flake8_quotes::settings::Settings,
|
pub flake8_quotes: flake8_quotes::settings::Settings,
|
||||||
pub flake8_tidy_imports: flake8_tidy_imports::settings::Settings,
|
pub flake8_tidy_imports: flake8_tidy_imports::settings::Settings,
|
||||||
pub isort: isort::settings::Settings,
|
pub isort: isort::settings::Settings,
|
||||||
|
pub mccabe: mccabe::settings::Settings,
|
||||||
pub pep8_naming: pep8_naming::settings::Settings,
|
pub pep8_naming: pep8_naming::settings::Settings,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -153,6 +155,10 @@ impl Configuration {
|
||||||
.isort
|
.isort
|
||||||
.map(isort::settings::Settings::from_options)
|
.map(isort::settings::Settings::from_options)
|
||||||
.unwrap_or_default(),
|
.unwrap_or_default(),
|
||||||
|
mccabe: options
|
||||||
|
.mccabe
|
||||||
|
.map(mccabe::settings::Settings::from_options)
|
||||||
|
.unwrap_or_default(),
|
||||||
pep8_naming: options
|
pep8_naming: options
|
||||||
.pep8_naming
|
.pep8_naming
|
||||||
.map(pep8_naming::settings::Settings::from_options)
|
.map(pep8_naming::settings::Settings::from_options)
|
||||||
|
|
|
@ -14,7 +14,8 @@ use crate::checks_gen::{CheckCodePrefix, PrefixSpecificity};
|
||||||
use crate::settings::configuration::Configuration;
|
use crate::settings::configuration::Configuration;
|
||||||
use crate::settings::types::{FilePattern, PerFileIgnore, PythonVersion};
|
use crate::settings::types::{FilePattern, PerFileIgnore, PythonVersion};
|
||||||
use crate::{
|
use crate::{
|
||||||
flake8_annotations, flake8_bugbear, flake8_quotes, flake8_tidy_imports, isort, pep8_naming,
|
flake8_annotations, flake8_bugbear, flake8_quotes, flake8_tidy_imports, isort, mccabe,
|
||||||
|
pep8_naming,
|
||||||
};
|
};
|
||||||
|
|
||||||
pub mod configuration;
|
pub mod configuration;
|
||||||
|
@ -39,6 +40,7 @@ pub struct Settings {
|
||||||
pub flake8_quotes: flake8_quotes::settings::Settings,
|
pub flake8_quotes: flake8_quotes::settings::Settings,
|
||||||
pub flake8_tidy_imports: flake8_tidy_imports::settings::Settings,
|
pub flake8_tidy_imports: flake8_tidy_imports::settings::Settings,
|
||||||
pub isort: isort::settings::Settings,
|
pub isort: isort::settings::Settings,
|
||||||
|
pub mccabe: mccabe::settings::Settings,
|
||||||
pub pep8_naming: pep8_naming::settings::Settings,
|
pub pep8_naming: pep8_naming::settings::Settings,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -59,6 +61,7 @@ impl Settings {
|
||||||
flake8_quotes: config.flake8_quotes,
|
flake8_quotes: config.flake8_quotes,
|
||||||
flake8_tidy_imports: config.flake8_tidy_imports,
|
flake8_tidy_imports: config.flake8_tidy_imports,
|
||||||
isort: config.isort,
|
isort: config.isort,
|
||||||
|
mccabe: config.mccabe,
|
||||||
line_length: config.line_length,
|
line_length: config.line_length,
|
||||||
pep8_naming: config.pep8_naming,
|
pep8_naming: config.pep8_naming,
|
||||||
per_file_ignores: config.per_file_ignores,
|
per_file_ignores: config.per_file_ignores,
|
||||||
|
@ -82,6 +85,7 @@ impl Settings {
|
||||||
flake8_quotes: Default::default(),
|
flake8_quotes: Default::default(),
|
||||||
flake8_tidy_imports: Default::default(),
|
flake8_tidy_imports: Default::default(),
|
||||||
isort: Default::default(),
|
isort: Default::default(),
|
||||||
|
mccabe: Default::default(),
|
||||||
pep8_naming: Default::default(),
|
pep8_naming: Default::default(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -101,6 +105,7 @@ impl Settings {
|
||||||
flake8_quotes: Default::default(),
|
flake8_quotes: Default::default(),
|
||||||
flake8_tidy_imports: Default::default(),
|
flake8_tidy_imports: Default::default(),
|
||||||
isort: Default::default(),
|
isort: Default::default(),
|
||||||
|
mccabe: Default::default(),
|
||||||
pep8_naming: Default::default(),
|
pep8_naming: Default::default(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -124,6 +129,7 @@ impl Hash for Settings {
|
||||||
self.flake8_quotes.hash(state);
|
self.flake8_quotes.hash(state);
|
||||||
self.flake8_tidy_imports.hash(state);
|
self.flake8_tidy_imports.hash(state);
|
||||||
self.isort.hash(state);
|
self.isort.hash(state);
|
||||||
|
self.mccabe.hash(state);
|
||||||
self.pep8_naming.hash(state);
|
self.pep8_naming.hash(state);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -6,7 +6,8 @@ use serde::{Deserialize, Serialize};
|
||||||
use crate::checks_gen::CheckCodePrefix;
|
use crate::checks_gen::CheckCodePrefix;
|
||||||
use crate::settings::types::PythonVersion;
|
use crate::settings::types::PythonVersion;
|
||||||
use crate::{
|
use crate::{
|
||||||
flake8_annotations, flake8_bugbear, flake8_quotes, flake8_tidy_imports, isort, pep8_naming,
|
flake8_annotations, flake8_bugbear, flake8_quotes, flake8_tidy_imports, isort, mccabe,
|
||||||
|
pep8_naming,
|
||||||
};
|
};
|
||||||
|
|
||||||
#[derive(Debug, PartialEq, Eq, Serialize, Deserialize, Default)]
|
#[derive(Debug, PartialEq, Eq, Serialize, Deserialize, Default)]
|
||||||
|
@ -29,6 +30,7 @@ pub struct Options {
|
||||||
pub flake8_quotes: Option<flake8_quotes::settings::Options>,
|
pub flake8_quotes: Option<flake8_quotes::settings::Options>,
|
||||||
pub flake8_tidy_imports: Option<flake8_tidy_imports::settings::Options>,
|
pub flake8_tidy_imports: Option<flake8_tidy_imports::settings::Options>,
|
||||||
pub isort: Option<isort::settings::Options>,
|
pub isort: Option<isort::settings::Options>,
|
||||||
|
pub mccabe: Option<mccabe::settings::Options>,
|
||||||
pub pep8_naming: Option<pep8_naming::settings::Options>,
|
pub pep8_naming: Option<pep8_naming::settings::Options>,
|
||||||
// Tables are required to go last.
|
// Tables are required to go last.
|
||||||
pub per_file_ignores: Option<FnvHashMap<String, Vec<CheckCodePrefix>>>,
|
pub per_file_ignores: Option<FnvHashMap<String, Vec<CheckCodePrefix>>>,
|
||||||
|
|
|
@ -110,7 +110,7 @@ mod tests {
|
||||||
find_project_root, find_pyproject_toml, parse_pyproject_toml, Options, Pyproject, Tools,
|
find_project_root, find_pyproject_toml, parse_pyproject_toml, Options, Pyproject, Tools,
|
||||||
};
|
};
|
||||||
use crate::settings::types::PatternPrefixPair;
|
use crate::settings::types::PatternPrefixPair;
|
||||||
use crate::{flake8_bugbear, flake8_quotes, flake8_tidy_imports, pep8_naming};
|
use crate::{flake8_bugbear, flake8_quotes, flake8_tidy_imports, mccabe, pep8_naming};
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn deserialize() -> Result<()> {
|
fn deserialize() -> Result<()> {
|
||||||
|
@ -151,6 +151,7 @@ mod tests {
|
||||||
flake8_quotes: None,
|
flake8_quotes: None,
|
||||||
flake8_tidy_imports: None,
|
flake8_tidy_imports: None,
|
||||||
isort: None,
|
isort: None,
|
||||||
|
mccabe: None,
|
||||||
pep8_naming: None,
|
pep8_naming: None,
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
@ -184,6 +185,7 @@ line-length = 79
|
||||||
flake8_quotes: None,
|
flake8_quotes: None,
|
||||||
flake8_tidy_imports: None,
|
flake8_tidy_imports: None,
|
||||||
isort: None,
|
isort: None,
|
||||||
|
mccabe: None,
|
||||||
pep8_naming: None,
|
pep8_naming: None,
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
@ -217,6 +219,7 @@ exclude = ["foo.py"]
|
||||||
flake8_quotes: None,
|
flake8_quotes: None,
|
||||||
flake8_tidy_imports: None,
|
flake8_tidy_imports: None,
|
||||||
isort: None,
|
isort: None,
|
||||||
|
mccabe: None,
|
||||||
pep8_naming: None,
|
pep8_naming: None,
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
@ -250,6 +253,7 @@ select = ["E501"]
|
||||||
flake8_quotes: None,
|
flake8_quotes: None,
|
||||||
flake8_tidy_imports: None,
|
flake8_tidy_imports: None,
|
||||||
isort: None,
|
isort: None,
|
||||||
|
mccabe: None,
|
||||||
pep8_naming: None,
|
pep8_naming: None,
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
@ -284,6 +288,7 @@ ignore = ["E501"]
|
||||||
flake8_quotes: None,
|
flake8_quotes: None,
|
||||||
flake8_tidy_imports: None,
|
flake8_tidy_imports: None,
|
||||||
isort: None,
|
isort: None,
|
||||||
|
mccabe: None,
|
||||||
pep8_naming: None,
|
pep8_naming: None,
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
@ -376,6 +381,9 @@ other-attribute = 1
|
||||||
ban_relative_imports: Some(Strictness::Parents)
|
ban_relative_imports: Some(Strictness::Parents)
|
||||||
}),
|
}),
|
||||||
isort: None,
|
isort: None,
|
||||||
|
mccabe: Some(mccabe::settings::Options {
|
||||||
|
max_complexity: Some(10),
|
||||||
|
}),
|
||||||
pep8_naming: Some(pep8_naming::settings::Options {
|
pep8_naming: Some(pep8_naming::settings::Options {
|
||||||
ignore_names: Some(vec![
|
ignore_names: Some(vec![
|
||||||
"setUp".to_string(),
|
"setUp".to_string(),
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue