mirror of
https://github.com/astral-sh/ruff.git
synced 2025-10-01 14:21:53 +00:00
Implement flake8-annotations (#625)
This commit is contained in:
parent
050f34dd25
commit
99d9aa61bf
59 changed files with 1987 additions and 213 deletions
24
README.md
24
README.md
|
@ -52,8 +52,9 @@ Read the [launch blog post](https://notes.crmarsh.com/python-tooling-could-be-mu
|
||||||
8. [flake8-builtins](#flake8-builtins)
|
8. [flake8-builtins](#flake8-builtins)
|
||||||
9. [flake8-print](#flake8-print)
|
9. [flake8-print](#flake8-print)
|
||||||
10. [flake8-quotes](#flake8-quotes)
|
10. [flake8-quotes](#flake8-quotes)
|
||||||
11. [Ruff-specific rules](#ruff-specific-rules)
|
11. [flake8-annotations](#flake8-annotations)
|
||||||
12. [Meta rules](#meta-rules)
|
12. [Ruff-specific rules](#ruff-specific-rules)
|
||||||
|
13. [Meta rules](#meta-rules)
|
||||||
5. [Editor Integrations](#editor-integrations)
|
5. [Editor Integrations](#editor-integrations)
|
||||||
6. [FAQ](#faq)
|
6. [FAQ](#faq)
|
||||||
7. [Development](#development)
|
7. [Development](#development)
|
||||||
|
@ -523,6 +524,23 @@ For more, see [flake8-quotes](https://pypi.org/project/flake8-quotes/3.3.1/) on
|
||||||
| Q002 | BadQuotesDocstring | Single quote docstring found but double quotes preferred | |
|
| Q002 | BadQuotesDocstring | Single quote docstring found but double quotes preferred | |
|
||||||
| Q003 | AvoidQuoteEscape | Change outer quotes to avoid escaping inner quotes | |
|
| Q003 | AvoidQuoteEscape | Change outer quotes to avoid escaping inner quotes | |
|
||||||
|
|
||||||
|
### flake8-annotations
|
||||||
|
|
||||||
|
For more, see [flake8-annotations](https://pypi.org/project/flake8-annotations/2.9.1/) on PyPI.
|
||||||
|
|
||||||
|
| Code | Name | Message | Fix |
|
||||||
|
| ---- | ---- | ------- | --- |
|
||||||
|
| ANN001 | MissingTypeFunctionArgument | Missing type annotation for function argument | |
|
||||||
|
| ANN002 | MissingTypeArgs | Missing type annotation for `*args` | |
|
||||||
|
| ANN003 | MissingTypeKwargs | Missing type annotation for `**kwargs` | |
|
||||||
|
| ANN101 | MissingTypeSelf | Missing type annotation for `self` in method | |
|
||||||
|
| ANN102 | MissingTypeCls | Missing type annotation for `cls` in classmethod | |
|
||||||
|
| ANN201 | MissingReturnTypePublicFunction | Missing return type annotation for public function | |
|
||||||
|
| ANN202 | MissingReturnTypePrivateFunction | Missing return type annotation for private function | |
|
||||||
|
| ANN204 | MissingReturnTypeMagicMethod | Missing return type annotation for magic method | |
|
||||||
|
| ANN205 | MissingReturnTypeStaticMethod | Missing return type annotation for staticmethod | |
|
||||||
|
| ANN206 | MissingReturnTypeClassMethod | Missing return type annotation for classmethod | |
|
||||||
|
|
||||||
### Ruff-specific rules
|
### Ruff-specific rules
|
||||||
|
|
||||||
| Code | Name | Message | Fix |
|
| Code | Name | Message | Fix |
|
||||||
|
@ -605,6 +623,7 @@ including:
|
||||||
- [`flake8-super`](https://pypi.org/project/flake8-super/)
|
- [`flake8-super`](https://pypi.org/project/flake8-super/)
|
||||||
- [`flake8-print`](https://pypi.org/project/flake8-print/)
|
- [`flake8-print`](https://pypi.org/project/flake8-print/)
|
||||||
- [`flake8-quotes`](https://pypi.org/project/flake8-quotes/)
|
- [`flake8-quotes`](https://pypi.org/project/flake8-quotes/)
|
||||||
|
- [`flake8-annotations`](https://pypi.org/project/flake8-annotations/)
|
||||||
- [`flake8-comprehensions`](https://pypi.org/project/flake8-comprehensions/)
|
- [`flake8-comprehensions`](https://pypi.org/project/flake8-comprehensions/)
|
||||||
- [`flake8-bugbear`](https://pypi.org/project/flake8-bugbear/) (15/32)
|
- [`flake8-bugbear`](https://pypi.org/project/flake8-bugbear/) (15/32)
|
||||||
- [`pyupgrade`](https://pypi.org/project/pyupgrade/) (11/34)
|
- [`pyupgrade`](https://pypi.org/project/pyupgrade/) (11/34)
|
||||||
|
@ -627,6 +646,7 @@ Today, Ruff can be used to replace Flake8 when used with any of the following pl
|
||||||
- [`flake8-super`](https://pypi.org/project/flake8-super/)
|
- [`flake8-super`](https://pypi.org/project/flake8-super/)
|
||||||
- [`flake8-print`](https://pypi.org/project/flake8-print/)
|
- [`flake8-print`](https://pypi.org/project/flake8-print/)
|
||||||
- [`flake8-quotes`](https://pypi.org/project/flake8-quotes/)
|
- [`flake8-quotes`](https://pypi.org/project/flake8-quotes/)
|
||||||
|
- [`flake8-annotations`](https://pypi.org/project/flake8-annotations/)
|
||||||
- [`flake8-comprehensions`](https://pypi.org/project/flake8-comprehensions/)
|
- [`flake8-comprehensions`](https://pypi.org/project/flake8-comprehensions/)
|
||||||
- [`flake8-bugbear`](https://pypi.org/project/flake8-bugbear/) (15/32)
|
- [`flake8-bugbear`](https://pypi.org/project/flake8-bugbear/) (15/32)
|
||||||
|
|
||||||
|
|
|
@ -4,7 +4,7 @@ use anyhow::Result;
|
||||||
use ruff::flake8_quotes::settings::Quote;
|
use ruff::flake8_quotes::settings::Quote;
|
||||||
use ruff::settings::options::Options;
|
use ruff::settings::options::Options;
|
||||||
use ruff::settings::pyproject::Pyproject;
|
use ruff::settings::pyproject::Pyproject;
|
||||||
use ruff::{flake8_quotes, pep8_naming};
|
use ruff::{flake8_annotations, flake8_quotes, pep8_naming};
|
||||||
|
|
||||||
use crate::plugin::Plugin;
|
use crate::plugin::Plugin;
|
||||||
use crate::{parser, plugin};
|
use crate::{parser, plugin};
|
||||||
|
@ -39,6 +39,7 @@ pub fn convert(
|
||||||
|
|
||||||
// Parse each supported option.
|
// Parse each supported option.
|
||||||
let mut options: Options = Default::default();
|
let mut options: Options = Default::default();
|
||||||
|
let mut flake8_annotations: flake8_annotations::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 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 {
|
||||||
|
@ -78,6 +79,25 @@ pub fn convert(
|
||||||
Err(e) => eprintln!("Unable to parse '{key}' property: {e}"),
|
Err(e) => eprintln!("Unable to parse '{key}' property: {e}"),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
// flake8-annotations
|
||||||
|
"suppress-none-returning" | "suppress_none_returning" => {
|
||||||
|
match parser::parse_bool(value.as_ref()) {
|
||||||
|
Ok(bool) => flake8_annotations.suppress_none_returning = Some(bool),
|
||||||
|
Err(e) => eprintln!("Unable to parse '{key}' property: {e}"),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
"suppress-dummy-args" | "suppress_dummy_args" => {
|
||||||
|
match parser::parse_bool(value.as_ref()) {
|
||||||
|
Ok(bool) => flake8_annotations.suppress_dummy_args = Some(bool),
|
||||||
|
Err(e) => eprintln!("Unable to parse '{key}' property: {e}"),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
"mypy-init-return" | "mypy_init_return" => {
|
||||||
|
match parser::parse_bool(value.as_ref()) {
|
||||||
|
Ok(bool) => flake8_annotations.mypy_init_return = Some(bool),
|
||||||
|
Err(e) => eprintln!("Unable to parse '{key}' property: {e}"),
|
||||||
|
}
|
||||||
|
}
|
||||||
// flake8-quotes
|
// flake8-quotes
|
||||||
"quotes" | "inline-quotes" | "inline_quotes" => match value.trim() {
|
"quotes" | "inline-quotes" | "inline_quotes" => match value.trim() {
|
||||||
"'" | "single" => flake8_quotes.inline_quotes = Some(Quote::Single),
|
"'" | "single" => flake8_quotes.inline_quotes = Some(Quote::Single),
|
||||||
|
@ -94,10 +114,9 @@ pub fn convert(
|
||||||
"\"" | "double" => flake8_quotes.docstring_quotes = Some(Quote::Single),
|
"\"" | "double" => flake8_quotes.docstring_quotes = Some(Quote::Single),
|
||||||
_ => eprintln!("Unexpected '{key}' value: {value}"),
|
_ => eprintln!("Unexpected '{key}' value: {value}"),
|
||||||
},
|
},
|
||||||
"avoid-escape" | "avoid_escape" => match value.trim() {
|
"avoid-escape" | "avoid_escape" => match parser::parse_bool(value.as_ref()) {
|
||||||
"true" => flake8_quotes.avoid_escape = Some(true),
|
Ok(bool) => flake8_quotes.avoid_escape = Some(bool),
|
||||||
"false" => flake8_quotes.avoid_escape = Some(false),
|
Err(e) => eprintln!("Unable to parse '{key}' property: {e}"),
|
||||||
_ => eprintln!("Unexpected '{key}' value: {value}"),
|
|
||||||
},
|
},
|
||||||
// pep8-naming
|
// pep8-naming
|
||||||
"ignore-names" | "ignore_names" => {
|
"ignore-names" | "ignore_names" => {
|
||||||
|
@ -166,6 +185,7 @@ mod tests {
|
||||||
per_file_ignores: None,
|
per_file_ignores: None,
|
||||||
dummy_variable_rgx: None,
|
dummy_variable_rgx: None,
|
||||||
target_version: None,
|
target_version: None,
|
||||||
|
flake8_annotations: None,
|
||||||
flake8_quotes: None,
|
flake8_quotes: None,
|
||||||
pep8_naming: None,
|
pep8_naming: None,
|
||||||
});
|
});
|
||||||
|
@ -195,6 +215,7 @@ mod tests {
|
||||||
per_file_ignores: None,
|
per_file_ignores: None,
|
||||||
dummy_variable_rgx: None,
|
dummy_variable_rgx: None,
|
||||||
target_version: None,
|
target_version: None,
|
||||||
|
flake8_annotations: None,
|
||||||
flake8_quotes: None,
|
flake8_quotes: None,
|
||||||
pep8_naming: None,
|
pep8_naming: None,
|
||||||
});
|
});
|
||||||
|
@ -224,6 +245,7 @@ mod tests {
|
||||||
per_file_ignores: None,
|
per_file_ignores: None,
|
||||||
dummy_variable_rgx: None,
|
dummy_variable_rgx: None,
|
||||||
target_version: None,
|
target_version: None,
|
||||||
|
flake8_annotations: None,
|
||||||
flake8_quotes: None,
|
flake8_quotes: None,
|
||||||
pep8_naming: None,
|
pep8_naming: None,
|
||||||
});
|
});
|
||||||
|
@ -253,6 +275,7 @@ mod tests {
|
||||||
per_file_ignores: None,
|
per_file_ignores: None,
|
||||||
dummy_variable_rgx: None,
|
dummy_variable_rgx: None,
|
||||||
target_version: None,
|
target_version: None,
|
||||||
|
flake8_annotations: None,
|
||||||
flake8_quotes: None,
|
flake8_quotes: None,
|
||||||
pep8_naming: None,
|
pep8_naming: None,
|
||||||
});
|
});
|
||||||
|
@ -282,6 +305,7 @@ mod tests {
|
||||||
per_file_ignores: None,
|
per_file_ignores: None,
|
||||||
dummy_variable_rgx: None,
|
dummy_variable_rgx: None,
|
||||||
target_version: None,
|
target_version: None,
|
||||||
|
flake8_annotations: None,
|
||||||
flake8_quotes: Some(flake8_quotes::settings::Options {
|
flake8_quotes: Some(flake8_quotes::settings::Options {
|
||||||
inline_quotes: Some(flake8_quotes::settings::Quote::Single),
|
inline_quotes: Some(flake8_quotes::settings::Quote::Single),
|
||||||
multiline_quotes: None,
|
multiline_quotes: None,
|
||||||
|
@ -354,6 +378,7 @@ mod tests {
|
||||||
per_file_ignores: None,
|
per_file_ignores: None,
|
||||||
dummy_variable_rgx: None,
|
dummy_variable_rgx: None,
|
||||||
target_version: None,
|
target_version: None,
|
||||||
|
flake8_annotations: None,
|
||||||
flake8_quotes: None,
|
flake8_quotes: None,
|
||||||
pep8_naming: None,
|
pep8_naming: None,
|
||||||
});
|
});
|
||||||
|
@ -384,6 +409,7 @@ mod tests {
|
||||||
per_file_ignores: None,
|
per_file_ignores: None,
|
||||||
dummy_variable_rgx: None,
|
dummy_variable_rgx: None,
|
||||||
target_version: None,
|
target_version: None,
|
||||||
|
flake8_annotations: None,
|
||||||
flake8_quotes: Some(flake8_quotes::settings::Options {
|
flake8_quotes: Some(flake8_quotes::settings::Options {
|
||||||
inline_quotes: Some(flake8_quotes::settings::Quote::Single),
|
inline_quotes: Some(flake8_quotes::settings::Quote::Single),
|
||||||
multiline_quotes: None,
|
multiline_quotes: None,
|
||||||
|
|
|
@ -37,6 +37,15 @@ pub fn parse_strings(value: &str) -> Vec<String> {
|
||||||
.collect()
|
.collect()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Parse a boolean.
|
||||||
|
pub fn parse_bool(value: &str) -> Result<bool> {
|
||||||
|
match value.trim() {
|
||||||
|
"true" => Ok(true),
|
||||||
|
"false" => Ok(false),
|
||||||
|
_ => Err(anyhow::anyhow!("Unexpected boolean value: {value}")),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
struct Token {
|
struct Token {
|
||||||
token_name: TokenType,
|
token_name: TokenType,
|
||||||
|
|
|
@ -12,6 +12,7 @@ pub enum Plugin {
|
||||||
Flake8Docstrings,
|
Flake8Docstrings,
|
||||||
Flake8Print,
|
Flake8Print,
|
||||||
Flake8Quotes,
|
Flake8Quotes,
|
||||||
|
Flake8Annotations,
|
||||||
PEP8Naming,
|
PEP8Naming,
|
||||||
Pyupgrade,
|
Pyupgrade,
|
||||||
}
|
}
|
||||||
|
@ -27,6 +28,7 @@ impl FromStr for Plugin {
|
||||||
"flake8-docstrings" => Ok(Plugin::Flake8Docstrings),
|
"flake8-docstrings" => Ok(Plugin::Flake8Docstrings),
|
||||||
"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),
|
||||||
"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)),
|
||||||
|
@ -58,6 +60,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::PEP8Naming => vec![CheckCodePrefix::N],
|
Plugin::PEP8Naming => vec![CheckCodePrefix::N],
|
||||||
Plugin::Pyupgrade => vec![CheckCodePrefix::U],
|
Plugin::Pyupgrade => vec![CheckCodePrefix::U],
|
||||||
}
|
}
|
||||||
|
@ -262,6 +265,31 @@ pub fn infer_plugins(flake8: &HashMap<String, Option<String>>) -> Vec<Plugin> {
|
||||||
"avoid-escape" | "avoid_escape" => {
|
"avoid-escape" | "avoid_escape" => {
|
||||||
plugins.insert(Plugin::Flake8Quotes);
|
plugins.insert(Plugin::Flake8Quotes);
|
||||||
}
|
}
|
||||||
|
// flake8-annotations
|
||||||
|
"suppress-none-returning" | "suppress_none_returning" => {
|
||||||
|
plugins.insert(Plugin::Flake8Annotations);
|
||||||
|
}
|
||||||
|
"suppress-dummy-args" | "suppress_dummy_args" => {
|
||||||
|
plugins.insert(Plugin::Flake8Annotations);
|
||||||
|
}
|
||||||
|
"allow-untyped-defs" | "allow_untyped_defs" => {
|
||||||
|
plugins.insert(Plugin::Flake8Annotations);
|
||||||
|
}
|
||||||
|
"allow-untyped-nested" | "allow_untyped_nested" => {
|
||||||
|
plugins.insert(Plugin::Flake8Annotations);
|
||||||
|
}
|
||||||
|
"mypy-init-return" | "mypy_init_return" => {
|
||||||
|
plugins.insert(Plugin::Flake8Annotations);
|
||||||
|
}
|
||||||
|
"dispatch-decorators" | "dispatch_decorators" => {
|
||||||
|
plugins.insert(Plugin::Flake8Annotations);
|
||||||
|
}
|
||||||
|
"overload-decorators" | "overload_decorators" => {
|
||||||
|
plugins.insert(Plugin::Flake8Annotations);
|
||||||
|
}
|
||||||
|
"allow-star-arg-any" | "allow_star_arg_any" => {
|
||||||
|
plugins.insert(Plugin::Flake8Annotations);
|
||||||
|
}
|
||||||
// pep8-naming
|
// pep8-naming
|
||||||
"ignore-names" | "ignore_names" => {
|
"ignore-names" | "ignore_names" => {
|
||||||
plugins.insert(Plugin::PEP8Naming);
|
plugins.insert(Plugin::PEP8Naming);
|
||||||
|
|
5
foo.py
Normal file
5
foo.py
Normal file
|
@ -0,0 +1,5 @@
|
||||||
|
def f():
|
||||||
|
class A:
|
||||||
|
return 1
|
||||||
|
|
||||||
|
f()
|
33
resources/test/fixtures/flake8_annotations/annotation_presence.py
vendored
Normal file
33
resources/test/fixtures/flake8_annotations/annotation_presence.py
vendored
Normal file
|
@ -0,0 +1,33 @@
|
||||||
|
# Error
|
||||||
|
def foo(a, b):
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
# Error
|
||||||
|
def foo(a: int, b):
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
# Error
|
||||||
|
def foo(a: int, b) -> int:
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
# Error
|
||||||
|
def foo(a: int, b: int):
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
# Error
|
||||||
|
def foo():
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
# OK
|
||||||
|
def foo(a: int, b: int) -> int:
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
# OK
|
||||||
|
def foo() -> int:
|
||||||
|
pass
|
41
resources/test/fixtures/flake8_annotations/mypy_init_return.py
vendored
Normal file
41
resources/test/fixtures/flake8_annotations/mypy_init_return.py
vendored
Normal file
|
@ -0,0 +1,41 @@
|
||||||
|
"""Test case expected to be run with `mypy_init_return = True`."""
|
||||||
|
|
||||||
|
# Error
|
||||||
|
class Foo:
|
||||||
|
def __init__(self):
|
||||||
|
...
|
||||||
|
|
||||||
|
|
||||||
|
# Error
|
||||||
|
class Foo:
|
||||||
|
def __init__(self, foo):
|
||||||
|
...
|
||||||
|
|
||||||
|
|
||||||
|
# OK
|
||||||
|
class Foo:
|
||||||
|
def __init__(self, foo) -> None:
|
||||||
|
...
|
||||||
|
|
||||||
|
|
||||||
|
# OK
|
||||||
|
class Foo:
|
||||||
|
def __init__(self) -> None:
|
||||||
|
...
|
||||||
|
|
||||||
|
|
||||||
|
# OK
|
||||||
|
class Foo:
|
||||||
|
def __init__(self, foo: int):
|
||||||
|
...
|
||||||
|
|
||||||
|
|
||||||
|
# OK
|
||||||
|
class Foo:
|
||||||
|
def __init__(self, foo: int) -> None:
|
||||||
|
...
|
||||||
|
|
||||||
|
|
||||||
|
# Error
|
||||||
|
def __init__(self, foo: int):
|
||||||
|
...
|
26
resources/test/fixtures/flake8_annotations/suppress_dummy_args.py
vendored
Normal file
26
resources/test/fixtures/flake8_annotations/suppress_dummy_args.py
vendored
Normal file
|
@ -0,0 +1,26 @@
|
||||||
|
"""Test case expected to be run with `suppress_dummy_args = True`."""
|
||||||
|
|
||||||
|
# OK
|
||||||
|
def foo(_) -> None:
|
||||||
|
...
|
||||||
|
|
||||||
|
|
||||||
|
# OK
|
||||||
|
def foo(*_) -> None:
|
||||||
|
...
|
||||||
|
|
||||||
|
|
||||||
|
# OK
|
||||||
|
def foo(**_) -> None:
|
||||||
|
...
|
||||||
|
|
||||||
|
|
||||||
|
# OK
|
||||||
|
def foo(a: int, _) -> None:
|
||||||
|
...
|
||||||
|
|
||||||
|
|
||||||
|
# OK
|
||||||
|
def foo() -> None:
|
||||||
|
def bar(_) -> None:
|
||||||
|
...
|
55
resources/test/fixtures/flake8_annotations/suppress_none_returning.py
vendored
Normal file
55
resources/test/fixtures/flake8_annotations/suppress_none_returning.py
vendored
Normal file
|
@ -0,0 +1,55 @@
|
||||||
|
"""Test case expected to be run with `suppress_none_returning = True`."""
|
||||||
|
|
||||||
|
# OK
|
||||||
|
def foo():
|
||||||
|
a = 2 + 2
|
||||||
|
|
||||||
|
|
||||||
|
# OK
|
||||||
|
def foo():
|
||||||
|
return
|
||||||
|
|
||||||
|
|
||||||
|
# OK
|
||||||
|
def foo():
|
||||||
|
return None
|
||||||
|
|
||||||
|
|
||||||
|
# OK
|
||||||
|
def foo():
|
||||||
|
a = 2 + 2
|
||||||
|
if a == 4:
|
||||||
|
return
|
||||||
|
else:
|
||||||
|
return
|
||||||
|
|
||||||
|
|
||||||
|
# OK
|
||||||
|
def foo():
|
||||||
|
a = 2 + 2
|
||||||
|
if a == 4:
|
||||||
|
return None
|
||||||
|
else:
|
||||||
|
return
|
||||||
|
|
||||||
|
|
||||||
|
# OK
|
||||||
|
def foo():
|
||||||
|
def bar() -> bool:
|
||||||
|
return True
|
||||||
|
|
||||||
|
bar()
|
||||||
|
|
||||||
|
|
||||||
|
# Error
|
||||||
|
def foo():
|
||||||
|
return True
|
||||||
|
|
||||||
|
|
||||||
|
# Error
|
||||||
|
def foo():
|
||||||
|
a = 2 + 2
|
||||||
|
if a == 4:
|
||||||
|
return True
|
||||||
|
else:
|
||||||
|
return
|
|
@ -32,8 +32,8 @@ use crate::settings::Settings;
|
||||||
use crate::source_code_locator::SourceCodeLocator;
|
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_bugbear, flake8_builtins, flake8_comprehensions, flake8_print, pep8_naming,
|
docstrings, flake8_annotations, flake8_bugbear, flake8_builtins, flake8_comprehensions,
|
||||||
pycodestyle, pydocstyle, pyflakes, pyupgrade,
|
flake8_print, pep8_naming, pycodestyle, pydocstyle, pyflakes, pyupgrade,
|
||||||
};
|
};
|
||||||
|
|
||||||
const GLOBAL_SCOPE_INDEX: usize = 0;
|
const GLOBAL_SCOPE_INDEX: usize = 0;
|
||||||
|
@ -57,8 +57,8 @@ pub struct Checker<'a> {
|
||||||
pub(crate) locator: &'a SourceCodeLocator<'a>,
|
pub(crate) locator: &'a SourceCodeLocator<'a>,
|
||||||
// Computed checks.
|
// Computed checks.
|
||||||
checks: Vec<Check>,
|
checks: Vec<Check>,
|
||||||
// Docstring tracking.
|
// Function and class definition tracking (e.g., for docstring enforcement).
|
||||||
docstrings: Vec<(Definition<'a>, Visibility)>,
|
definitions: Vec<(Definition<'a>, Visibility)>,
|
||||||
// Edit tracking.
|
// Edit tracking.
|
||||||
// TODO(charlie): Instead of exposing deletions, wrap in a public API.
|
// TODO(charlie): Instead of exposing deletions, wrap in a public API.
|
||||||
pub(crate) deletions: BTreeSet<usize>,
|
pub(crate) deletions: BTreeSet<usize>,
|
||||||
|
@ -100,7 +100,7 @@ impl<'a> Checker<'a> {
|
||||||
path,
|
path,
|
||||||
locator,
|
locator,
|
||||||
checks: Default::default(),
|
checks: Default::default(),
|
||||||
docstrings: Default::default(),
|
definitions: Default::default(),
|
||||||
deletions: Default::default(),
|
deletions: Default::default(),
|
||||||
parents: Default::default(),
|
parents: Default::default(),
|
||||||
parent_stack: Default::default(),
|
parent_stack: Default::default(),
|
||||||
|
@ -834,7 +834,8 @@ where
|
||||||
&Documentable::Function,
|
&Documentable::Function,
|
||||||
);
|
);
|
||||||
let scope = transition_scope(&self.visible_scope, stmt, &Documentable::Function);
|
let scope = transition_scope(&self.visible_scope, stmt, &Documentable::Function);
|
||||||
self.docstrings.push((definition, scope.visibility.clone()));
|
self.definitions
|
||||||
|
.push((definition, scope.visibility.clone()));
|
||||||
self.visible_scope = scope;
|
self.visible_scope = scope;
|
||||||
|
|
||||||
self.deferred_functions.push((
|
self.deferred_functions.push((
|
||||||
|
@ -852,7 +853,8 @@ where
|
||||||
&Documentable::Class,
|
&Documentable::Class,
|
||||||
);
|
);
|
||||||
let scope = transition_scope(&self.visible_scope, stmt, &Documentable::Class);
|
let scope = transition_scope(&self.visible_scope, stmt, &Documentable::Class);
|
||||||
self.docstrings.push((definition, scope.visibility.clone()));
|
self.definitions
|
||||||
|
.push((definition, scope.visibility.clone()));
|
||||||
self.visible_scope = scope;
|
self.visible_scope = scope;
|
||||||
|
|
||||||
for stmt in body {
|
for stmt in body {
|
||||||
|
@ -2105,7 +2107,7 @@ impl<'a> Checker<'a> {
|
||||||
'b: 'a,
|
'b: 'a,
|
||||||
{
|
{
|
||||||
let docstring = docstrings::extraction::docstring_from(python_ast);
|
let docstring = docstrings::extraction::docstring_from(python_ast);
|
||||||
self.docstrings.push((
|
self.definitions.push((
|
||||||
Definition {
|
Definition {
|
||||||
kind: if self.path.ends_with("__init__.py") {
|
kind: if self.path.ends_with("__init__.py") {
|
||||||
DefinitionKind::Package
|
DefinitionKind::Package
|
||||||
|
@ -2354,68 +2356,86 @@ impl<'a> Checker<'a> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn check_docstrings(&mut self) {
|
fn check_definitions(&mut self) {
|
||||||
while let Some((docstring, visibility)) = self.docstrings.pop() {
|
while let Some((definition, visibility)) = self.definitions.pop() {
|
||||||
if !pydocstyle::plugins::not_empty(self, &docstring) {
|
// flake8-annotations
|
||||||
|
if self.settings.enabled.contains(&CheckCode::ANN001)
|
||||||
|
|| self.settings.enabled.contains(&CheckCode::ANN002)
|
||||||
|
|| self.settings.enabled.contains(&CheckCode::ANN003)
|
||||||
|
|| self.settings.enabled.contains(&CheckCode::ANN201)
|
||||||
|
|| self.settings.enabled.contains(&CheckCode::ANN202)
|
||||||
|
|| self.settings.enabled.contains(&CheckCode::ANN001)
|
||||||
|
|| self.settings.enabled.contains(&CheckCode::ANN002)
|
||||||
|
|| self.settings.enabled.contains(&CheckCode::ANN003)
|
||||||
|
|| self.settings.enabled.contains(&CheckCode::ANN101)
|
||||||
|
|| self.settings.enabled.contains(&CheckCode::ANN102)
|
||||||
|
|| self.settings.enabled.contains(&CheckCode::ANN201)
|
||||||
|
|| self.settings.enabled.contains(&CheckCode::ANN202)
|
||||||
|
{
|
||||||
|
flake8_annotations::plugins::definition(self, &definition, &visibility);
|
||||||
|
}
|
||||||
|
|
||||||
|
// pydocstyle
|
||||||
|
if !pydocstyle::plugins::not_empty(self, &definition) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
if !pydocstyle::plugins::not_missing(self, &docstring, &visibility) {
|
if !pydocstyle::plugins::not_missing(self, &definition, &visibility) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
if self.settings.enabled.contains(&CheckCode::D200) {
|
if self.settings.enabled.contains(&CheckCode::D200) {
|
||||||
pydocstyle::plugins::one_liner(self, &docstring);
|
pydocstyle::plugins::one_liner(self, &definition);
|
||||||
}
|
}
|
||||||
if self.settings.enabled.contains(&CheckCode::D201)
|
if self.settings.enabled.contains(&CheckCode::D201)
|
||||||
|| self.settings.enabled.contains(&CheckCode::D202)
|
|| self.settings.enabled.contains(&CheckCode::D202)
|
||||||
{
|
{
|
||||||
pydocstyle::plugins::blank_before_after_function(self, &docstring);
|
pydocstyle::plugins::blank_before_after_function(self, &definition);
|
||||||
}
|
}
|
||||||
if self.settings.enabled.contains(&CheckCode::D203)
|
if self.settings.enabled.contains(&CheckCode::D203)
|
||||||
|| self.settings.enabled.contains(&CheckCode::D204)
|
|| self.settings.enabled.contains(&CheckCode::D204)
|
||||||
|| self.settings.enabled.contains(&CheckCode::D211)
|
|| self.settings.enabled.contains(&CheckCode::D211)
|
||||||
{
|
{
|
||||||
pydocstyle::plugins::blank_before_after_class(self, &docstring);
|
pydocstyle::plugins::blank_before_after_class(self, &definition);
|
||||||
}
|
}
|
||||||
if self.settings.enabled.contains(&CheckCode::D205) {
|
if self.settings.enabled.contains(&CheckCode::D205) {
|
||||||
pydocstyle::plugins::blank_after_summary(self, &docstring);
|
pydocstyle::plugins::blank_after_summary(self, &definition);
|
||||||
}
|
}
|
||||||
if self.settings.enabled.contains(&CheckCode::D206)
|
if self.settings.enabled.contains(&CheckCode::D206)
|
||||||
|| self.settings.enabled.contains(&CheckCode::D207)
|
|| self.settings.enabled.contains(&CheckCode::D207)
|
||||||
|| self.settings.enabled.contains(&CheckCode::D208)
|
|| self.settings.enabled.contains(&CheckCode::D208)
|
||||||
{
|
{
|
||||||
pydocstyle::plugins::indent(self, &docstring);
|
pydocstyle::plugins::indent(self, &definition);
|
||||||
}
|
}
|
||||||
if self.settings.enabled.contains(&CheckCode::D209) {
|
if self.settings.enabled.contains(&CheckCode::D209) {
|
||||||
pydocstyle::plugins::newline_after_last_paragraph(self, &docstring);
|
pydocstyle::plugins::newline_after_last_paragraph(self, &definition);
|
||||||
}
|
}
|
||||||
if self.settings.enabled.contains(&CheckCode::D210) {
|
if self.settings.enabled.contains(&CheckCode::D210) {
|
||||||
pydocstyle::plugins::no_surrounding_whitespace(self, &docstring);
|
pydocstyle::plugins::no_surrounding_whitespace(self, &definition);
|
||||||
}
|
}
|
||||||
if self.settings.enabled.contains(&CheckCode::D212)
|
if self.settings.enabled.contains(&CheckCode::D212)
|
||||||
|| self.settings.enabled.contains(&CheckCode::D213)
|
|| self.settings.enabled.contains(&CheckCode::D213)
|
||||||
{
|
{
|
||||||
pydocstyle::plugins::multi_line_summary_start(self, &docstring);
|
pydocstyle::plugins::multi_line_summary_start(self, &definition);
|
||||||
}
|
}
|
||||||
if self.settings.enabled.contains(&CheckCode::D300) {
|
if self.settings.enabled.contains(&CheckCode::D300) {
|
||||||
pydocstyle::plugins::triple_quotes(self, &docstring);
|
pydocstyle::plugins::triple_quotes(self, &definition);
|
||||||
}
|
}
|
||||||
if self.settings.enabled.contains(&CheckCode::D400) {
|
if self.settings.enabled.contains(&CheckCode::D400) {
|
||||||
pydocstyle::plugins::ends_with_period(self, &docstring);
|
pydocstyle::plugins::ends_with_period(self, &definition);
|
||||||
}
|
}
|
||||||
if self.settings.enabled.contains(&CheckCode::D402) {
|
if self.settings.enabled.contains(&CheckCode::D402) {
|
||||||
pydocstyle::plugins::no_signature(self, &docstring);
|
pydocstyle::plugins::no_signature(self, &definition);
|
||||||
}
|
}
|
||||||
if self.settings.enabled.contains(&CheckCode::D403) {
|
if self.settings.enabled.contains(&CheckCode::D403) {
|
||||||
pydocstyle::plugins::capitalized(self, &docstring);
|
pydocstyle::plugins::capitalized(self, &definition);
|
||||||
}
|
}
|
||||||
if self.settings.enabled.contains(&CheckCode::D404) {
|
if self.settings.enabled.contains(&CheckCode::D404) {
|
||||||
pydocstyle::plugins::starts_with_this(self, &docstring);
|
pydocstyle::plugins::starts_with_this(self, &definition);
|
||||||
}
|
}
|
||||||
if self.settings.enabled.contains(&CheckCode::D415) {
|
if self.settings.enabled.contains(&CheckCode::D415) {
|
||||||
pydocstyle::plugins::ends_with_punctuation(self, &docstring);
|
pydocstyle::plugins::ends_with_punctuation(self, &definition);
|
||||||
}
|
}
|
||||||
if self.settings.enabled.contains(&CheckCode::D418) {
|
if self.settings.enabled.contains(&CheckCode::D418) {
|
||||||
pydocstyle::plugins::if_needed(self, &docstring);
|
pydocstyle::plugins::if_needed(self, &definition);
|
||||||
}
|
}
|
||||||
if self.settings.enabled.contains(&CheckCode::D212)
|
if self.settings.enabled.contains(&CheckCode::D212)
|
||||||
|| self.settings.enabled.contains(&CheckCode::D214)
|
|| self.settings.enabled.contains(&CheckCode::D214)
|
||||||
|
@ -2433,7 +2453,7 @@ impl<'a> Checker<'a> {
|
||||||
|| self.settings.enabled.contains(&CheckCode::D416)
|
|| self.settings.enabled.contains(&CheckCode::D416)
|
||||||
|| self.settings.enabled.contains(&CheckCode::D417)
|
|| self.settings.enabled.contains(&CheckCode::D417)
|
||||||
{
|
{
|
||||||
pydocstyle::plugins::sections(self, &docstring);
|
pydocstyle::plugins::sections(self, &definition);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -2512,7 +2532,7 @@ pub fn check_ast(
|
||||||
checker.check_dead_scopes();
|
checker.check_dead_scopes();
|
||||||
|
|
||||||
// Check docstrings.
|
// Check docstrings.
|
||||||
checker.check_docstrings();
|
checker.check_definitions();
|
||||||
|
|
||||||
checker.checks
|
checker.checks
|
||||||
}
|
}
|
||||||
|
|
|
@ -115,6 +115,17 @@ pub enum CheckCode {
|
||||||
Q001,
|
Q001,
|
||||||
Q002,
|
Q002,
|
||||||
Q003,
|
Q003,
|
||||||
|
// flake8-annotations
|
||||||
|
ANN001,
|
||||||
|
ANN002,
|
||||||
|
ANN003,
|
||||||
|
ANN101,
|
||||||
|
ANN102,
|
||||||
|
ANN201,
|
||||||
|
ANN202,
|
||||||
|
ANN204,
|
||||||
|
ANN205,
|
||||||
|
ANN206,
|
||||||
// pyupgrade
|
// pyupgrade
|
||||||
U001,
|
U001,
|
||||||
U002,
|
U002,
|
||||||
|
@ -205,6 +216,7 @@ pub enum CheckCategory {
|
||||||
Flake8Builtins,
|
Flake8Builtins,
|
||||||
Flake8Print,
|
Flake8Print,
|
||||||
Flake8Quotes,
|
Flake8Quotes,
|
||||||
|
Flake8Annotations,
|
||||||
Ruff,
|
Ruff,
|
||||||
Meta,
|
Meta,
|
||||||
}
|
}
|
||||||
|
@ -219,6 +231,7 @@ impl CheckCategory {
|
||||||
CheckCategory::Flake8Comprehensions => "flake8-comprehensions",
|
CheckCategory::Flake8Comprehensions => "flake8-comprehensions",
|
||||||
CheckCategory::Flake8Print => "flake8-print",
|
CheckCategory::Flake8Print => "flake8-print",
|
||||||
CheckCategory::Flake8Quotes => "flake8-quotes",
|
CheckCategory::Flake8Quotes => "flake8-quotes",
|
||||||
|
CheckCategory::Flake8Annotations => "flake8-annotations",
|
||||||
CheckCategory::Pyupgrade => "pyupgrade",
|
CheckCategory::Pyupgrade => "pyupgrade",
|
||||||
CheckCategory::Pydocstyle => "pydocstyle",
|
CheckCategory::Pydocstyle => "pydocstyle",
|
||||||
CheckCategory::PEP8Naming => "pep8-naming",
|
CheckCategory::PEP8Naming => "pep8-naming",
|
||||||
|
@ -242,6 +255,9 @@ impl CheckCategory {
|
||||||
}
|
}
|
||||||
CheckCategory::Flake8Print => Some("https://pypi.org/project/flake8-print/5.0.0/"),
|
CheckCategory::Flake8Print => Some("https://pypi.org/project/flake8-print/5.0.0/"),
|
||||||
CheckCategory::Flake8Quotes => Some("https://pypi.org/project/flake8-quotes/3.3.1/"),
|
CheckCategory::Flake8Quotes => Some("https://pypi.org/project/flake8-quotes/3.3.1/"),
|
||||||
|
CheckCategory::Flake8Annotations => {
|
||||||
|
Some("https://pypi.org/project/flake8-annotations/2.9.1/")
|
||||||
|
}
|
||||||
CheckCategory::Pyupgrade => Some("https://pypi.org/project/pyupgrade/3.2.0/"),
|
CheckCategory::Pyupgrade => Some("https://pypi.org/project/pyupgrade/3.2.0/"),
|
||||||
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/"),
|
||||||
|
@ -357,6 +373,17 @@ pub enum CheckKind {
|
||||||
BadQuotesMultilineString(Quote),
|
BadQuotesMultilineString(Quote),
|
||||||
BadQuotesDocstring(Quote),
|
BadQuotesDocstring(Quote),
|
||||||
AvoidQuoteEscape,
|
AvoidQuoteEscape,
|
||||||
|
// flake8-annotations
|
||||||
|
MissingTypeFunctionArgument,
|
||||||
|
MissingTypeArgs,
|
||||||
|
MissingTypeKwargs,
|
||||||
|
MissingTypeSelf,
|
||||||
|
MissingTypeCls,
|
||||||
|
MissingReturnTypePublicFunction,
|
||||||
|
MissingReturnTypePrivateFunction,
|
||||||
|
MissingReturnTypeMagicMethod,
|
||||||
|
MissingReturnTypeStaticMethod,
|
||||||
|
MissingReturnTypeClassMethod,
|
||||||
// pyupgrade
|
// pyupgrade
|
||||||
TypeOfPrimitive(Primitive),
|
TypeOfPrimitive(Primitive),
|
||||||
UnnecessaryAbspath,
|
UnnecessaryAbspath,
|
||||||
|
@ -565,6 +592,17 @@ impl CheckCode {
|
||||||
CheckCode::Q001 => CheckKind::BadQuotesMultilineString(Quote::Double),
|
CheckCode::Q001 => CheckKind::BadQuotesMultilineString(Quote::Double),
|
||||||
CheckCode::Q002 => CheckKind::BadQuotesDocstring(Quote::Double),
|
CheckCode::Q002 => CheckKind::BadQuotesDocstring(Quote::Double),
|
||||||
CheckCode::Q003 => CheckKind::AvoidQuoteEscape,
|
CheckCode::Q003 => CheckKind::AvoidQuoteEscape,
|
||||||
|
// flake8-annotations
|
||||||
|
CheckCode::ANN001 => CheckKind::MissingTypeFunctionArgument,
|
||||||
|
CheckCode::ANN002 => CheckKind::MissingTypeArgs,
|
||||||
|
CheckCode::ANN003 => CheckKind::MissingTypeKwargs,
|
||||||
|
CheckCode::ANN101 => CheckKind::MissingTypeSelf,
|
||||||
|
CheckCode::ANN102 => CheckKind::MissingTypeCls,
|
||||||
|
CheckCode::ANN201 => CheckKind::MissingReturnTypePublicFunction,
|
||||||
|
CheckCode::ANN202 => CheckKind::MissingReturnTypePrivateFunction,
|
||||||
|
CheckCode::ANN204 => CheckKind::MissingReturnTypeMagicMethod,
|
||||||
|
CheckCode::ANN205 => CheckKind::MissingReturnTypeStaticMethod,
|
||||||
|
CheckCode::ANN206 => CheckKind::MissingReturnTypeClassMethod,
|
||||||
// pyupgrade
|
// pyupgrade
|
||||||
CheckCode::U001 => CheckKind::UselessMetaclassType,
|
CheckCode::U001 => CheckKind::UselessMetaclassType,
|
||||||
CheckCode::U002 => CheckKind::UnnecessaryAbspath,
|
CheckCode::U002 => CheckKind::UnnecessaryAbspath,
|
||||||
|
@ -747,6 +785,16 @@ impl CheckCode {
|
||||||
CheckCode::Q001 => CheckCategory::Flake8Quotes,
|
CheckCode::Q001 => CheckCategory::Flake8Quotes,
|
||||||
CheckCode::Q002 => CheckCategory::Flake8Quotes,
|
CheckCode::Q002 => CheckCategory::Flake8Quotes,
|
||||||
CheckCode::Q003 => CheckCategory::Flake8Quotes,
|
CheckCode::Q003 => CheckCategory::Flake8Quotes,
|
||||||
|
CheckCode::ANN001 => CheckCategory::Flake8Annotations,
|
||||||
|
CheckCode::ANN002 => CheckCategory::Flake8Annotations,
|
||||||
|
CheckCode::ANN003 => CheckCategory::Flake8Annotations,
|
||||||
|
CheckCode::ANN101 => CheckCategory::Flake8Annotations,
|
||||||
|
CheckCode::ANN102 => CheckCategory::Flake8Annotations,
|
||||||
|
CheckCode::ANN201 => CheckCategory::Flake8Annotations,
|
||||||
|
CheckCode::ANN202 => CheckCategory::Flake8Annotations,
|
||||||
|
CheckCode::ANN204 => CheckCategory::Flake8Annotations,
|
||||||
|
CheckCode::ANN205 => CheckCategory::Flake8Annotations,
|
||||||
|
CheckCode::ANN206 => CheckCategory::Flake8Annotations,
|
||||||
CheckCode::U001 => CheckCategory::Pyupgrade,
|
CheckCode::U001 => CheckCategory::Pyupgrade,
|
||||||
CheckCode::U002 => CheckCategory::Pyupgrade,
|
CheckCode::U002 => CheckCategory::Pyupgrade,
|
||||||
CheckCode::U003 => CheckCategory::Pyupgrade,
|
CheckCode::U003 => CheckCategory::Pyupgrade,
|
||||||
|
@ -915,6 +963,17 @@ impl CheckKind {
|
||||||
CheckKind::BadQuotesMultilineString(_) => &CheckCode::Q001,
|
CheckKind::BadQuotesMultilineString(_) => &CheckCode::Q001,
|
||||||
CheckKind::BadQuotesDocstring(_) => &CheckCode::Q002,
|
CheckKind::BadQuotesDocstring(_) => &CheckCode::Q002,
|
||||||
CheckKind::AvoidQuoteEscape => &CheckCode::Q003,
|
CheckKind::AvoidQuoteEscape => &CheckCode::Q003,
|
||||||
|
// flake8-annotations
|
||||||
|
CheckKind::MissingTypeFunctionArgument => &CheckCode::ANN001,
|
||||||
|
CheckKind::MissingTypeArgs => &CheckCode::ANN002,
|
||||||
|
CheckKind::MissingTypeKwargs => &CheckCode::ANN003,
|
||||||
|
CheckKind::MissingTypeSelf => &CheckCode::ANN101,
|
||||||
|
CheckKind::MissingTypeCls => &CheckCode::ANN102,
|
||||||
|
CheckKind::MissingReturnTypePublicFunction => &CheckCode::ANN201,
|
||||||
|
CheckKind::MissingReturnTypePrivateFunction => &CheckCode::ANN202,
|
||||||
|
CheckKind::MissingReturnTypeMagicMethod => &CheckCode::ANN204,
|
||||||
|
CheckKind::MissingReturnTypeStaticMethod => &CheckCode::ANN205,
|
||||||
|
CheckKind::MissingReturnTypeClassMethod => &CheckCode::ANN206,
|
||||||
// pyupgrade
|
// pyupgrade
|
||||||
CheckKind::TypeOfPrimitive(_) => &CheckCode::U003,
|
CheckKind::TypeOfPrimitive(_) => &CheckCode::U003,
|
||||||
CheckKind::UnnecessaryAbspath => &CheckCode::U002,
|
CheckKind::UnnecessaryAbspath => &CheckCode::U002,
|
||||||
|
@ -1300,6 +1359,33 @@ impl CheckKind {
|
||||||
CheckKind::AvoidQuoteEscape => {
|
CheckKind::AvoidQuoteEscape => {
|
||||||
"Change outer quotes to avoid escaping inner quotes".to_string()
|
"Change outer quotes to avoid escaping inner quotes".to_string()
|
||||||
}
|
}
|
||||||
|
// flake8-annotations
|
||||||
|
CheckKind::MissingTypeFunctionArgument => {
|
||||||
|
"Missing type annotation for function argument".to_string()
|
||||||
|
}
|
||||||
|
CheckKind::MissingTypeArgs => "Missing type annotation for `*args`".to_string(),
|
||||||
|
CheckKind::MissingTypeKwargs => "Missing type annotation for `**kwargs`".to_string(),
|
||||||
|
CheckKind::MissingTypeSelf => {
|
||||||
|
"Missing type annotation for `self` in method".to_string()
|
||||||
|
}
|
||||||
|
CheckKind::MissingTypeCls => {
|
||||||
|
"Missing type annotation for `cls` in classmethod".to_string()
|
||||||
|
}
|
||||||
|
CheckKind::MissingReturnTypePublicFunction => {
|
||||||
|
"Missing return type annotation for public function".to_string()
|
||||||
|
}
|
||||||
|
CheckKind::MissingReturnTypePrivateFunction => {
|
||||||
|
"Missing return type annotation for private function".to_string()
|
||||||
|
}
|
||||||
|
CheckKind::MissingReturnTypeMagicMethod => {
|
||||||
|
"Missing return type annotation for magic method".to_string()
|
||||||
|
}
|
||||||
|
CheckKind::MissingReturnTypeStaticMethod => {
|
||||||
|
"Missing return type annotation for staticmethod".to_string()
|
||||||
|
}
|
||||||
|
CheckKind::MissingReturnTypeClassMethod => {
|
||||||
|
"Missing return type annotation for classmethod".to_string()
|
||||||
|
}
|
||||||
// pyupgrade
|
// pyupgrade
|
||||||
CheckKind::TypeOfPrimitive(primitive) => {
|
CheckKind::TypeOfPrimitive(primitive) => {
|
||||||
format!("Use `{}` instead of `type(...)`", primitive.builtin())
|
format!("Use `{}` instead of `type(...)`", primitive.builtin())
|
||||||
|
|
|
@ -13,6 +13,23 @@ pub enum CheckCodePrefix {
|
||||||
A001,
|
A001,
|
||||||
A002,
|
A002,
|
||||||
A003,
|
A003,
|
||||||
|
ANN,
|
||||||
|
ANN0,
|
||||||
|
ANN00,
|
||||||
|
ANN001,
|
||||||
|
ANN002,
|
||||||
|
ANN003,
|
||||||
|
ANN1,
|
||||||
|
ANN10,
|
||||||
|
ANN101,
|
||||||
|
ANN102,
|
||||||
|
ANN2,
|
||||||
|
ANN20,
|
||||||
|
ANN201,
|
||||||
|
ANN202,
|
||||||
|
ANN204,
|
||||||
|
ANN205,
|
||||||
|
ANN206,
|
||||||
B,
|
B,
|
||||||
B0,
|
B0,
|
||||||
B00,
|
B00,
|
||||||
|
@ -257,6 +274,46 @@ impl CheckCodePrefix {
|
||||||
CheckCodePrefix::A001 => vec![CheckCode::A001],
|
CheckCodePrefix::A001 => vec![CheckCode::A001],
|
||||||
CheckCodePrefix::A002 => vec![CheckCode::A002],
|
CheckCodePrefix::A002 => vec![CheckCode::A002],
|
||||||
CheckCodePrefix::A003 => vec![CheckCode::A003],
|
CheckCodePrefix::A003 => vec![CheckCode::A003],
|
||||||
|
CheckCodePrefix::ANN => vec![
|
||||||
|
CheckCode::ANN001,
|
||||||
|
CheckCode::ANN002,
|
||||||
|
CheckCode::ANN003,
|
||||||
|
CheckCode::ANN101,
|
||||||
|
CheckCode::ANN102,
|
||||||
|
CheckCode::ANN201,
|
||||||
|
CheckCode::ANN202,
|
||||||
|
CheckCode::ANN204,
|
||||||
|
CheckCode::ANN205,
|
||||||
|
CheckCode::ANN206,
|
||||||
|
],
|
||||||
|
CheckCodePrefix::ANN0 => vec![CheckCode::ANN001, CheckCode::ANN002, CheckCode::ANN003],
|
||||||
|
CheckCodePrefix::ANN00 => vec![CheckCode::ANN001, CheckCode::ANN002, CheckCode::ANN003],
|
||||||
|
CheckCodePrefix::ANN001 => vec![CheckCode::ANN001],
|
||||||
|
CheckCodePrefix::ANN002 => vec![CheckCode::ANN002],
|
||||||
|
CheckCodePrefix::ANN003 => vec![CheckCode::ANN003],
|
||||||
|
CheckCodePrefix::ANN1 => vec![CheckCode::ANN101, CheckCode::ANN102],
|
||||||
|
CheckCodePrefix::ANN10 => vec![CheckCode::ANN101, CheckCode::ANN102],
|
||||||
|
CheckCodePrefix::ANN101 => vec![CheckCode::ANN101],
|
||||||
|
CheckCodePrefix::ANN102 => vec![CheckCode::ANN102],
|
||||||
|
CheckCodePrefix::ANN2 => vec![
|
||||||
|
CheckCode::ANN201,
|
||||||
|
CheckCode::ANN202,
|
||||||
|
CheckCode::ANN204,
|
||||||
|
CheckCode::ANN205,
|
||||||
|
CheckCode::ANN206,
|
||||||
|
],
|
||||||
|
CheckCodePrefix::ANN20 => vec![
|
||||||
|
CheckCode::ANN201,
|
||||||
|
CheckCode::ANN202,
|
||||||
|
CheckCode::ANN204,
|
||||||
|
CheckCode::ANN205,
|
||||||
|
CheckCode::ANN206,
|
||||||
|
],
|
||||||
|
CheckCodePrefix::ANN201 => vec![CheckCode::ANN201],
|
||||||
|
CheckCodePrefix::ANN202 => vec![CheckCode::ANN202],
|
||||||
|
CheckCodePrefix::ANN204 => vec![CheckCode::ANN204],
|
||||||
|
CheckCodePrefix::ANN205 => vec![CheckCode::ANN205],
|
||||||
|
CheckCodePrefix::ANN206 => vec![CheckCode::ANN206],
|
||||||
CheckCodePrefix::B => vec![
|
CheckCodePrefix::B => vec![
|
||||||
CheckCode::B002,
|
CheckCode::B002,
|
||||||
CheckCode::B003,
|
CheckCode::B003,
|
||||||
|
@ -931,6 +988,23 @@ impl CheckCodePrefix {
|
||||||
CheckCodePrefix::A001 => PrefixSpecificity::Explicit,
|
CheckCodePrefix::A001 => PrefixSpecificity::Explicit,
|
||||||
CheckCodePrefix::A002 => PrefixSpecificity::Explicit,
|
CheckCodePrefix::A002 => PrefixSpecificity::Explicit,
|
||||||
CheckCodePrefix::A003 => PrefixSpecificity::Explicit,
|
CheckCodePrefix::A003 => PrefixSpecificity::Explicit,
|
||||||
|
CheckCodePrefix::ANN => PrefixSpecificity::Category,
|
||||||
|
CheckCodePrefix::ANN0 => PrefixSpecificity::Hundreds,
|
||||||
|
CheckCodePrefix::ANN00 => PrefixSpecificity::Tens,
|
||||||
|
CheckCodePrefix::ANN001 => PrefixSpecificity::Explicit,
|
||||||
|
CheckCodePrefix::ANN002 => PrefixSpecificity::Explicit,
|
||||||
|
CheckCodePrefix::ANN003 => PrefixSpecificity::Explicit,
|
||||||
|
CheckCodePrefix::ANN1 => PrefixSpecificity::Hundreds,
|
||||||
|
CheckCodePrefix::ANN10 => PrefixSpecificity::Tens,
|
||||||
|
CheckCodePrefix::ANN101 => PrefixSpecificity::Explicit,
|
||||||
|
CheckCodePrefix::ANN102 => PrefixSpecificity::Explicit,
|
||||||
|
CheckCodePrefix::ANN2 => PrefixSpecificity::Hundreds,
|
||||||
|
CheckCodePrefix::ANN20 => PrefixSpecificity::Tens,
|
||||||
|
CheckCodePrefix::ANN201 => PrefixSpecificity::Explicit,
|
||||||
|
CheckCodePrefix::ANN202 => PrefixSpecificity::Explicit,
|
||||||
|
CheckCodePrefix::ANN204 => PrefixSpecificity::Explicit,
|
||||||
|
CheckCodePrefix::ANN205 => PrefixSpecificity::Explicit,
|
||||||
|
CheckCodePrefix::ANN206 => PrefixSpecificity::Explicit,
|
||||||
CheckCodePrefix::B => PrefixSpecificity::Category,
|
CheckCodePrefix::B => PrefixSpecificity::Category,
|
||||||
CheckCodePrefix::B0 => PrefixSpecificity::Hundreds,
|
CheckCodePrefix::B0 => PrefixSpecificity::Hundreds,
|
||||||
CheckCodePrefix::B00 => PrefixSpecificity::Tens,
|
CheckCodePrefix::B00 => PrefixSpecificity::Tens,
|
||||||
|
|
131
src/flake8_annotations/mod.rs
Normal file
131
src/flake8_annotations/mod.rs
Normal file
|
@ -0,0 +1,131 @@
|
||||||
|
pub mod plugins;
|
||||||
|
pub mod settings;
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
use std::path::Path;
|
||||||
|
|
||||||
|
use anyhow::Result;
|
||||||
|
use rustpython_parser::lexer::LexResult;
|
||||||
|
|
||||||
|
use crate::autofix::fixer;
|
||||||
|
use crate::checks::{Check, CheckCode};
|
||||||
|
use crate::linter::tokenize;
|
||||||
|
use crate::{flake8_annotations, fs, linter, noqa, Settings, SourceCodeLocator};
|
||||||
|
|
||||||
|
fn check_path(path: &Path, settings: &Settings, autofix: &fixer::Mode) -> Result<Vec<Check>> {
|
||||||
|
let contents = fs::read_file(path)?;
|
||||||
|
let tokens: Vec<LexResult> = tokenize(&contents);
|
||||||
|
let locator = SourceCodeLocator::new(&contents);
|
||||||
|
let noqa_line_for = noqa::extract_noqa_line_for(&tokens);
|
||||||
|
linter::check_path(
|
||||||
|
path,
|
||||||
|
&contents,
|
||||||
|
tokens,
|
||||||
|
&locator,
|
||||||
|
&noqa_line_for,
|
||||||
|
settings,
|
||||||
|
autofix,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn defaults() -> Result<()> {
|
||||||
|
let mut checks = check_path(
|
||||||
|
Path::new("./resources/test/fixtures/flake8_annotations/annotation_presence.py"),
|
||||||
|
&Settings {
|
||||||
|
..Settings::for_rules(vec![
|
||||||
|
CheckCode::ANN001,
|
||||||
|
CheckCode::ANN002,
|
||||||
|
CheckCode::ANN003,
|
||||||
|
CheckCode::ANN101,
|
||||||
|
CheckCode::ANN102,
|
||||||
|
CheckCode::ANN201,
|
||||||
|
CheckCode::ANN202,
|
||||||
|
CheckCode::ANN204,
|
||||||
|
CheckCode::ANN205,
|
||||||
|
CheckCode::ANN206,
|
||||||
|
])
|
||||||
|
},
|
||||||
|
&fixer::Mode::Generate,
|
||||||
|
)?;
|
||||||
|
checks.sort_by_key(|check| check.location);
|
||||||
|
insta::assert_yaml_snapshot!(checks);
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn suppress_dummy_args() -> Result<()> {
|
||||||
|
let mut checks = check_path(
|
||||||
|
Path::new("./resources/test/fixtures/flake8_annotations/suppress_dummy_args.py"),
|
||||||
|
&Settings {
|
||||||
|
flake8_annotations: flake8_annotations::settings::Settings {
|
||||||
|
mypy_init_return: false,
|
||||||
|
suppress_dummy_args: true,
|
||||||
|
suppress_none_returning: false,
|
||||||
|
},
|
||||||
|
..Settings::for_rules(vec![
|
||||||
|
CheckCode::ANN001,
|
||||||
|
CheckCode::ANN002,
|
||||||
|
CheckCode::ANN003,
|
||||||
|
CheckCode::ANN101,
|
||||||
|
CheckCode::ANN102,
|
||||||
|
])
|
||||||
|
},
|
||||||
|
&fixer::Mode::Generate,
|
||||||
|
)?;
|
||||||
|
checks.sort_by_key(|check| check.location);
|
||||||
|
insta::assert_yaml_snapshot!(checks);
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn mypy_init_return() -> Result<()> {
|
||||||
|
let mut checks = check_path(
|
||||||
|
Path::new("./resources/test/fixtures/flake8_annotations/mypy_init_return.py"),
|
||||||
|
&Settings {
|
||||||
|
flake8_annotations: flake8_annotations::settings::Settings {
|
||||||
|
mypy_init_return: true,
|
||||||
|
suppress_dummy_args: false,
|
||||||
|
suppress_none_returning: false,
|
||||||
|
},
|
||||||
|
..Settings::for_rules(vec![
|
||||||
|
CheckCode::ANN201,
|
||||||
|
CheckCode::ANN202,
|
||||||
|
CheckCode::ANN204,
|
||||||
|
CheckCode::ANN205,
|
||||||
|
CheckCode::ANN206,
|
||||||
|
])
|
||||||
|
},
|
||||||
|
&fixer::Mode::Generate,
|
||||||
|
)?;
|
||||||
|
checks.sort_by_key(|check| check.location);
|
||||||
|
insta::assert_yaml_snapshot!(checks);
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn suppress_none_returning() -> Result<()> {
|
||||||
|
let mut checks = check_path(
|
||||||
|
Path::new("./resources/test/fixtures/flake8_annotations/suppress_none_returning.py"),
|
||||||
|
&Settings {
|
||||||
|
flake8_annotations: flake8_annotations::settings::Settings {
|
||||||
|
mypy_init_return: false,
|
||||||
|
suppress_dummy_args: false,
|
||||||
|
suppress_none_returning: true,
|
||||||
|
},
|
||||||
|
..Settings::for_rules(vec![
|
||||||
|
CheckCode::ANN201,
|
||||||
|
CheckCode::ANN202,
|
||||||
|
CheckCode::ANN204,
|
||||||
|
CheckCode::ANN205,
|
||||||
|
CheckCode::ANN206,
|
||||||
|
])
|
||||||
|
},
|
||||||
|
&fixer::Mode::Generate,
|
||||||
|
)?;
|
||||||
|
checks.sort_by_key(|check| check.location);
|
||||||
|
insta::assert_yaml_snapshot!(checks);
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
326
src/flake8_annotations/plugins.rs
Normal file
326
src/flake8_annotations/plugins.rs
Normal file
|
@ -0,0 +1,326 @@
|
||||||
|
use rustpython_ast::{Arguments, Constant, Expr, ExprKind, Stmt, StmtKind};
|
||||||
|
|
||||||
|
use crate::ast::types::Range;
|
||||||
|
use crate::ast::visitor;
|
||||||
|
use crate::ast::visitor::Visitor;
|
||||||
|
use crate::check_ast::Checker;
|
||||||
|
use crate::checks::{CheckCode, CheckKind};
|
||||||
|
use crate::docstrings::definition::{Definition, DefinitionKind};
|
||||||
|
use crate::visibility::Visibility;
|
||||||
|
use crate::{visibility, Check};
|
||||||
|
|
||||||
|
#[derive(Default)]
|
||||||
|
struct ReturnStatementVisitor<'a> {
|
||||||
|
returns: Vec<&'a Option<Box<Expr>>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a, 'b> Visitor<'b> for ReturnStatementVisitor<'a>
|
||||||
|
where
|
||||||
|
'b: 'a,
|
||||||
|
{
|
||||||
|
fn visit_stmt(&mut self, stmt: &'b Stmt) {
|
||||||
|
match &stmt.node {
|
||||||
|
StmtKind::FunctionDef { .. } | StmtKind::AsyncFunctionDef { .. } => {
|
||||||
|
// No recurse.
|
||||||
|
}
|
||||||
|
StmtKind::Return { value } => self.returns.push(value),
|
||||||
|
_ => visitor::walk_stmt(self, stmt),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn is_none_returning(stmt: &Stmt) -> bool {
|
||||||
|
let mut visitor: ReturnStatementVisitor = Default::default();
|
||||||
|
for stmt in match_body(stmt) {
|
||||||
|
visitor.visit_stmt(stmt);
|
||||||
|
}
|
||||||
|
for expr in visitor.returns.into_iter().flatten() {
|
||||||
|
if !matches!(
|
||||||
|
expr.node,
|
||||||
|
ExprKind::Constant {
|
||||||
|
value: Constant::None,
|
||||||
|
..
|
||||||
|
}
|
||||||
|
) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
true
|
||||||
|
}
|
||||||
|
|
||||||
|
fn match_args(stmt: &Stmt) -> &Arguments {
|
||||||
|
match &stmt.node {
|
||||||
|
StmtKind::FunctionDef { args, .. } | StmtKind::AsyncFunctionDef { args, .. } => args,
|
||||||
|
_ => panic!("Found non-FunctionDef in match_args"),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn match_body(stmt: &Stmt) -> &Vec<Stmt> {
|
||||||
|
match &stmt.node {
|
||||||
|
StmtKind::FunctionDef { body, .. } | StmtKind::AsyncFunctionDef { body, .. } => body,
|
||||||
|
_ => panic!("Found non-FunctionDef in match_body"),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn match_returns(stmt: &Stmt) -> &Option<Box<Expr>> {
|
||||||
|
match &stmt.node {
|
||||||
|
StmtKind::FunctionDef { returns, .. } | StmtKind::AsyncFunctionDef { returns, .. } => {
|
||||||
|
returns
|
||||||
|
}
|
||||||
|
_ => panic!("Found non-FunctionDef in match_returns"),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Generate flake8-annotation checks for a given `Definition`.
|
||||||
|
pub fn definition(checker: &mut Checker, definition: &Definition, visibility: &Visibility) {
|
||||||
|
match &definition.kind {
|
||||||
|
DefinitionKind::Module => {}
|
||||||
|
DefinitionKind::Package => {}
|
||||||
|
DefinitionKind::Class(_) => {}
|
||||||
|
DefinitionKind::NestedClass(_) => {}
|
||||||
|
DefinitionKind::Function(stmt) | DefinitionKind::NestedFunction(stmt) => {
|
||||||
|
let args = match_args(stmt);
|
||||||
|
let returns = match_returns(stmt);
|
||||||
|
|
||||||
|
// ANN001
|
||||||
|
for arg in args
|
||||||
|
.args
|
||||||
|
.iter()
|
||||||
|
.chain(args.posonlyargs.iter())
|
||||||
|
.chain(args.kwonlyargs.iter())
|
||||||
|
{
|
||||||
|
if arg.node.annotation.is_none() {
|
||||||
|
if !(checker.settings.flake8_annotations.suppress_dummy_args
|
||||||
|
&& checker.settings.dummy_variable_rgx.is_match(&arg.node.arg))
|
||||||
|
{
|
||||||
|
if checker.settings.enabled.contains(&CheckCode::ANN001) {
|
||||||
|
checker.add_check(Check::new(
|
||||||
|
CheckKind::MissingTypeFunctionArgument,
|
||||||
|
Range::from_located(arg),
|
||||||
|
));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// ANN002
|
||||||
|
if let Some(arg) = &args.vararg {
|
||||||
|
if arg.node.annotation.is_none() {
|
||||||
|
if !(checker.settings.flake8_annotations.suppress_dummy_args
|
||||||
|
&& checker.settings.dummy_variable_rgx.is_match(&arg.node.arg))
|
||||||
|
{
|
||||||
|
if checker.settings.enabled.contains(&CheckCode::ANN002) {
|
||||||
|
checker.add_check(Check::new(
|
||||||
|
CheckKind::MissingTypeArgs,
|
||||||
|
Range::from_located(arg),
|
||||||
|
));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// ANN003
|
||||||
|
if let Some(arg) = &args.kwarg {
|
||||||
|
if arg.node.annotation.is_none() {
|
||||||
|
if !(checker.settings.flake8_annotations.suppress_dummy_args
|
||||||
|
&& checker.settings.dummy_variable_rgx.is_match(&arg.node.arg))
|
||||||
|
{
|
||||||
|
if checker.settings.enabled.contains(&CheckCode::ANN003) {
|
||||||
|
checker.add_check(Check::new(
|
||||||
|
CheckKind::MissingTypeKwargs,
|
||||||
|
Range::from_located(arg),
|
||||||
|
));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// ANN201, ANN202
|
||||||
|
if returns.is_none() {
|
||||||
|
// Allow omission of return annotation in `__init__` functions, if the function
|
||||||
|
// only returns `None` (explicitly or implicitly).
|
||||||
|
if checker.settings.flake8_annotations.suppress_none_returning
|
||||||
|
&& is_none_returning(stmt)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
match visibility {
|
||||||
|
Visibility::Public => {
|
||||||
|
if checker.settings.enabled.contains(&CheckCode::ANN201) {
|
||||||
|
checker.add_check(Check::new(
|
||||||
|
CheckKind::MissingReturnTypePublicFunction,
|
||||||
|
Range::from_located(stmt),
|
||||||
|
));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Visibility::Private => {
|
||||||
|
if checker.settings.enabled.contains(&CheckCode::ANN202) {
|
||||||
|
checker.add_check(Check::new(
|
||||||
|
CheckKind::MissingReturnTypePrivateFunction,
|
||||||
|
Range::from_located(stmt),
|
||||||
|
));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
DefinitionKind::Method(stmt) => {
|
||||||
|
let args = match_args(stmt);
|
||||||
|
let returns = match_returns(stmt);
|
||||||
|
let mut has_any_typed_arg = false;
|
||||||
|
|
||||||
|
// ANN001
|
||||||
|
for arg in args
|
||||||
|
.args
|
||||||
|
.iter()
|
||||||
|
.chain(args.posonlyargs.iter())
|
||||||
|
.chain(args.kwonlyargs.iter())
|
||||||
|
.skip(
|
||||||
|
// If this is a non-static method, skip `cls` or `self`.
|
||||||
|
usize::from(!visibility::is_staticmethod(stmt)),
|
||||||
|
)
|
||||||
|
{
|
||||||
|
if arg.node.annotation.is_none() {
|
||||||
|
if !(checker.settings.flake8_annotations.suppress_dummy_args
|
||||||
|
&& checker.settings.dummy_variable_rgx.is_match(&arg.node.arg))
|
||||||
|
{
|
||||||
|
if checker.settings.enabled.contains(&CheckCode::ANN001) {
|
||||||
|
checker.add_check(Check::new(
|
||||||
|
CheckKind::MissingTypeFunctionArgument,
|
||||||
|
Range::from_located(arg),
|
||||||
|
));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
has_any_typed_arg = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// ANN002
|
||||||
|
if let Some(arg) = &args.vararg {
|
||||||
|
if arg.node.annotation.is_none() {
|
||||||
|
if !(checker.settings.flake8_annotations.suppress_dummy_args
|
||||||
|
&& checker.settings.dummy_variable_rgx.is_match(&arg.node.arg))
|
||||||
|
{
|
||||||
|
if checker.settings.enabled.contains(&CheckCode::ANN002) {
|
||||||
|
checker.add_check(Check::new(
|
||||||
|
CheckKind::MissingTypeArgs,
|
||||||
|
Range::from_located(arg),
|
||||||
|
));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
has_any_typed_arg = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// ANN003
|
||||||
|
if let Some(arg) = &args.kwarg {
|
||||||
|
if arg.node.annotation.is_none() {
|
||||||
|
if !(checker.settings.flake8_annotations.suppress_dummy_args
|
||||||
|
&& checker.settings.dummy_variable_rgx.is_match(&arg.node.arg))
|
||||||
|
{
|
||||||
|
if checker.settings.enabled.contains(&CheckCode::ANN003) {
|
||||||
|
checker.add_check(Check::new(
|
||||||
|
CheckKind::MissingTypeKwargs,
|
||||||
|
Range::from_located(arg),
|
||||||
|
));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
has_any_typed_arg = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// ANN101, ANN102
|
||||||
|
if !visibility::is_staticmethod(stmt) {
|
||||||
|
if let Some(arg) = args.args.first() {
|
||||||
|
if arg.node.annotation.is_none() {
|
||||||
|
if visibility::is_classmethod(stmt) {
|
||||||
|
if checker.settings.enabled.contains(&CheckCode::ANN101) {
|
||||||
|
checker.add_check(Check::new(
|
||||||
|
CheckKind::MissingTypeCls,
|
||||||
|
Range::from_located(arg),
|
||||||
|
));
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if checker.settings.enabled.contains(&CheckCode::ANN102) {
|
||||||
|
checker.add_check(Check::new(
|
||||||
|
CheckKind::MissingTypeSelf,
|
||||||
|
Range::from_located(arg),
|
||||||
|
));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// ANN201, ANN202
|
||||||
|
if returns.is_none() {
|
||||||
|
// Allow omission of return annotation in `__init__` functions, if the function
|
||||||
|
// only returns `None` (explicitly or implicitly).
|
||||||
|
if checker.settings.flake8_annotations.suppress_none_returning
|
||||||
|
&& is_none_returning(stmt)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if visibility::is_classmethod(stmt) {
|
||||||
|
if checker.settings.enabled.contains(&CheckCode::ANN206) {
|
||||||
|
checker.add_check(Check::new(
|
||||||
|
CheckKind::MissingReturnTypeClassMethod,
|
||||||
|
Range::from_located(stmt),
|
||||||
|
));
|
||||||
|
}
|
||||||
|
} else if visibility::is_staticmethod(stmt) {
|
||||||
|
if checker.settings.enabled.contains(&CheckCode::ANN205) {
|
||||||
|
checker.add_check(Check::new(
|
||||||
|
CheckKind::MissingReturnTypeStaticMethod,
|
||||||
|
Range::from_located(stmt),
|
||||||
|
));
|
||||||
|
}
|
||||||
|
} else if visibility::is_magic(stmt) {
|
||||||
|
if checker.settings.enabled.contains(&CheckCode::ANN204) {
|
||||||
|
checker.add_check(Check::new(
|
||||||
|
CheckKind::MissingReturnTypeMagicMethod,
|
||||||
|
Range::from_located(stmt),
|
||||||
|
));
|
||||||
|
}
|
||||||
|
} else if visibility::is_init(stmt) {
|
||||||
|
// Allow omission of return annotation in `__init__` functions, as long as at
|
||||||
|
// least one argument is typed.
|
||||||
|
if checker.settings.enabled.contains(&CheckCode::ANN204) {
|
||||||
|
if !(checker.settings.flake8_annotations.mypy_init_return
|
||||||
|
&& has_any_typed_arg)
|
||||||
|
{
|
||||||
|
checker.add_check(Check::new(
|
||||||
|
CheckKind::MissingReturnTypeMagicMethod,
|
||||||
|
Range::from_located(stmt),
|
||||||
|
));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
match visibility {
|
||||||
|
Visibility::Public => {
|
||||||
|
if checker.settings.enabled.contains(&CheckCode::ANN201) {
|
||||||
|
checker.add_check(Check::new(
|
||||||
|
CheckKind::MissingReturnTypePublicFunction,
|
||||||
|
Range::from_located(stmt),
|
||||||
|
));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Visibility::Private => {
|
||||||
|
if checker.settings.enabled.contains(&CheckCode::ANN202) {
|
||||||
|
checker.add_check(Check::new(
|
||||||
|
CheckKind::MissingReturnTypePrivateFunction,
|
||||||
|
Range::from_located(stmt),
|
||||||
|
));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
36
src/flake8_annotations/settings.rs
Normal file
36
src/flake8_annotations/settings.rs
Normal file
|
@ -0,0 +1,36 @@
|
||||||
|
//! Settings for the `flake-annotations` plugin.
|
||||||
|
|
||||||
|
use serde::{Deserialize, Serialize};
|
||||||
|
|
||||||
|
#[derive(Debug, PartialEq, Eq, Serialize, Deserialize, Default)]
|
||||||
|
#[serde(deny_unknown_fields, rename_all = "kebab-case")]
|
||||||
|
pub struct Options {
|
||||||
|
/// Allow omission of a return type hint for `__init__` if at least one
|
||||||
|
/// argument is annotated.
|
||||||
|
pub mypy_init_return: Option<bool>,
|
||||||
|
/// Suppress ANN000-level errors for dummy arguments, like `_`.
|
||||||
|
pub suppress_dummy_args: Option<bool>,
|
||||||
|
/// Suppress ANN200-level errors for functions that meet one of the
|
||||||
|
/// following criteria:
|
||||||
|
/// - Contain no `return` statement
|
||||||
|
/// - Explicit `return` statement(s) all return `None` (explicitly or
|
||||||
|
/// implicitly).
|
||||||
|
pub suppress_none_returning: Option<bool>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Hash, Default)]
|
||||||
|
pub struct Settings {
|
||||||
|
pub mypy_init_return: bool,
|
||||||
|
pub suppress_dummy_args: bool,
|
||||||
|
pub suppress_none_returning: bool,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Settings {
|
||||||
|
pub fn from_options(options: Options) -> Self {
|
||||||
|
Self {
|
||||||
|
mypy_init_return: options.mypy_init_return.unwrap_or_default(),
|
||||||
|
suppress_dummy_args: options.suppress_dummy_args.unwrap_or_default(),
|
||||||
|
suppress_none_returning: options.suppress_none_returning.unwrap_or_default(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,69 @@
|
||||||
|
---
|
||||||
|
source: src/flake8_annotations/mod.rs
|
||||||
|
expression: checks
|
||||||
|
---
|
||||||
|
- kind: MissingReturnTypePublicFunction
|
||||||
|
location:
|
||||||
|
row: 2
|
||||||
|
column: 0
|
||||||
|
end_location:
|
||||||
|
row: 7
|
||||||
|
column: 0
|
||||||
|
fix: ~
|
||||||
|
- kind: MissingTypeFunctionArgument
|
||||||
|
location:
|
||||||
|
row: 2
|
||||||
|
column: 8
|
||||||
|
end_location:
|
||||||
|
row: 2
|
||||||
|
column: 9
|
||||||
|
fix: ~
|
||||||
|
- kind: MissingTypeFunctionArgument
|
||||||
|
location:
|
||||||
|
row: 2
|
||||||
|
column: 11
|
||||||
|
end_location:
|
||||||
|
row: 2
|
||||||
|
column: 12
|
||||||
|
fix: ~
|
||||||
|
- kind: MissingReturnTypePublicFunction
|
||||||
|
location:
|
||||||
|
row: 7
|
||||||
|
column: 0
|
||||||
|
end_location:
|
||||||
|
row: 12
|
||||||
|
column: 0
|
||||||
|
fix: ~
|
||||||
|
- kind: MissingTypeFunctionArgument
|
||||||
|
location:
|
||||||
|
row: 7
|
||||||
|
column: 16
|
||||||
|
end_location:
|
||||||
|
row: 7
|
||||||
|
column: 17
|
||||||
|
fix: ~
|
||||||
|
- kind: MissingTypeFunctionArgument
|
||||||
|
location:
|
||||||
|
row: 12
|
||||||
|
column: 16
|
||||||
|
end_location:
|
||||||
|
row: 12
|
||||||
|
column: 17
|
||||||
|
fix: ~
|
||||||
|
- kind: MissingReturnTypePublicFunction
|
||||||
|
location:
|
||||||
|
row: 17
|
||||||
|
column: 0
|
||||||
|
end_location:
|
||||||
|
row: 22
|
||||||
|
column: 0
|
||||||
|
fix: ~
|
||||||
|
- kind: MissingReturnTypePublicFunction
|
||||||
|
location:
|
||||||
|
row: 22
|
||||||
|
column: 0
|
||||||
|
end_location:
|
||||||
|
row: 27
|
||||||
|
column: 0
|
||||||
|
fix: ~
|
||||||
|
|
|
@ -0,0 +1,29 @@
|
||||||
|
---
|
||||||
|
source: src/flake8_annotations/mod.rs
|
||||||
|
expression: checks
|
||||||
|
---
|
||||||
|
- kind: MissingReturnTypeMagicMethod
|
||||||
|
location:
|
||||||
|
row: 5
|
||||||
|
column: 4
|
||||||
|
end_location:
|
||||||
|
row: 10
|
||||||
|
column: 0
|
||||||
|
fix: ~
|
||||||
|
- kind: MissingReturnTypeMagicMethod
|
||||||
|
location:
|
||||||
|
row: 11
|
||||||
|
column: 4
|
||||||
|
end_location:
|
||||||
|
row: 16
|
||||||
|
column: 0
|
||||||
|
fix: ~
|
||||||
|
- kind: MissingReturnTypePrivateFunction
|
||||||
|
location:
|
||||||
|
row: 40
|
||||||
|
column: 0
|
||||||
|
end_location:
|
||||||
|
row: 42
|
||||||
|
column: 0
|
||||||
|
fix: ~
|
||||||
|
|
|
@ -0,0 +1,6 @@
|
||||||
|
---
|
||||||
|
source: src/flake8_annotations/mod.rs
|
||||||
|
expression: checks
|
||||||
|
---
|
||||||
|
[]
|
||||||
|
|
|
@ -0,0 +1,21 @@
|
||||||
|
---
|
||||||
|
source: src/flake8_annotations/mod.rs
|
||||||
|
expression: checks
|
||||||
|
---
|
||||||
|
- kind: MissingReturnTypePublicFunction
|
||||||
|
location:
|
||||||
|
row: 45
|
||||||
|
column: 0
|
||||||
|
end_location:
|
||||||
|
row: 50
|
||||||
|
column: 0
|
||||||
|
fix: ~
|
||||||
|
- kind: MissingReturnTypePublicFunction
|
||||||
|
location:
|
||||||
|
row: 50
|
||||||
|
column: 0
|
||||||
|
end_location:
|
||||||
|
row: 56
|
||||||
|
column: 0
|
||||||
|
fix: ~
|
||||||
|
|
|
@ -135,172 +135,3 @@ pub fn quotes(
|
||||||
|
|
||||||
None
|
None
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
|
||||||
mod tests {
|
|
||||||
use std::path::Path;
|
|
||||||
|
|
||||||
use anyhow::Result;
|
|
||||||
use rustpython_parser::lexer::LexResult;
|
|
||||||
use test_case::test_case;
|
|
||||||
|
|
||||||
use crate::autofix::fixer;
|
|
||||||
use crate::checks::{Check, CheckCode};
|
|
||||||
use crate::flake8_quotes::settings::Quote;
|
|
||||||
use crate::linter::tokenize;
|
|
||||||
use crate::{flake8_quotes, fs, linter, noqa, Settings, SourceCodeLocator};
|
|
||||||
|
|
||||||
fn check_path(path: &Path, settings: &Settings, autofix: &fixer::Mode) -> Result<Vec<Check>> {
|
|
||||||
let contents = fs::read_file(path)?;
|
|
||||||
let tokens: Vec<LexResult> = tokenize(&contents);
|
|
||||||
let locator = SourceCodeLocator::new(&contents);
|
|
||||||
let noqa_line_for = noqa::extract_noqa_line_for(&tokens);
|
|
||||||
linter::check_path(
|
|
||||||
path,
|
|
||||||
&contents,
|
|
||||||
tokens,
|
|
||||||
&locator,
|
|
||||||
&noqa_line_for,
|
|
||||||
settings,
|
|
||||||
autofix,
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test_case(Path::new("doubles.py"))]
|
|
||||||
#[test_case(Path::new("doubles_escaped.py"))]
|
|
||||||
#[test_case(Path::new("doubles_multiline_string.py"))]
|
|
||||||
#[test_case(Path::new("doubles_noqa.py"))]
|
|
||||||
#[test_case(Path::new("doubles_wrapped.py"))]
|
|
||||||
fn doubles(path: &Path) -> Result<()> {
|
|
||||||
let snapshot = format!("doubles_{}", path.to_string_lossy());
|
|
||||||
let mut checks = check_path(
|
|
||||||
Path::new("./resources/test/fixtures/flake8_quotes")
|
|
||||||
.join(path)
|
|
||||||
.as_path(),
|
|
||||||
&Settings {
|
|
||||||
flake8_quotes: flake8_quotes::settings::Settings {
|
|
||||||
inline_quotes: Quote::Single,
|
|
||||||
multiline_quotes: Quote::Single,
|
|
||||||
docstring_quotes: Quote::Single,
|
|
||||||
avoid_escape: true,
|
|
||||||
},
|
|
||||||
..Settings::for_rules(vec![
|
|
||||||
CheckCode::Q000,
|
|
||||||
CheckCode::Q001,
|
|
||||||
CheckCode::Q002,
|
|
||||||
CheckCode::Q003,
|
|
||||||
])
|
|
||||||
},
|
|
||||||
&fixer::Mode::Generate,
|
|
||||||
)?;
|
|
||||||
checks.sort_by_key(|check| check.location);
|
|
||||||
insta::assert_yaml_snapshot!(snapshot, checks);
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test_case(Path::new("singles.py"))]
|
|
||||||
#[test_case(Path::new("singles_escaped.py"))]
|
|
||||||
#[test_case(Path::new("singles_multiline_string.py"))]
|
|
||||||
#[test_case(Path::new("singles_noqa.py"))]
|
|
||||||
#[test_case(Path::new("singles_wrapped.py"))]
|
|
||||||
fn singles(path: &Path) -> Result<()> {
|
|
||||||
let snapshot = format!("singles_{}", path.to_string_lossy());
|
|
||||||
let mut checks = check_path(
|
|
||||||
Path::new("./resources/test/fixtures/flake8_quotes")
|
|
||||||
.join(path)
|
|
||||||
.as_path(),
|
|
||||||
&Settings {
|
|
||||||
flake8_quotes: flake8_quotes::settings::Settings {
|
|
||||||
inline_quotes: Quote::Double,
|
|
||||||
multiline_quotes: Quote::Double,
|
|
||||||
docstring_quotes: Quote::Double,
|
|
||||||
avoid_escape: true,
|
|
||||||
},
|
|
||||||
..Settings::for_rules(vec![
|
|
||||||
CheckCode::Q000,
|
|
||||||
CheckCode::Q001,
|
|
||||||
CheckCode::Q002,
|
|
||||||
CheckCode::Q003,
|
|
||||||
])
|
|
||||||
},
|
|
||||||
&fixer::Mode::Generate,
|
|
||||||
)?;
|
|
||||||
checks.sort_by_key(|check| check.location);
|
|
||||||
insta::assert_yaml_snapshot!(snapshot, checks);
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test_case(Path::new("docstring_doubles.py"))]
|
|
||||||
#[test_case(Path::new("docstring_doubles_module_multiline.py"))]
|
|
||||||
#[test_case(Path::new("docstring_doubles_module_singleline.py"))]
|
|
||||||
#[test_case(Path::new("docstring_doubles_class.py"))]
|
|
||||||
#[test_case(Path::new("docstring_doubles_function.py"))]
|
|
||||||
#[test_case(Path::new("docstring_singles.py"))]
|
|
||||||
#[test_case(Path::new("docstring_singles_module_multiline.py"))]
|
|
||||||
#[test_case(Path::new("docstring_singles_module_singleline.py"))]
|
|
||||||
#[test_case(Path::new("docstring_singles_class.py"))]
|
|
||||||
#[test_case(Path::new("docstring_singles_function.py"))]
|
|
||||||
fn double_docstring(path: &Path) -> Result<()> {
|
|
||||||
let snapshot = format!("double_docstring_{}", path.to_string_lossy());
|
|
||||||
let mut checks = check_path(
|
|
||||||
Path::new("./resources/test/fixtures/flake8_quotes")
|
|
||||||
.join(path)
|
|
||||||
.as_path(),
|
|
||||||
&Settings {
|
|
||||||
flake8_quotes: flake8_quotes::settings::Settings {
|
|
||||||
inline_quotes: Quote::Single,
|
|
||||||
multiline_quotes: Quote::Single,
|
|
||||||
docstring_quotes: Quote::Double,
|
|
||||||
avoid_escape: true,
|
|
||||||
},
|
|
||||||
..Settings::for_rules(vec![
|
|
||||||
CheckCode::Q000,
|
|
||||||
CheckCode::Q001,
|
|
||||||
CheckCode::Q002,
|
|
||||||
CheckCode::Q003,
|
|
||||||
])
|
|
||||||
},
|
|
||||||
&fixer::Mode::Generate,
|
|
||||||
)?;
|
|
||||||
checks.sort_by_key(|check| check.location);
|
|
||||||
insta::assert_yaml_snapshot!(snapshot, checks);
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test_case(Path::new("docstring_doubles.py"))]
|
|
||||||
#[test_case(Path::new("docstring_doubles_module_multiline.py"))]
|
|
||||||
#[test_case(Path::new("docstring_doubles_module_singleline.py"))]
|
|
||||||
#[test_case(Path::new("docstring_doubles_class.py"))]
|
|
||||||
#[test_case(Path::new("docstring_doubles_function.py"))]
|
|
||||||
#[test_case(Path::new("docstring_singles.py"))]
|
|
||||||
#[test_case(Path::new("docstring_singles_module_multiline.py"))]
|
|
||||||
#[test_case(Path::new("docstring_singles_module_singleline.py"))]
|
|
||||||
#[test_case(Path::new("docstring_singles_class.py"))]
|
|
||||||
#[test_case(Path::new("docstring_singles_function.py"))]
|
|
||||||
fn single_docstring(path: &Path) -> Result<()> {
|
|
||||||
let snapshot = format!("single_docstring_{}", path.to_string_lossy());
|
|
||||||
let mut checks = check_path(
|
|
||||||
Path::new("./resources/test/fixtures/flake8_quotes")
|
|
||||||
.join(path)
|
|
||||||
.as_path(),
|
|
||||||
&Settings {
|
|
||||||
flake8_quotes: flake8_quotes::settings::Settings {
|
|
||||||
inline_quotes: Quote::Single,
|
|
||||||
multiline_quotes: Quote::Double,
|
|
||||||
docstring_quotes: Quote::Single,
|
|
||||||
avoid_escape: true,
|
|
||||||
},
|
|
||||||
..Settings::for_rules(vec![
|
|
||||||
CheckCode::Q000,
|
|
||||||
CheckCode::Q001,
|
|
||||||
CheckCode::Q002,
|
|
||||||
CheckCode::Q003,
|
|
||||||
])
|
|
||||||
},
|
|
||||||
&fixer::Mode::Generate,
|
|
||||||
)?;
|
|
||||||
checks.sort_by_key(|check| check.location);
|
|
||||||
insta::assert_yaml_snapshot!(snapshot, checks);
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
|
@ -1,2 +1,171 @@
|
||||||
pub mod checks;
|
pub mod checks;
|
||||||
pub mod settings;
|
pub mod settings;
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
use std::path::Path;
|
||||||
|
|
||||||
|
use anyhow::Result;
|
||||||
|
use rustpython_parser::lexer::LexResult;
|
||||||
|
use test_case::test_case;
|
||||||
|
|
||||||
|
use crate::autofix::fixer;
|
||||||
|
use crate::checks::{Check, CheckCode};
|
||||||
|
use crate::flake8_quotes::settings::Quote;
|
||||||
|
use crate::linter::tokenize;
|
||||||
|
use crate::{flake8_quotes, fs, linter, noqa, Settings, SourceCodeLocator};
|
||||||
|
|
||||||
|
fn check_path(path: &Path, settings: &Settings, autofix: &fixer::Mode) -> Result<Vec<Check>> {
|
||||||
|
let contents = fs::read_file(path)?;
|
||||||
|
let tokens: Vec<LexResult> = tokenize(&contents);
|
||||||
|
let locator = SourceCodeLocator::new(&contents);
|
||||||
|
let noqa_line_for = noqa::extract_noqa_line_for(&tokens);
|
||||||
|
linter::check_path(
|
||||||
|
path,
|
||||||
|
&contents,
|
||||||
|
tokens,
|
||||||
|
&locator,
|
||||||
|
&noqa_line_for,
|
||||||
|
settings,
|
||||||
|
autofix,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test_case(Path::new("doubles.py"))]
|
||||||
|
#[test_case(Path::new("doubles_escaped.py"))]
|
||||||
|
#[test_case(Path::new("doubles_multiline_string.py"))]
|
||||||
|
#[test_case(Path::new("doubles_noqa.py"))]
|
||||||
|
#[test_case(Path::new("doubles_wrapped.py"))]
|
||||||
|
fn doubles(path: &Path) -> Result<()> {
|
||||||
|
let snapshot = format!("doubles_{}", path.to_string_lossy());
|
||||||
|
let mut checks = check_path(
|
||||||
|
Path::new("./resources/test/fixtures/flake8_quotes")
|
||||||
|
.join(path)
|
||||||
|
.as_path(),
|
||||||
|
&Settings {
|
||||||
|
flake8_quotes: flake8_quotes::settings::Settings {
|
||||||
|
inline_quotes: Quote::Single,
|
||||||
|
multiline_quotes: Quote::Single,
|
||||||
|
docstring_quotes: Quote::Single,
|
||||||
|
avoid_escape: true,
|
||||||
|
},
|
||||||
|
..Settings::for_rules(vec![
|
||||||
|
CheckCode::Q000,
|
||||||
|
CheckCode::Q001,
|
||||||
|
CheckCode::Q002,
|
||||||
|
CheckCode::Q003,
|
||||||
|
])
|
||||||
|
},
|
||||||
|
&fixer::Mode::Generate,
|
||||||
|
)?;
|
||||||
|
checks.sort_by_key(|check| check.location);
|
||||||
|
insta::assert_yaml_snapshot!(snapshot, checks);
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test_case(Path::new("singles.py"))]
|
||||||
|
#[test_case(Path::new("singles_escaped.py"))]
|
||||||
|
#[test_case(Path::new("singles_multiline_string.py"))]
|
||||||
|
#[test_case(Path::new("singles_noqa.py"))]
|
||||||
|
#[test_case(Path::new("singles_wrapped.py"))]
|
||||||
|
fn singles(path: &Path) -> Result<()> {
|
||||||
|
let snapshot = format!("singles_{}", path.to_string_lossy());
|
||||||
|
let mut checks = check_path(
|
||||||
|
Path::new("./resources/test/fixtures/flake8_quotes")
|
||||||
|
.join(path)
|
||||||
|
.as_path(),
|
||||||
|
&Settings {
|
||||||
|
flake8_quotes: flake8_quotes::settings::Settings {
|
||||||
|
inline_quotes: Quote::Double,
|
||||||
|
multiline_quotes: Quote::Double,
|
||||||
|
docstring_quotes: Quote::Double,
|
||||||
|
avoid_escape: true,
|
||||||
|
},
|
||||||
|
..Settings::for_rules(vec![
|
||||||
|
CheckCode::Q000,
|
||||||
|
CheckCode::Q001,
|
||||||
|
CheckCode::Q002,
|
||||||
|
CheckCode::Q003,
|
||||||
|
])
|
||||||
|
},
|
||||||
|
&fixer::Mode::Generate,
|
||||||
|
)?;
|
||||||
|
checks.sort_by_key(|check| check.location);
|
||||||
|
insta::assert_yaml_snapshot!(snapshot, checks);
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test_case(Path::new("docstring_doubles.py"))]
|
||||||
|
#[test_case(Path::new("docstring_doubles_module_multiline.py"))]
|
||||||
|
#[test_case(Path::new("docstring_doubles_module_singleline.py"))]
|
||||||
|
#[test_case(Path::new("docstring_doubles_class.py"))]
|
||||||
|
#[test_case(Path::new("docstring_doubles_function.py"))]
|
||||||
|
#[test_case(Path::new("docstring_singles.py"))]
|
||||||
|
#[test_case(Path::new("docstring_singles_module_multiline.py"))]
|
||||||
|
#[test_case(Path::new("docstring_singles_module_singleline.py"))]
|
||||||
|
#[test_case(Path::new("docstring_singles_class.py"))]
|
||||||
|
#[test_case(Path::new("docstring_singles_function.py"))]
|
||||||
|
fn double_docstring(path: &Path) -> Result<()> {
|
||||||
|
let snapshot = format!("double_docstring_{}", path.to_string_lossy());
|
||||||
|
let mut checks = check_path(
|
||||||
|
Path::new("./resources/test/fixtures/flake8_quotes")
|
||||||
|
.join(path)
|
||||||
|
.as_path(),
|
||||||
|
&Settings {
|
||||||
|
flake8_quotes: flake8_quotes::settings::Settings {
|
||||||
|
inline_quotes: Quote::Single,
|
||||||
|
multiline_quotes: Quote::Single,
|
||||||
|
docstring_quotes: Quote::Double,
|
||||||
|
avoid_escape: true,
|
||||||
|
},
|
||||||
|
..Settings::for_rules(vec![
|
||||||
|
CheckCode::Q000,
|
||||||
|
CheckCode::Q001,
|
||||||
|
CheckCode::Q002,
|
||||||
|
CheckCode::Q003,
|
||||||
|
])
|
||||||
|
},
|
||||||
|
&fixer::Mode::Generate,
|
||||||
|
)?;
|
||||||
|
checks.sort_by_key(|check| check.location);
|
||||||
|
insta::assert_yaml_snapshot!(snapshot, checks);
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test_case(Path::new("docstring_doubles.py"))]
|
||||||
|
#[test_case(Path::new("docstring_doubles_module_multiline.py"))]
|
||||||
|
#[test_case(Path::new("docstring_doubles_module_singleline.py"))]
|
||||||
|
#[test_case(Path::new("docstring_doubles_class.py"))]
|
||||||
|
#[test_case(Path::new("docstring_doubles_function.py"))]
|
||||||
|
#[test_case(Path::new("docstring_singles.py"))]
|
||||||
|
#[test_case(Path::new("docstring_singles_module_multiline.py"))]
|
||||||
|
#[test_case(Path::new("docstring_singles_module_singleline.py"))]
|
||||||
|
#[test_case(Path::new("docstring_singles_class.py"))]
|
||||||
|
#[test_case(Path::new("docstring_singles_function.py"))]
|
||||||
|
fn single_docstring(path: &Path) -> Result<()> {
|
||||||
|
let snapshot = format!("single_docstring_{}", path.to_string_lossy());
|
||||||
|
let mut checks = check_path(
|
||||||
|
Path::new("./resources/test/fixtures/flake8_quotes")
|
||||||
|
.join(path)
|
||||||
|
.as_path(),
|
||||||
|
&Settings {
|
||||||
|
flake8_quotes: flake8_quotes::settings::Settings {
|
||||||
|
inline_quotes: Quote::Single,
|
||||||
|
multiline_quotes: Quote::Double,
|
||||||
|
docstring_quotes: Quote::Single,
|
||||||
|
avoid_escape: true,
|
||||||
|
},
|
||||||
|
..Settings::for_rules(vec![
|
||||||
|
CheckCode::Q000,
|
||||||
|
CheckCode::Q001,
|
||||||
|
CheckCode::Q002,
|
||||||
|
CheckCode::Q003,
|
||||||
|
])
|
||||||
|
},
|
||||||
|
&fixer::Mode::Generate,
|
||||||
|
)?;
|
||||||
|
checks.sort_by_key(|check| check.location);
|
||||||
|
insta::assert_yaml_snapshot!(snapshot, checks);
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -0,0 +1,50 @@
|
||||||
|
---
|
||||||
|
source: src/flake8_quotes/mod.rs
|
||||||
|
expression: checks
|
||||||
|
---
|
||||||
|
- kind:
|
||||||
|
BadQuotesMultilineString: single
|
||||||
|
location:
|
||||||
|
row: 5
|
||||||
|
column: 0
|
||||||
|
end_location:
|
||||||
|
row: 7
|
||||||
|
column: 3
|
||||||
|
fix: ~
|
||||||
|
- kind:
|
||||||
|
BadQuotesMultilineString: single
|
||||||
|
location:
|
||||||
|
row: 16
|
||||||
|
column: 4
|
||||||
|
end_location:
|
||||||
|
row: 18
|
||||||
|
column: 7
|
||||||
|
fix: ~
|
||||||
|
- kind:
|
||||||
|
BadQuotesMultilineString: single
|
||||||
|
location:
|
||||||
|
row: 21
|
||||||
|
column: 20
|
||||||
|
end_location:
|
||||||
|
row: 22
|
||||||
|
column: 37
|
||||||
|
fix: ~
|
||||||
|
- kind:
|
||||||
|
BadQuotesMultilineString: single
|
||||||
|
location:
|
||||||
|
row: 30
|
||||||
|
column: 8
|
||||||
|
end_location:
|
||||||
|
row: 32
|
||||||
|
column: 11
|
||||||
|
fix: ~
|
||||||
|
- kind:
|
||||||
|
BadQuotesMultilineString: single
|
||||||
|
location:
|
||||||
|
row: 35
|
||||||
|
column: 12
|
||||||
|
end_location:
|
||||||
|
row: 37
|
||||||
|
column: 15
|
||||||
|
fix: ~
|
||||||
|
|
|
@ -0,0 +1,23 @@
|
||||||
|
---
|
||||||
|
source: src/flake8_quotes/mod.rs
|
||||||
|
expression: checks
|
||||||
|
---
|
||||||
|
- kind:
|
||||||
|
BadQuotesMultilineString: single
|
||||||
|
location:
|
||||||
|
row: 3
|
||||||
|
column: 4
|
||||||
|
end_location:
|
||||||
|
row: 3
|
||||||
|
column: 27
|
||||||
|
fix: ~
|
||||||
|
- kind:
|
||||||
|
BadQuotesMultilineString: single
|
||||||
|
location:
|
||||||
|
row: 5
|
||||||
|
column: 22
|
||||||
|
end_location:
|
||||||
|
row: 5
|
||||||
|
column: 43
|
||||||
|
fix: ~
|
||||||
|
|
|
@ -0,0 +1,50 @@
|
||||||
|
---
|
||||||
|
source: src/flake8_quotes/mod.rs
|
||||||
|
expression: checks
|
||||||
|
---
|
||||||
|
- kind:
|
||||||
|
BadQuotesMultilineString: single
|
||||||
|
location:
|
||||||
|
row: 3
|
||||||
|
column: 4
|
||||||
|
end_location:
|
||||||
|
row: 3
|
||||||
|
column: 26
|
||||||
|
fix: ~
|
||||||
|
- kind:
|
||||||
|
BadQuotesMultilineString: single
|
||||||
|
location:
|
||||||
|
row: 11
|
||||||
|
column: 4
|
||||||
|
end_location:
|
||||||
|
row: 11
|
||||||
|
column: 26
|
||||||
|
fix: ~
|
||||||
|
- kind:
|
||||||
|
BadQuotesMultilineString: single
|
||||||
|
location:
|
||||||
|
row: 15
|
||||||
|
column: 38
|
||||||
|
end_location:
|
||||||
|
row: 17
|
||||||
|
column: 3
|
||||||
|
fix: ~
|
||||||
|
- kind:
|
||||||
|
BadQuotesMultilineString: single
|
||||||
|
location:
|
||||||
|
row: 17
|
||||||
|
column: 4
|
||||||
|
end_location:
|
||||||
|
row: 17
|
||||||
|
column: 19
|
||||||
|
fix: ~
|
||||||
|
- kind:
|
||||||
|
BadQuotesMultilineString: single
|
||||||
|
location:
|
||||||
|
row: 21
|
||||||
|
column: 4
|
||||||
|
end_location:
|
||||||
|
row: 21
|
||||||
|
column: 27
|
||||||
|
fix: ~
|
||||||
|
|
|
@ -0,0 +1,23 @@
|
||||||
|
---
|
||||||
|
source: src/flake8_quotes/mod.rs
|
||||||
|
expression: checks
|
||||||
|
---
|
||||||
|
- kind:
|
||||||
|
BadQuotesMultilineString: single
|
||||||
|
location:
|
||||||
|
row: 4
|
||||||
|
column: 0
|
||||||
|
end_location:
|
||||||
|
row: 6
|
||||||
|
column: 3
|
||||||
|
fix: ~
|
||||||
|
- kind:
|
||||||
|
BadQuotesMultilineString: single
|
||||||
|
location:
|
||||||
|
row: 9
|
||||||
|
column: 0
|
||||||
|
end_location:
|
||||||
|
row: 11
|
||||||
|
column: 3
|
||||||
|
fix: ~
|
||||||
|
|
|
@ -0,0 +1,23 @@
|
||||||
|
---
|
||||||
|
source: src/flake8_quotes/mod.rs
|
||||||
|
expression: checks
|
||||||
|
---
|
||||||
|
- kind:
|
||||||
|
BadQuotesMultilineString: single
|
||||||
|
location:
|
||||||
|
row: 2
|
||||||
|
column: 0
|
||||||
|
end_location:
|
||||||
|
row: 2
|
||||||
|
column: 31
|
||||||
|
fix: ~
|
||||||
|
- kind:
|
||||||
|
BadQuotesMultilineString: single
|
||||||
|
location:
|
||||||
|
row: 6
|
||||||
|
column: 0
|
||||||
|
end_location:
|
||||||
|
row: 6
|
||||||
|
column: 31
|
||||||
|
fix: ~
|
||||||
|
|
|
@ -0,0 +1,32 @@
|
||||||
|
---
|
||||||
|
source: src/flake8_quotes/mod.rs
|
||||||
|
expression: checks
|
||||||
|
---
|
||||||
|
- kind:
|
||||||
|
BadQuotesDocstring: double
|
||||||
|
location:
|
||||||
|
row: 1
|
||||||
|
column: 0
|
||||||
|
end_location:
|
||||||
|
row: 3
|
||||||
|
column: 3
|
||||||
|
fix: ~
|
||||||
|
- kind:
|
||||||
|
BadQuotesDocstring: double
|
||||||
|
location:
|
||||||
|
row: 14
|
||||||
|
column: 4
|
||||||
|
end_location:
|
||||||
|
row: 16
|
||||||
|
column: 7
|
||||||
|
fix: ~
|
||||||
|
- kind:
|
||||||
|
BadQuotesDocstring: double
|
||||||
|
location:
|
||||||
|
row: 26
|
||||||
|
column: 8
|
||||||
|
end_location:
|
||||||
|
row: 28
|
||||||
|
column: 11
|
||||||
|
fix: ~
|
||||||
|
|
|
@ -0,0 +1,32 @@
|
||||||
|
---
|
||||||
|
source: src/flake8_quotes/mod.rs
|
||||||
|
expression: checks
|
||||||
|
---
|
||||||
|
- kind:
|
||||||
|
BadQuotesDocstring: double
|
||||||
|
location:
|
||||||
|
row: 2
|
||||||
|
column: 4
|
||||||
|
end_location:
|
||||||
|
row: 2
|
||||||
|
column: 53
|
||||||
|
fix: ~
|
||||||
|
- kind:
|
||||||
|
BadQuotesDocstring: double
|
||||||
|
location:
|
||||||
|
row: 6
|
||||||
|
column: 8
|
||||||
|
end_location:
|
||||||
|
row: 6
|
||||||
|
column: 57
|
||||||
|
fix: ~
|
||||||
|
- kind:
|
||||||
|
BadQuotesDocstring: double
|
||||||
|
location:
|
||||||
|
row: 9
|
||||||
|
column: 28
|
||||||
|
end_location:
|
||||||
|
row: 9
|
||||||
|
column: 52
|
||||||
|
fix: ~
|
||||||
|
|
|
@ -0,0 +1,23 @@
|
||||||
|
---
|
||||||
|
source: src/flake8_quotes/mod.rs
|
||||||
|
expression: checks
|
||||||
|
---
|
||||||
|
- kind:
|
||||||
|
BadQuotesDocstring: double
|
||||||
|
location:
|
||||||
|
row: 2
|
||||||
|
column: 4
|
||||||
|
end_location:
|
||||||
|
row: 2
|
||||||
|
column: 56
|
||||||
|
fix: ~
|
||||||
|
- kind:
|
||||||
|
BadQuotesDocstring: double
|
||||||
|
location:
|
||||||
|
row: 8
|
||||||
|
column: 4
|
||||||
|
end_location:
|
||||||
|
row: 10
|
||||||
|
column: 7
|
||||||
|
fix: ~
|
||||||
|
|
|
@ -0,0 +1,14 @@
|
||||||
|
---
|
||||||
|
source: src/flake8_quotes/mod.rs
|
||||||
|
expression: checks
|
||||||
|
---
|
||||||
|
- kind:
|
||||||
|
BadQuotesDocstring: double
|
||||||
|
location:
|
||||||
|
row: 1
|
||||||
|
column: 0
|
||||||
|
end_location:
|
||||||
|
row: 3
|
||||||
|
column: 3
|
||||||
|
fix: ~
|
||||||
|
|
|
@ -0,0 +1,14 @@
|
||||||
|
---
|
||||||
|
source: src/flake8_quotes/mod.rs
|
||||||
|
expression: checks
|
||||||
|
---
|
||||||
|
- kind:
|
||||||
|
BadQuotesDocstring: double
|
||||||
|
location:
|
||||||
|
row: 1
|
||||||
|
column: 0
|
||||||
|
end_location:
|
||||||
|
row: 1
|
||||||
|
column: 49
|
||||||
|
fix: ~
|
||||||
|
|
|
@ -0,0 +1,23 @@
|
||||||
|
---
|
||||||
|
source: src/flake8_quotes/mod.rs
|
||||||
|
expression: checks
|
||||||
|
---
|
||||||
|
- kind:
|
||||||
|
BadQuotesInlineString: single
|
||||||
|
location:
|
||||||
|
row: 1
|
||||||
|
column: 24
|
||||||
|
end_location:
|
||||||
|
row: 1
|
||||||
|
column: 45
|
||||||
|
fix: ~
|
||||||
|
- kind:
|
||||||
|
BadQuotesInlineString: single
|
||||||
|
location:
|
||||||
|
row: 2
|
||||||
|
column: 24
|
||||||
|
end_location:
|
||||||
|
row: 2
|
||||||
|
column: 46
|
||||||
|
fix: ~
|
||||||
|
|
|
@ -0,0 +1,13 @@
|
||||||
|
---
|
||||||
|
source: src/flake8_quotes/mod.rs
|
||||||
|
expression: checks
|
||||||
|
---
|
||||||
|
- kind: AvoidQuoteEscape
|
||||||
|
location:
|
||||||
|
row: 1
|
||||||
|
column: 25
|
||||||
|
end_location:
|
||||||
|
row: 1
|
||||||
|
column: 47
|
||||||
|
fix: ~
|
||||||
|
|
|
@ -0,0 +1,14 @@
|
||||||
|
---
|
||||||
|
source: src/flake8_quotes/mod.rs
|
||||||
|
expression: checks
|
||||||
|
---
|
||||||
|
- kind:
|
||||||
|
BadQuotesMultilineString: single
|
||||||
|
location:
|
||||||
|
row: 1
|
||||||
|
column: 4
|
||||||
|
end_location:
|
||||||
|
row: 3
|
||||||
|
column: 12
|
||||||
|
fix: ~
|
||||||
|
|
|
@ -0,0 +1,6 @@
|
||||||
|
---
|
||||||
|
source: src/flake8_quotes/mod.rs
|
||||||
|
expression: checks
|
||||||
|
---
|
||||||
|
[]
|
||||||
|
|
|
@ -0,0 +1,6 @@
|
||||||
|
---
|
||||||
|
source: src/flake8_quotes/mod.rs
|
||||||
|
expression: checks
|
||||||
|
---
|
||||||
|
[]
|
||||||
|
|
|
@ -0,0 +1,32 @@
|
||||||
|
---
|
||||||
|
source: src/flake8_quotes/mod.rs
|
||||||
|
expression: checks
|
||||||
|
---
|
||||||
|
- kind:
|
||||||
|
BadQuotesDocstring: single
|
||||||
|
location:
|
||||||
|
row: 1
|
||||||
|
column: 0
|
||||||
|
end_location:
|
||||||
|
row: 3
|
||||||
|
column: 3
|
||||||
|
fix: ~
|
||||||
|
- kind:
|
||||||
|
BadQuotesDocstring: single
|
||||||
|
location:
|
||||||
|
row: 12
|
||||||
|
column: 4
|
||||||
|
end_location:
|
||||||
|
row: 14
|
||||||
|
column: 7
|
||||||
|
fix: ~
|
||||||
|
- kind:
|
||||||
|
BadQuotesDocstring: single
|
||||||
|
location:
|
||||||
|
row: 24
|
||||||
|
column: 8
|
||||||
|
end_location:
|
||||||
|
row: 26
|
||||||
|
column: 11
|
||||||
|
fix: ~
|
||||||
|
|
|
@ -0,0 +1,32 @@
|
||||||
|
---
|
||||||
|
source: src/flake8_quotes/mod.rs
|
||||||
|
expression: checks
|
||||||
|
---
|
||||||
|
- kind:
|
||||||
|
BadQuotesDocstring: single
|
||||||
|
location:
|
||||||
|
row: 2
|
||||||
|
column: 4
|
||||||
|
end_location:
|
||||||
|
row: 2
|
||||||
|
column: 53
|
||||||
|
fix: ~
|
||||||
|
- kind:
|
||||||
|
BadQuotesDocstring: single
|
||||||
|
location:
|
||||||
|
row: 6
|
||||||
|
column: 8
|
||||||
|
end_location:
|
||||||
|
row: 6
|
||||||
|
column: 57
|
||||||
|
fix: ~
|
||||||
|
- kind:
|
||||||
|
BadQuotesDocstring: single
|
||||||
|
location:
|
||||||
|
row: 9
|
||||||
|
column: 28
|
||||||
|
end_location:
|
||||||
|
row: 9
|
||||||
|
column: 52
|
||||||
|
fix: ~
|
||||||
|
|
|
@ -0,0 +1,23 @@
|
||||||
|
---
|
||||||
|
source: src/flake8_quotes/mod.rs
|
||||||
|
expression: checks
|
||||||
|
---
|
||||||
|
- kind:
|
||||||
|
BadQuotesDocstring: single
|
||||||
|
location:
|
||||||
|
row: 2
|
||||||
|
column: 4
|
||||||
|
end_location:
|
||||||
|
row: 2
|
||||||
|
column: 56
|
||||||
|
fix: ~
|
||||||
|
- kind:
|
||||||
|
BadQuotesDocstring: single
|
||||||
|
location:
|
||||||
|
row: 8
|
||||||
|
column: 4
|
||||||
|
end_location:
|
||||||
|
row: 10
|
||||||
|
column: 7
|
||||||
|
fix: ~
|
||||||
|
|
|
@ -0,0 +1,14 @@
|
||||||
|
---
|
||||||
|
source: src/flake8_quotes/mod.rs
|
||||||
|
expression: checks
|
||||||
|
---
|
||||||
|
- kind:
|
||||||
|
BadQuotesDocstring: single
|
||||||
|
location:
|
||||||
|
row: 1
|
||||||
|
column: 0
|
||||||
|
end_location:
|
||||||
|
row: 3
|
||||||
|
column: 3
|
||||||
|
fix: ~
|
||||||
|
|
|
@ -0,0 +1,14 @@
|
||||||
|
---
|
||||||
|
source: src/flake8_quotes/mod.rs
|
||||||
|
expression: checks
|
||||||
|
---
|
||||||
|
- kind:
|
||||||
|
BadQuotesDocstring: single
|
||||||
|
location:
|
||||||
|
row: 1
|
||||||
|
column: 0
|
||||||
|
end_location:
|
||||||
|
row: 1
|
||||||
|
column: 49
|
||||||
|
fix: ~
|
||||||
|
|
|
@ -0,0 +1,59 @@
|
||||||
|
---
|
||||||
|
source: src/flake8_quotes/mod.rs
|
||||||
|
expression: checks
|
||||||
|
---
|
||||||
|
- kind:
|
||||||
|
BadQuotesMultilineString: double
|
||||||
|
location:
|
||||||
|
row: 5
|
||||||
|
column: 0
|
||||||
|
end_location:
|
||||||
|
row: 7
|
||||||
|
column: 3
|
||||||
|
fix: ~
|
||||||
|
- kind:
|
||||||
|
BadQuotesMultilineString: double
|
||||||
|
location:
|
||||||
|
row: 11
|
||||||
|
column: 20
|
||||||
|
end_location:
|
||||||
|
row: 13
|
||||||
|
column: 3
|
||||||
|
fix: ~
|
||||||
|
- kind:
|
||||||
|
BadQuotesMultilineString: double
|
||||||
|
location:
|
||||||
|
row: 18
|
||||||
|
column: 4
|
||||||
|
end_location:
|
||||||
|
row: 20
|
||||||
|
column: 7
|
||||||
|
fix: ~
|
||||||
|
- kind:
|
||||||
|
BadQuotesMultilineString: double
|
||||||
|
location:
|
||||||
|
row: 23
|
||||||
|
column: 20
|
||||||
|
end_location:
|
||||||
|
row: 24
|
||||||
|
column: 37
|
||||||
|
fix: ~
|
||||||
|
- kind:
|
||||||
|
BadQuotesMultilineString: double
|
||||||
|
location:
|
||||||
|
row: 32
|
||||||
|
column: 8
|
||||||
|
end_location:
|
||||||
|
row: 34
|
||||||
|
column: 11
|
||||||
|
fix: ~
|
||||||
|
- kind:
|
||||||
|
BadQuotesMultilineString: double
|
||||||
|
location:
|
||||||
|
row: 37
|
||||||
|
column: 12
|
||||||
|
end_location:
|
||||||
|
row: 39
|
||||||
|
column: 15
|
||||||
|
fix: ~
|
||||||
|
|
|
@ -0,0 +1,23 @@
|
||||||
|
---
|
||||||
|
source: src/flake8_quotes/mod.rs
|
||||||
|
expression: checks
|
||||||
|
---
|
||||||
|
- kind:
|
||||||
|
BadQuotesMultilineString: double
|
||||||
|
location:
|
||||||
|
row: 3
|
||||||
|
column: 4
|
||||||
|
end_location:
|
||||||
|
row: 3
|
||||||
|
column: 27
|
||||||
|
fix: ~
|
||||||
|
- kind:
|
||||||
|
BadQuotesMultilineString: double
|
||||||
|
location:
|
||||||
|
row: 5
|
||||||
|
column: 22
|
||||||
|
end_location:
|
||||||
|
row: 5
|
||||||
|
column: 43
|
||||||
|
fix: ~
|
||||||
|
|
|
@ -0,0 +1,50 @@
|
||||||
|
---
|
||||||
|
source: src/flake8_quotes/mod.rs
|
||||||
|
expression: checks
|
||||||
|
---
|
||||||
|
- kind:
|
||||||
|
BadQuotesMultilineString: double
|
||||||
|
location:
|
||||||
|
row: 3
|
||||||
|
column: 4
|
||||||
|
end_location:
|
||||||
|
row: 3
|
||||||
|
column: 26
|
||||||
|
fix: ~
|
||||||
|
- kind:
|
||||||
|
BadQuotesMultilineString: double
|
||||||
|
location:
|
||||||
|
row: 11
|
||||||
|
column: 4
|
||||||
|
end_location:
|
||||||
|
row: 11
|
||||||
|
column: 26
|
||||||
|
fix: ~
|
||||||
|
- kind:
|
||||||
|
BadQuotesMultilineString: double
|
||||||
|
location:
|
||||||
|
row: 15
|
||||||
|
column: 38
|
||||||
|
end_location:
|
||||||
|
row: 17
|
||||||
|
column: 3
|
||||||
|
fix: ~
|
||||||
|
- kind:
|
||||||
|
BadQuotesMultilineString: double
|
||||||
|
location:
|
||||||
|
row: 17
|
||||||
|
column: 4
|
||||||
|
end_location:
|
||||||
|
row: 17
|
||||||
|
column: 19
|
||||||
|
fix: ~
|
||||||
|
- kind:
|
||||||
|
BadQuotesMultilineString: double
|
||||||
|
location:
|
||||||
|
row: 21
|
||||||
|
column: 4
|
||||||
|
end_location:
|
||||||
|
row: 21
|
||||||
|
column: 27
|
||||||
|
fix: ~
|
||||||
|
|
|
@ -0,0 +1,23 @@
|
||||||
|
---
|
||||||
|
source: src/flake8_quotes/mod.rs
|
||||||
|
expression: checks
|
||||||
|
---
|
||||||
|
- kind:
|
||||||
|
BadQuotesMultilineString: double
|
||||||
|
location:
|
||||||
|
row: 4
|
||||||
|
column: 0
|
||||||
|
end_location:
|
||||||
|
row: 6
|
||||||
|
column: 3
|
||||||
|
fix: ~
|
||||||
|
- kind:
|
||||||
|
BadQuotesMultilineString: double
|
||||||
|
location:
|
||||||
|
row: 9
|
||||||
|
column: 0
|
||||||
|
end_location:
|
||||||
|
row: 11
|
||||||
|
column: 3
|
||||||
|
fix: ~
|
||||||
|
|
|
@ -0,0 +1,23 @@
|
||||||
|
---
|
||||||
|
source: src/flake8_quotes/mod.rs
|
||||||
|
expression: checks
|
||||||
|
---
|
||||||
|
- kind:
|
||||||
|
BadQuotesMultilineString: double
|
||||||
|
location:
|
||||||
|
row: 2
|
||||||
|
column: 0
|
||||||
|
end_location:
|
||||||
|
row: 2
|
||||||
|
column: 31
|
||||||
|
fix: ~
|
||||||
|
- kind:
|
||||||
|
BadQuotesMultilineString: double
|
||||||
|
location:
|
||||||
|
row: 6
|
||||||
|
column: 0
|
||||||
|
end_location:
|
||||||
|
row: 6
|
||||||
|
column: 31
|
||||||
|
fix: ~
|
||||||
|
|
|
@ -0,0 +1,23 @@
|
||||||
|
---
|
||||||
|
source: src/flake8_quotes/mod.rs
|
||||||
|
expression: checks
|
||||||
|
---
|
||||||
|
- kind:
|
||||||
|
BadQuotesInlineString: double
|
||||||
|
location:
|
||||||
|
row: 1
|
||||||
|
column: 24
|
||||||
|
end_location:
|
||||||
|
row: 1
|
||||||
|
column: 45
|
||||||
|
fix: ~
|
||||||
|
- kind:
|
||||||
|
BadQuotesInlineString: double
|
||||||
|
location:
|
||||||
|
row: 2
|
||||||
|
column: 24
|
||||||
|
end_location:
|
||||||
|
row: 2
|
||||||
|
column: 46
|
||||||
|
fix: ~
|
||||||
|
|
|
@ -0,0 +1,13 @@
|
||||||
|
---
|
||||||
|
source: src/flake8_quotes/mod.rs
|
||||||
|
expression: checks
|
||||||
|
---
|
||||||
|
- kind: AvoidQuoteEscape
|
||||||
|
location:
|
||||||
|
row: 1
|
||||||
|
column: 25
|
||||||
|
end_location:
|
||||||
|
row: 1
|
||||||
|
column: 47
|
||||||
|
fix: ~
|
||||||
|
|
|
@ -0,0 +1,14 @@
|
||||||
|
---
|
||||||
|
source: src/flake8_quotes/mod.rs
|
||||||
|
expression: checks
|
||||||
|
---
|
||||||
|
- kind:
|
||||||
|
BadQuotesMultilineString: double
|
||||||
|
location:
|
||||||
|
row: 1
|
||||||
|
column: 4
|
||||||
|
end_location:
|
||||||
|
row: 3
|
||||||
|
column: 12
|
||||||
|
fix: ~
|
||||||
|
|
|
@ -0,0 +1,6 @@
|
||||||
|
---
|
||||||
|
source: src/flake8_quotes/mod.rs
|
||||||
|
expression: checks
|
||||||
|
---
|
||||||
|
[]
|
||||||
|
|
|
@ -0,0 +1,6 @@
|
||||||
|
---
|
||||||
|
source: src/flake8_quotes/mod.rs
|
||||||
|
expression: checks
|
||||||
|
---
|
||||||
|
[]
|
||||||
|
|
|
@ -25,6 +25,7 @@ pub mod cli;
|
||||||
pub mod code_gen;
|
pub mod code_gen;
|
||||||
mod cst;
|
mod cst;
|
||||||
mod docstrings;
|
mod docstrings;
|
||||||
|
pub mod flake8_annotations;
|
||||||
mod flake8_bugbear;
|
mod flake8_bugbear;
|
||||||
mod flake8_builtins;
|
mod flake8_builtins;
|
||||||
mod flake8_comprehensions;
|
mod flake8_comprehensions;
|
||||||
|
|
|
@ -13,7 +13,7 @@ use crate::docstrings::definition::{Definition, DefinitionKind};
|
||||||
use crate::docstrings::helpers;
|
use crate::docstrings::helpers;
|
||||||
use crate::docstrings::sections::{section_contexts, SectionContext};
|
use crate::docstrings::sections::{section_contexts, SectionContext};
|
||||||
use crate::docstrings::styles::SectionStyle;
|
use crate::docstrings::styles::SectionStyle;
|
||||||
use crate::visibility::{is_init, is_magic, is_overload, is_static, Visibility};
|
use crate::visibility::{is_init, is_magic, is_overload, is_staticmethod, Visibility};
|
||||||
|
|
||||||
/// D100, D101, D102, D103, D104, D105, D106, D107
|
/// D100, D101, D102, D103, D104, D105, D106, D107
|
||||||
pub fn not_missing(
|
pub fn not_missing(
|
||||||
|
@ -1308,7 +1308,8 @@ fn missing_args(checker: &mut Checker, definition: &Definition, docstrings_args:
|
||||||
.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(
|
usize::from(
|
||||||
matches!(definition.kind, DefinitionKind::Method(_)) && !is_static(parent),
|
matches!(definition.kind, DefinitionKind::Method(_))
|
||||||
|
&& !is_staticmethod(parent),
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
.collect();
|
.collect();
|
||||||
|
|
|
@ -12,7 +12,7 @@ use regex::Regex;
|
||||||
use crate::checks_gen::CheckCodePrefix;
|
use crate::checks_gen::CheckCodePrefix;
|
||||||
use crate::settings::pyproject::load_options;
|
use crate::settings::pyproject::load_options;
|
||||||
use crate::settings::types::{FilePattern, PythonVersion};
|
use crate::settings::types::{FilePattern, PythonVersion};
|
||||||
use crate::{flake8_quotes, pep8_naming};
|
use crate::{flake8_annotations, flake8_quotes, pep8_naming};
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub struct Configuration {
|
pub struct Configuration {
|
||||||
|
@ -27,6 +27,7 @@ pub struct Configuration {
|
||||||
pub select: Vec<CheckCodePrefix>,
|
pub select: Vec<CheckCodePrefix>,
|
||||||
pub target_version: PythonVersion,
|
pub target_version: PythonVersion,
|
||||||
// Plugins
|
// Plugins
|
||||||
|
pub flake8_annotations: flake8_annotations::settings::Settings,
|
||||||
pub flake8_quotes: flake8_quotes::settings::Settings,
|
pub flake8_quotes: flake8_quotes::settings::Settings,
|
||||||
pub pep8_naming: pep8_naming::settings::Settings,
|
pub pep8_naming: pep8_naming::settings::Settings,
|
||||||
}
|
}
|
||||||
|
@ -95,6 +96,10 @@ impl Configuration {
|
||||||
line_length: options.line_length.unwrap_or(88),
|
line_length: options.line_length.unwrap_or(88),
|
||||||
per_file_ignores: options.per_file_ignores.unwrap_or_default(),
|
per_file_ignores: options.per_file_ignores.unwrap_or_default(),
|
||||||
// Plugins
|
// Plugins
|
||||||
|
flake8_annotations: options
|
||||||
|
.flake8_annotations
|
||||||
|
.map(flake8_annotations::settings::Settings::from_options)
|
||||||
|
.unwrap_or_default(),
|
||||||
flake8_quotes: options
|
flake8_quotes: options
|
||||||
.flake8_quotes
|
.flake8_quotes
|
||||||
.map(flake8_quotes::settings::Settings::from_options)
|
.map(flake8_quotes::settings::Settings::from_options)
|
||||||
|
|
|
@ -11,7 +11,7 @@ use crate::checks::CheckCode;
|
||||||
use crate::checks_gen::{CheckCodePrefix, PrefixSpecificity};
|
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::{flake8_quotes, pep8_naming};
|
use crate::{flake8_annotations, flake8_quotes, pep8_naming};
|
||||||
|
|
||||||
pub mod configuration;
|
pub mod configuration;
|
||||||
pub mod options;
|
pub mod options;
|
||||||
|
@ -29,6 +29,7 @@ pub struct Settings {
|
||||||
pub per_file_ignores: Vec<PerFileIgnore>,
|
pub per_file_ignores: Vec<PerFileIgnore>,
|
||||||
pub target_version: PythonVersion,
|
pub target_version: PythonVersion,
|
||||||
// Plugins
|
// Plugins
|
||||||
|
pub flake8_annotations: flake8_annotations::settings::Settings,
|
||||||
pub flake8_quotes: flake8_quotes::settings::Settings,
|
pub flake8_quotes: flake8_quotes::settings::Settings,
|
||||||
pub pep8_naming: pep8_naming::settings::Settings,
|
pub pep8_naming: pep8_naming::settings::Settings,
|
||||||
}
|
}
|
||||||
|
@ -45,6 +46,7 @@ impl Settings {
|
||||||
),
|
),
|
||||||
exclude: config.exclude,
|
exclude: config.exclude,
|
||||||
extend_exclude: config.extend_exclude,
|
extend_exclude: config.extend_exclude,
|
||||||
|
flake8_annotations: config.flake8_annotations,
|
||||||
flake8_quotes: config.flake8_quotes,
|
flake8_quotes: config.flake8_quotes,
|
||||||
line_length: config.line_length,
|
line_length: config.line_length,
|
||||||
pep8_naming: config.pep8_naming,
|
pep8_naming: config.pep8_naming,
|
||||||
|
@ -62,6 +64,7 @@ impl Settings {
|
||||||
line_length: 88,
|
line_length: 88,
|
||||||
per_file_ignores: Default::default(),
|
per_file_ignores: Default::default(),
|
||||||
target_version: PythonVersion::Py310,
|
target_version: PythonVersion::Py310,
|
||||||
|
flake8_annotations: Default::default(),
|
||||||
flake8_quotes: Default::default(),
|
flake8_quotes: Default::default(),
|
||||||
pep8_naming: Default::default(),
|
pep8_naming: Default::default(),
|
||||||
}
|
}
|
||||||
|
@ -76,6 +79,7 @@ impl Settings {
|
||||||
line_length: 88,
|
line_length: 88,
|
||||||
per_file_ignores: Default::default(),
|
per_file_ignores: Default::default(),
|
||||||
target_version: PythonVersion::Py310,
|
target_version: PythonVersion::Py310,
|
||||||
|
flake8_annotations: Default::default(),
|
||||||
flake8_quotes: Default::default(),
|
flake8_quotes: Default::default(),
|
||||||
pep8_naming: Default::default(),
|
pep8_naming: Default::default(),
|
||||||
}
|
}
|
||||||
|
@ -95,6 +99,7 @@ impl Hash for Settings {
|
||||||
}
|
}
|
||||||
self.target_version.hash(state);
|
self.target_version.hash(state);
|
||||||
// Add plugin properties in alphabetical order.
|
// Add plugin properties in alphabetical order.
|
||||||
|
self.flake8_annotations.hash(state);
|
||||||
self.flake8_quotes.hash(state);
|
self.flake8_quotes.hash(state);
|
||||||
self.pep8_naming.hash(state);
|
self.pep8_naming.hash(state);
|
||||||
}
|
}
|
||||||
|
|
|
@ -6,7 +6,7 @@ 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::{flake8_quotes, pep8_naming};
|
use crate::{flake8_annotations, flake8_quotes, pep8_naming};
|
||||||
|
|
||||||
#[derive(Debug, PartialEq, Eq, Serialize, Deserialize, Default)]
|
#[derive(Debug, PartialEq, Eq, Serialize, Deserialize, Default)]
|
||||||
#[serde(deny_unknown_fields, rename_all = "kebab-case")]
|
#[serde(deny_unknown_fields, rename_all = "kebab-case")]
|
||||||
|
@ -22,6 +22,7 @@ pub struct Options {
|
||||||
pub dummy_variable_rgx: Option<String>,
|
pub dummy_variable_rgx: Option<String>,
|
||||||
pub target_version: Option<PythonVersion>,
|
pub target_version: Option<PythonVersion>,
|
||||||
// Plugins
|
// Plugins
|
||||||
|
pub flake8_annotations: Option<flake8_annotations::settings::Options>,
|
||||||
pub flake8_quotes: Option<flake8_quotes::settings::Options>,
|
pub flake8_quotes: Option<flake8_quotes::settings::Options>,
|
||||||
pub pep8_naming: Option<pep8_naming::settings::Options>,
|
pub pep8_naming: Option<pep8_naming::settings::Options>,
|
||||||
}
|
}
|
||||||
|
|
|
@ -143,6 +143,7 @@ mod tests {
|
||||||
per_file_ignores: None,
|
per_file_ignores: None,
|
||||||
dummy_variable_rgx: None,
|
dummy_variable_rgx: None,
|
||||||
target_version: None,
|
target_version: None,
|
||||||
|
flake8_annotations: None,
|
||||||
flake8_quotes: None,
|
flake8_quotes: None,
|
||||||
pep8_naming: None,
|
pep8_naming: None,
|
||||||
})
|
})
|
||||||
|
@ -170,6 +171,7 @@ line-length = 79
|
||||||
per_file_ignores: None,
|
per_file_ignores: None,
|
||||||
dummy_variable_rgx: None,
|
dummy_variable_rgx: None,
|
||||||
target_version: None,
|
target_version: None,
|
||||||
|
flake8_annotations: None,
|
||||||
flake8_quotes: None,
|
flake8_quotes: None,
|
||||||
pep8_naming: None,
|
pep8_naming: None,
|
||||||
})
|
})
|
||||||
|
@ -197,6 +199,7 @@ exclude = ["foo.py"]
|
||||||
per_file_ignores: None,
|
per_file_ignores: None,
|
||||||
dummy_variable_rgx: None,
|
dummy_variable_rgx: None,
|
||||||
target_version: None,
|
target_version: None,
|
||||||
|
flake8_annotations: None,
|
||||||
flake8_quotes: None,
|
flake8_quotes: None,
|
||||||
pep8_naming: None,
|
pep8_naming: None,
|
||||||
})
|
})
|
||||||
|
@ -224,6 +227,7 @@ select = ["E501"]
|
||||||
per_file_ignores: None,
|
per_file_ignores: None,
|
||||||
dummy_variable_rgx: None,
|
dummy_variable_rgx: None,
|
||||||
target_version: None,
|
target_version: None,
|
||||||
|
flake8_annotations: None,
|
||||||
flake8_quotes: None,
|
flake8_quotes: None,
|
||||||
pep8_naming: None,
|
pep8_naming: None,
|
||||||
})
|
})
|
||||||
|
@ -252,6 +256,7 @@ ignore = ["E501"]
|
||||||
per_file_ignores: None,
|
per_file_ignores: None,
|
||||||
dummy_variable_rgx: None,
|
dummy_variable_rgx: None,
|
||||||
target_version: None,
|
target_version: None,
|
||||||
|
flake8_annotations: None,
|
||||||
flake8_quotes: None,
|
flake8_quotes: None,
|
||||||
pep8_naming: None,
|
pep8_naming: None,
|
||||||
})
|
})
|
||||||
|
@ -326,6 +331,7 @@ other-attribute = 1
|
||||||
),])),
|
),])),
|
||||||
dummy_variable_rgx: None,
|
dummy_variable_rgx: None,
|
||||||
target_version: None,
|
target_version: None,
|
||||||
|
flake8_annotations: None,
|
||||||
flake8_quotes: Some(flake8_quotes::settings::Options {
|
flake8_quotes: Some(flake8_quotes::settings::Options {
|
||||||
inline_quotes: Some(Quote::Single),
|
inline_quotes: Some(Quote::Single),
|
||||||
multiline_quotes: Some(Quote::Double),
|
multiline_quotes: Some(Quote::Double),
|
||||||
|
|
|
@ -7,7 +7,7 @@ use regex::Regex;
|
||||||
|
|
||||||
use crate::checks_gen::CheckCodePrefix;
|
use crate::checks_gen::CheckCodePrefix;
|
||||||
use crate::settings::types::{FilePattern, PythonVersion};
|
use crate::settings::types::{FilePattern, PythonVersion};
|
||||||
use crate::{flake8_quotes, pep8_naming, Configuration};
|
use crate::{flake8_annotations, flake8_quotes, pep8_naming, Configuration};
|
||||||
|
|
||||||
/// Struct to render user-facing exclusion patterns.
|
/// Struct to render user-facing exclusion patterns.
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
|
@ -46,6 +46,7 @@ pub struct UserConfiguration {
|
||||||
pub select: Vec<CheckCodePrefix>,
|
pub select: Vec<CheckCodePrefix>,
|
||||||
pub target_version: PythonVersion,
|
pub target_version: PythonVersion,
|
||||||
// Plugins
|
// Plugins
|
||||||
|
pub flake8_annotations: flake8_annotations::settings::Settings,
|
||||||
pub flake8_quotes: flake8_quotes::settings::Settings,
|
pub flake8_quotes: flake8_quotes::settings::Settings,
|
||||||
pub pep8_naming: pep8_naming::settings::Settings,
|
pub pep8_naming: pep8_naming::settings::Settings,
|
||||||
// Non-settings exposed to the user
|
// Non-settings exposed to the user
|
||||||
|
@ -78,6 +79,7 @@ impl UserConfiguration {
|
||||||
per_file_ignores: configuration.per_file_ignores,
|
per_file_ignores: configuration.per_file_ignores,
|
||||||
select: configuration.select,
|
select: configuration.select,
|
||||||
target_version: configuration.target_version,
|
target_version: configuration.target_version,
|
||||||
|
flake8_annotations: configuration.flake8_annotations,
|
||||||
flake8_quotes: configuration.flake8_quotes,
|
flake8_quotes: configuration.flake8_quotes,
|
||||||
pep8_naming: configuration.pep8_naming,
|
pep8_naming: configuration.pep8_naming,
|
||||||
project_root,
|
project_root,
|
||||||
|
|
|
@ -28,13 +28,24 @@ pub struct VisibleScope {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns `true` if a function is a "static method".
|
/// Returns `true` if a function is a "static method".
|
||||||
pub fn is_static(stmt: &Stmt) -> bool {
|
pub fn is_staticmethod(stmt: &Stmt) -> bool {
|
||||||
match &stmt.node {
|
match &stmt.node {
|
||||||
StmtKind::FunctionDef { decorator_list, .. }
|
StmtKind::FunctionDef { decorator_list, .. }
|
||||||
| StmtKind::AsyncFunctionDef { decorator_list, .. } => decorator_list
|
| StmtKind::AsyncFunctionDef { decorator_list, .. } => decorator_list
|
||||||
.iter()
|
.iter()
|
||||||
.any(|expr| match_name_or_attr(expr, "staticmethod")),
|
.any(|expr| match_name_or_attr(expr, "staticmethod")),
|
||||||
_ => panic!("Found non-FunctionDef in is_overload"),
|
_ => panic!("Found non-FunctionDef in is_staticmethod"),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns `true` if a function is a "class method".
|
||||||
|
pub fn is_classmethod(stmt: &Stmt) -> bool {
|
||||||
|
match &stmt.node {
|
||||||
|
StmtKind::FunctionDef { decorator_list, .. }
|
||||||
|
| StmtKind::AsyncFunctionDef { decorator_list, .. } => decorator_list
|
||||||
|
.iter()
|
||||||
|
.any(|expr| match_name_or_attr(expr, "classmethod")),
|
||||||
|
_ => panic!("Found non-FunctionDef in is_classmethod"),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue