Compare commits
15 Commits
v0.0.104
...
charlie/as
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
b9a119d335 | ||
|
|
de6435f41d | ||
|
|
589ae48f8c | ||
|
|
35cc3094b5 | ||
|
|
07c9fc55f6 | ||
|
|
7773e9728b | ||
|
|
8fc435bad9 | ||
|
|
dd20b23576 | ||
|
|
da8ee1df3e | ||
|
|
7f77ed0f86 | ||
|
|
1b33cfb9cb | ||
|
|
dbc64f1faa | ||
|
|
f4b5f0d259 | ||
|
|
85b882fc54 | ||
|
|
99d9aa61bf |
@@ -1,6 +1,6 @@
|
||||
repos:
|
||||
- repo: https://github.com/charliermarsh/ruff-pre-commit
|
||||
rev: v0.0.104
|
||||
rev: v0.0.105
|
||||
hooks:
|
||||
- id: ruff
|
||||
|
||||
|
||||
6
Cargo.lock
generated
6
Cargo.lock
generated
@@ -920,7 +920,7 @@ checksum = "0ce7134b9999ecaf8bcd65542e436736ef32ddca1b3e06094cb6ec5755203b80"
|
||||
|
||||
[[package]]
|
||||
name = "flake8-to-ruff"
|
||||
version = "0.0.104-dev.0"
|
||||
version = "0.0.105-dev.0"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"clap 4.0.15",
|
||||
@@ -2221,7 +2221,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "ruff"
|
||||
version = "0.0.104"
|
||||
version = "0.0.105"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"assert_cmd",
|
||||
@@ -2266,7 +2266,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "ruff_dev"
|
||||
version = "0.0.104"
|
||||
version = "0.0.105"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"clap 4.0.15",
|
||||
|
||||
@@ -6,7 +6,7 @@ members = [
|
||||
|
||||
[package]
|
||||
name = "ruff"
|
||||
version = "0.0.104"
|
||||
version = "0.0.105"
|
||||
edition = "2021"
|
||||
|
||||
[lib]
|
||||
|
||||
58
README.md
58
README.md
@@ -15,15 +15,15 @@ An extremely fast Python linter, written in Rust.
|
||||
<i>Linting the CPython codebase from scratch.</i>
|
||||
</p>
|
||||
|
||||
- ⚡️ 10-100x faster than existing linters
|
||||
- 🐍 Installable via `pip`
|
||||
- 🤝 Python 3.10 compatibility
|
||||
- 🛠️ `pyproject.toml` support
|
||||
- 📦 [ESLint](https://eslint.org/docs/latest/user-guide/command-line-interface#caching)-inspired cache support
|
||||
- 🔧 [ESLint](https://eslint.org/docs/latest/user-guide/command-line-interface#--fix)-inspired autofix support (e.g., automatically remove unused imports)
|
||||
- 👀 [TypeScript](https://www.typescriptlang.org/docs/handbook/configuring-watch.html)-inspired `--watch` support, for continuous file monitoring
|
||||
- ⚖️ [Near-parity](#how-does-ruff-compare-to-flake8) with the built-in Flake8 rule set
|
||||
- 🔌 Native re-implementations of popular Flake8 plugins, like [`flake8-docstrings`](https://pypi.org/project/flake8-docstrings/) ([`pydocstyle`](https://pypi.org/project/pydocstyle/))
|
||||
- ⚡️ 10-100x faster than existing linters
|
||||
- 🐍 Installable via `pip`
|
||||
- 🤝 Python 3.10 compatibility
|
||||
- 🛠️ `pyproject.toml` support
|
||||
- 📦 Built-in caching, to avoid re-analyzing unchanged files
|
||||
- 🔧 `--fix` support, for automatic error correction (e.g., automatically remove unused imports)
|
||||
- 👀 `--watch` support, for continuous file monitoring
|
||||
- ⚖️ [Near-parity](#how-does-ruff-compare-to-flake8) with the built-in Flake8 rule set
|
||||
- 🔌 Native re-implementations of popular Flake8 plugins, like [`flake8-docstrings`](https://pypi.org/project/flake8-docstrings/) ([`pydocstyle`](https://pypi.org/project/pydocstyle/))
|
||||
|
||||
Ruff aims to be orders of magnitude faster than alternative tools while integrating more
|
||||
functionality behind a single, common interface. Ruff can be used to replace Flake8 (plus a variety
|
||||
@@ -31,6 +31,9 @@ of plugins), [`pydocstyle`](https://pypi.org/project/pydocstyle/), [`yesqa`](htt
|
||||
and even a subset of [`pyupgrade`](https://pypi.org/project/pyupgrade/) and [`autoflake`](https://pypi.org/project/autoflake/)
|
||||
all while executing tens or hundreds of times faster than any individual tool.
|
||||
|
||||
(Coming from Flake8? Try [`flake8-to-ruff`](https://pypi.org/project/flake8-to-ruff/) to
|
||||
automatically convert your existing configuration.)
|
||||
|
||||
Ruff is actively developed and used in major open-source projects
|
||||
like [Zulip](https://github.com/zulip/zulip), [pydantic](https://github.com/pydantic/pydantic),
|
||||
and [Saleor](https://github.com/saleor/saleor).
|
||||
@@ -52,8 +55,9 @@ Read the [launch blog post](https://notes.crmarsh.com/python-tooling-could-be-mu
|
||||
8. [flake8-builtins](#flake8-builtins)
|
||||
9. [flake8-print](#flake8-print)
|
||||
10. [flake8-quotes](#flake8-quotes)
|
||||
11. [Ruff-specific rules](#ruff-specific-rules)
|
||||
12. [Meta rules](#meta-rules)
|
||||
11. [flake8-annotations](#flake8-annotations)
|
||||
12. [Ruff-specific rules](#ruff-specific-rules)
|
||||
13. [Meta rules](#meta-rules)
|
||||
5. [Editor Integrations](#editor-integrations)
|
||||
6. [FAQ](#faq)
|
||||
7. [Development](#development)
|
||||
@@ -93,7 +97,7 @@ Ruff also works with [pre-commit](https://pre-commit.com):
|
||||
```yaml
|
||||
repos:
|
||||
- repo: https://github.com/charliermarsh/ruff-pre-commit
|
||||
rev: v0.0.104
|
||||
rev: v0.0.105
|
||||
hooks:
|
||||
- id: ruff
|
||||
```
|
||||
@@ -199,7 +203,7 @@ Options:
|
||||
Exit with status code "0", even upon detecting errors
|
||||
-w, --watch
|
||||
Run in watch mode by re-running whenever files change
|
||||
-f, --fix
|
||||
--fix
|
||||
Attempt to automatically fix lint errors
|
||||
-n, --no-cache
|
||||
Disable cache reads
|
||||
@@ -481,6 +485,8 @@ For more, see [flake8-bugbear](https://pypi.org/project/flake8-bugbear/22.10.27/
|
||||
| ---- | ---- | ------- | --- |
|
||||
| B002 | UnaryPrefixIncrement | Python does not support the unary prefix increment. | |
|
||||
| B003 | AssignmentToOsEnviron | Assigning to `os.environ` doesn't clear the environment. | |
|
||||
| B004 | UnreliableCallableCheck | Using `hasattr(x, '__call__')` to test if x is callable is unreliable. Use `callable(x)` for consistent results. | |
|
||||
| B005 | StripWithMultiCharacters | Using `.strip()` with multi-character strings is misleading the reader. | |
|
||||
| B006 | MutableArgumentDefault | Do not use mutable data structures for argument defaults. | |
|
||||
| B007 | UnusedLoopControlVariable | Loop control variable `i` not used within the loop body. | 🛠 |
|
||||
| B008 | FunctionCallArgumentDefault | Do not perform function calls in argument defaults. | |
|
||||
@@ -523,6 +529,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 | |
|
||||
| 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
|
||||
|
||||
| Code | Name | Message | Fix |
|
||||
@@ -588,6 +611,9 @@ stylistic lint rules that are obviated by autoformatting.
|
||||
|
||||
### How does Ruff compare to Flake8?
|
||||
|
||||
(Coming from Flake8? Try [`flake8-to-ruff`](https://pypi.org/project/flake8-to-ruff/) to
|
||||
automatically convert your existing configuration.)
|
||||
|
||||
Ruff can be used as a (near) drop-in replacement for Flake8 when used (1) without or with a small
|
||||
number of plugins, (2) alongside Black, and (3) on Python 3 code.
|
||||
|
||||
@@ -605,8 +631,9 @@ including:
|
||||
- [`flake8-super`](https://pypi.org/project/flake8-super/)
|
||||
- [`flake8-print`](https://pypi.org/project/flake8-print/)
|
||||
- [`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-bugbear`](https://pypi.org/project/flake8-bugbear/) (15/32)
|
||||
- [`flake8-bugbear`](https://pypi.org/project/flake8-bugbear/) (17/32)
|
||||
- [`pyupgrade`](https://pypi.org/project/pyupgrade/) (11/34)
|
||||
- [`autoflake`](https://pypi.org/project/autoflake/) (1/7)
|
||||
|
||||
@@ -627,8 +654,9 @@ 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-print`](https://pypi.org/project/flake8-print/)
|
||||
- [`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-bugbear`](https://pypi.org/project/flake8-bugbear/) (15/32)
|
||||
- [`flake8-bugbear`](https://pypi.org/project/flake8-bugbear/) (17/32)
|
||||
|
||||
Ruff also implements the functionality that you get from [`yesqa`](https://github.com/asottile/yesqa),
|
||||
and a subset of the rules implemented in [`pyupgrade`](https://pypi.org/project/pyupgrade/) (11/34).
|
||||
|
||||
4
flake8_to_ruff/Cargo.lock
generated
4
flake8_to_ruff/Cargo.lock
generated
@@ -771,7 +771,7 @@ checksum = "0ce7134b9999ecaf8bcd65542e436736ef32ddca1b3e06094cb6ec5755203b80"
|
||||
|
||||
[[package]]
|
||||
name = "flake8_to_ruff"
|
||||
version = "0.0.104"
|
||||
version = "0.0.105"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"clap",
|
||||
@@ -1975,7 +1975,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "ruff"
|
||||
version = "0.0.104"
|
||||
version = "0.0.105"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"bincode",
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "flake8-to-ruff"
|
||||
version = "0.0.104-dev.0"
|
||||
version = "0.0.105-dev.0"
|
||||
edition = "2021"
|
||||
|
||||
[lib]
|
||||
|
||||
@@ -1,10 +1,11 @@
|
||||
use std::collections::{BTreeSet, HashMap};
|
||||
|
||||
use anyhow::Result;
|
||||
use ruff::checks_gen::CheckCodePrefix;
|
||||
use ruff::flake8_quotes::settings::Quote;
|
||||
use ruff::settings::options::Options;
|
||||
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::{parser, plugin};
|
||||
@@ -13,6 +14,29 @@ pub fn convert(
|
||||
flake8: &HashMap<String, Option<String>>,
|
||||
plugins: Option<Vec<Plugin>>,
|
||||
) -> Result<Pyproject> {
|
||||
// Extract all referenced check code prefixes, to power plugin inference.
|
||||
let mut referenced_codes: BTreeSet<CheckCodePrefix> = Default::default();
|
||||
for (key, value) in flake8 {
|
||||
if let Some(value) = value {
|
||||
match key.as_str() {
|
||||
"select" | "ignore" | "extend-select" | "extend_select" | "extend-ignore"
|
||||
| "extend_ignore" => {
|
||||
referenced_codes.extend(parser::parse_prefix_codes(value.as_ref()));
|
||||
}
|
||||
"per-file-ignores" | "per_file_ignores" => {
|
||||
if let Ok(per_file_ignores) =
|
||||
parser::parse_files_to_codes_mapping(value.as_ref())
|
||||
{
|
||||
for (_, codes) in parser::collect_per_file_ignores(per_file_ignores) {
|
||||
referenced_codes.extend(codes);
|
||||
}
|
||||
}
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Check if the user has specified a `select`. If not, we'll add our own
|
||||
// default `select`, and populate it based on user plugins.
|
||||
let mut select = flake8
|
||||
@@ -25,7 +49,12 @@ pub fn convert(
|
||||
.unwrap_or_else(|| {
|
||||
plugin::resolve_select(
|
||||
flake8,
|
||||
&plugins.unwrap_or_else(|| plugin::infer_plugins(flake8)),
|
||||
&plugins.unwrap_or_else(|| {
|
||||
plugin::infer_plugins_from_options(flake8)
|
||||
.into_iter()
|
||||
.chain(plugin::infer_plugins_from_codes(&referenced_codes))
|
||||
.collect()
|
||||
}),
|
||||
)
|
||||
});
|
||||
let mut ignore = flake8
|
||||
@@ -39,6 +68,7 @@ pub fn convert(
|
||||
|
||||
// Parse each supported option.
|
||||
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 pep8_naming: pep8_naming::settings::Options = Default::default();
|
||||
for (key, value) in flake8 {
|
||||
@@ -51,6 +81,7 @@ pub fn convert(
|
||||
},
|
||||
"select" => {
|
||||
// No-op (handled above).
|
||||
select.extend(parser::parse_prefix_codes(value.as_ref()));
|
||||
}
|
||||
"ignore" => {
|
||||
// No-op (handled above).
|
||||
@@ -78,6 +109,25 @@ pub fn convert(
|
||||
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
|
||||
"quotes" | "inline-quotes" | "inline_quotes" => match value.trim() {
|
||||
"'" | "single" => flake8_quotes.inline_quotes = Some(Quote::Single),
|
||||
@@ -94,10 +144,9 @@ pub fn convert(
|
||||
"\"" | "double" => flake8_quotes.docstring_quotes = Some(Quote::Single),
|
||||
_ => eprintln!("Unexpected '{key}' value: {value}"),
|
||||
},
|
||||
"avoid-escape" | "avoid_escape" => match value.trim() {
|
||||
"true" => flake8_quotes.avoid_escape = Some(true),
|
||||
"false" => flake8_quotes.avoid_escape = Some(false),
|
||||
_ => eprintln!("Unexpected '{key}' value: {value}"),
|
||||
"avoid-escape" | "avoid_escape" => match parser::parse_bool(value.as_ref()) {
|
||||
Ok(bool) => flake8_quotes.avoid_escape = Some(bool),
|
||||
Err(e) => eprintln!("Unable to parse '{key}' property: {e}"),
|
||||
},
|
||||
// pep8-naming
|
||||
"ignore-names" | "ignore_names" => {
|
||||
@@ -166,6 +215,7 @@ mod tests {
|
||||
per_file_ignores: None,
|
||||
dummy_variable_rgx: None,
|
||||
target_version: None,
|
||||
flake8_annotations: None,
|
||||
flake8_quotes: None,
|
||||
pep8_naming: None,
|
||||
});
|
||||
@@ -195,6 +245,7 @@ mod tests {
|
||||
per_file_ignores: None,
|
||||
dummy_variable_rgx: None,
|
||||
target_version: None,
|
||||
flake8_annotations: None,
|
||||
flake8_quotes: None,
|
||||
pep8_naming: None,
|
||||
});
|
||||
@@ -224,6 +275,7 @@ mod tests {
|
||||
per_file_ignores: None,
|
||||
dummy_variable_rgx: None,
|
||||
target_version: None,
|
||||
flake8_annotations: None,
|
||||
flake8_quotes: None,
|
||||
pep8_naming: None,
|
||||
});
|
||||
@@ -253,6 +305,7 @@ mod tests {
|
||||
per_file_ignores: None,
|
||||
dummy_variable_rgx: None,
|
||||
target_version: None,
|
||||
flake8_annotations: None,
|
||||
flake8_quotes: None,
|
||||
pep8_naming: None,
|
||||
});
|
||||
@@ -282,6 +335,7 @@ mod tests {
|
||||
per_file_ignores: None,
|
||||
dummy_variable_rgx: None,
|
||||
target_version: None,
|
||||
flake8_annotations: None,
|
||||
flake8_quotes: Some(flake8_quotes::settings::Options {
|
||||
inline_quotes: Some(flake8_quotes::settings::Quote::Single),
|
||||
multiline_quotes: None,
|
||||
@@ -354,6 +408,7 @@ mod tests {
|
||||
per_file_ignores: None,
|
||||
dummy_variable_rgx: None,
|
||||
target_version: None,
|
||||
flake8_annotations: None,
|
||||
flake8_quotes: None,
|
||||
pep8_naming: None,
|
||||
});
|
||||
@@ -384,6 +439,7 @@ mod tests {
|
||||
per_file_ignores: None,
|
||||
dummy_variable_rgx: None,
|
||||
target_version: None,
|
||||
flake8_annotations: None,
|
||||
flake8_quotes: Some(flake8_quotes::settings::Options {
|
||||
inline_quotes: Some(flake8_quotes::settings::Quote::Single),
|
||||
multiline_quotes: None,
|
||||
|
||||
@@ -37,6 +37,15 @@ pub fn parse_strings(value: &str) -> Vec<String> {
|
||||
.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)]
|
||||
struct Token {
|
||||
token_name: TokenType,
|
||||
|
||||
@@ -12,6 +12,7 @@ pub enum Plugin {
|
||||
Flake8Docstrings,
|
||||
Flake8Print,
|
||||
Flake8Quotes,
|
||||
Flake8Annotations,
|
||||
PEP8Naming,
|
||||
Pyupgrade,
|
||||
}
|
||||
@@ -27,6 +28,7 @@ impl FromStr for Plugin {
|
||||
"flake8-docstrings" => Ok(Plugin::Flake8Docstrings),
|
||||
"flake8-print" => Ok(Plugin::Flake8Print),
|
||||
"flake8-quotes" => Ok(Plugin::Flake8Quotes),
|
||||
"flake8-annotations" => Ok(Plugin::Flake8Annotations),
|
||||
"pep8-naming" => Ok(Plugin::PEP8Naming),
|
||||
"pyupgrade" => Ok(Plugin::Pyupgrade),
|
||||
_ => Err(anyhow!("Unknown plugin: {}", string)),
|
||||
@@ -35,6 +37,20 @@ impl FromStr for Plugin {
|
||||
}
|
||||
|
||||
impl Plugin {
|
||||
pub fn default(&self) -> CheckCodePrefix {
|
||||
match self {
|
||||
Plugin::Flake8Bugbear => CheckCodePrefix::B,
|
||||
Plugin::Flake8Builtins => CheckCodePrefix::A,
|
||||
Plugin::Flake8Comprehensions => CheckCodePrefix::C,
|
||||
Plugin::Flake8Docstrings => CheckCodePrefix::D,
|
||||
Plugin::Flake8Print => CheckCodePrefix::T,
|
||||
Plugin::Flake8Quotes => CheckCodePrefix::Q,
|
||||
Plugin::Flake8Annotations => CheckCodePrefix::ANN,
|
||||
Plugin::PEP8Naming => CheckCodePrefix::N,
|
||||
Plugin::Pyupgrade => CheckCodePrefix::U,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn select(&self, flake8: &HashMap<String, Option<String>>) -> Vec<CheckCodePrefix> {
|
||||
match self {
|
||||
Plugin::Flake8Bugbear => vec![CheckCodePrefix::B],
|
||||
@@ -58,6 +74,7 @@ impl Plugin {
|
||||
}
|
||||
Plugin::Flake8Print => vec![CheckCodePrefix::T],
|
||||
Plugin::Flake8Quotes => vec![CheckCodePrefix::Q],
|
||||
Plugin::Flake8Annotations => vec![CheckCodePrefix::ANN],
|
||||
Plugin::PEP8Naming => vec![CheckCodePrefix::N],
|
||||
Plugin::Pyupgrade => vec![CheckCodePrefix::U],
|
||||
}
|
||||
@@ -236,8 +253,11 @@ impl DocstringConvention {
|
||||
}
|
||||
}
|
||||
|
||||
/// Infer the enabled plugins based on user-provided settings.
|
||||
pub fn infer_plugins(flake8: &HashMap<String, Option<String>>) -> Vec<Plugin> {
|
||||
/// Infer the enabled plugins based on user-provided options.
|
||||
///
|
||||
/// For example, if the user specified a `mypy-init-return` setting, we should
|
||||
/// infer that `flake8-annotations` is active.
|
||||
pub fn infer_plugins_from_options(flake8: &HashMap<String, Option<String>>) -> Vec<Plugin> {
|
||||
let mut plugins = BTreeSet::new();
|
||||
for key in flake8.keys() {
|
||||
match key.as_str() {
|
||||
@@ -262,6 +282,31 @@ pub fn infer_plugins(flake8: &HashMap<String, Option<String>>) -> Vec<Plugin> {
|
||||
"avoid-escape" | "avoid_escape" => {
|
||||
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
|
||||
"ignore-names" | "ignore_names" => {
|
||||
plugins.insert(Plugin::PEP8Naming);
|
||||
@@ -278,6 +323,38 @@ pub fn infer_plugins(flake8: &HashMap<String, Option<String>>) -> Vec<Plugin> {
|
||||
Vec::from_iter(plugins)
|
||||
}
|
||||
|
||||
/// Infer the enabled plugins based on the referenced prefixes.
|
||||
///
|
||||
/// For example, if the user ignores `ANN101`, we should infer that
|
||||
/// `flake8-annotations` is active.
|
||||
pub fn infer_plugins_from_codes(codes: &BTreeSet<CheckCodePrefix>) -> Vec<Plugin> {
|
||||
[
|
||||
Plugin::Flake8Bugbear,
|
||||
Plugin::Flake8Builtins,
|
||||
Plugin::Flake8Comprehensions,
|
||||
Plugin::Flake8Docstrings,
|
||||
Plugin::Flake8Print,
|
||||
Plugin::Flake8Quotes,
|
||||
Plugin::Flake8Annotations,
|
||||
Plugin::PEP8Naming,
|
||||
Plugin::Pyupgrade,
|
||||
]
|
||||
.into_iter()
|
||||
.filter(|plugin| {
|
||||
for prefix in codes {
|
||||
if prefix
|
||||
.codes()
|
||||
.iter()
|
||||
.any(|code| plugin.default().codes().contains(code))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
false
|
||||
})
|
||||
.collect()
|
||||
}
|
||||
|
||||
/// Resolve the set of enabled `CheckCodePrefix` values for the given plugins.
|
||||
pub fn resolve_select(
|
||||
flake8: &HashMap<String, Option<String>>,
|
||||
@@ -288,9 +365,7 @@ pub fn resolve_select(
|
||||
|
||||
// Add prefix codes for every plugin.
|
||||
for plugin in plugins {
|
||||
for prefix in plugin.select(flake8) {
|
||||
select.insert(prefix);
|
||||
}
|
||||
select.extend(plugin.select(flake8));
|
||||
}
|
||||
|
||||
select
|
||||
@@ -300,18 +375,18 @@ pub fn resolve_select(
|
||||
mod tests {
|
||||
use std::collections::HashMap;
|
||||
|
||||
use crate::plugin::{infer_plugins, Plugin};
|
||||
use crate::plugin::{infer_plugins_from_options, Plugin};
|
||||
|
||||
#[test]
|
||||
fn it_infers_plugins() {
|
||||
let actual = infer_plugins(&HashMap::from([(
|
||||
let actual = infer_plugins_from_options(&HashMap::from([(
|
||||
"inline-quotes".to_string(),
|
||||
Some("single".to_string()),
|
||||
)]));
|
||||
let expected = vec![Plugin::Flake8Quotes];
|
||||
assert_eq!(actual, expected);
|
||||
|
||||
let actual = infer_plugins(&HashMap::from([(
|
||||
let actual = infer_plugins_from_options(&HashMap::from([(
|
||||
"staticmethod-decorators".to_string(),
|
||||
Some("[]".to_string()),
|
||||
)]));
|
||||
|
||||
3
resources/test/fixtures/A001.py
vendored
3
resources/test/fixtures/A001.py
vendored
@@ -1,3 +1,6 @@
|
||||
import os.path
|
||||
import ospath
|
||||
from os import path
|
||||
import some as sum
|
||||
from some import other as int
|
||||
|
||||
|
||||
12
resources/test/fixtures/B004.py
vendored
Normal file
12
resources/test/fixtures/B004.py
vendored
Normal file
@@ -0,0 +1,12 @@
|
||||
def this_is_a_bug():
|
||||
o = object()
|
||||
if hasattr(o, "__call__"):
|
||||
print("Ooh, callable! Or is it?")
|
||||
if getattr(o, "__call__", False):
|
||||
print("Ooh, callable! Or is it?")
|
||||
|
||||
|
||||
def this_is_fine():
|
||||
o = object()
|
||||
if callable(o):
|
||||
print("Ooh, this is actually callable.")
|
||||
26
resources/test/fixtures/B005.py
vendored
Normal file
26
resources/test/fixtures/B005.py
vendored
Normal file
@@ -0,0 +1,26 @@
|
||||
s = "qwe"
|
||||
s.strip(s) # no warning
|
||||
s.strip("we") # no warning
|
||||
s.strip(".facebook.com") # warning
|
||||
s.strip("e") # no warning
|
||||
s.strip("\n\t ") # no warning
|
||||
s.strip(r"\n\t ") # warning
|
||||
s.lstrip(s) # no warning
|
||||
s.lstrip("we") # no warning
|
||||
s.lstrip(".facebook.com") # warning
|
||||
s.lstrip("e") # no warning
|
||||
s.lstrip("\n\t ") # no warning
|
||||
s.lstrip(r"\n\t ") # warning
|
||||
s.rstrip(s) # no warning
|
||||
s.rstrip("we") # warning
|
||||
s.rstrip(".facebook.com") # warning
|
||||
s.rstrip("e") # no warning
|
||||
s.rstrip("\n\t ") # no warning
|
||||
s.rstrip(r"\n\t ") # warning
|
||||
|
||||
from somewhere import other_type, strip
|
||||
|
||||
strip("we") # no warning
|
||||
other_type().lstrip() # no warning
|
||||
other_type().rstrip(["a", "b", "c"]) # no warning
|
||||
other_type().strip("a", "b") # no warning
|
||||
55
resources/test/fixtures/flake8_annotations/annotation_presence.py
vendored
Normal file
55
resources/test/fixtures/flake8_annotations/annotation_presence.py
vendored
Normal file
@@ -0,0 +1,55 @@
|
||||
from typing import Type
|
||||
|
||||
# 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
|
||||
|
||||
|
||||
class Foo:
|
||||
# OK
|
||||
def foo(self: "Foo", a: int, b: int) -> int:
|
||||
pass
|
||||
|
||||
# ANN101
|
||||
def foo(self, a: int, b: int) -> int:
|
||||
pass
|
||||
|
||||
# OK
|
||||
@classmethod
|
||||
def foo(cls: Type["Foo"], a: int, b: int) -> int:
|
||||
pass
|
||||
|
||||
# ANN102
|
||||
@classmethod
|
||||
def foo(cls, a: int, b: int) -> 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
|
||||
@@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "ruff_dev"
|
||||
version = "0.0.104"
|
||||
version = "0.0.105"
|
||||
edition = "2021"
|
||||
|
||||
[dependencies]
|
||||
|
||||
10
rules/no-os.yml
Normal file
10
rules/no-os.yml
Normal file
@@ -0,0 +1,10 @@
|
||||
id: C001
|
||||
message: Don't import os
|
||||
severity: warning
|
||||
language: Python
|
||||
rule:
|
||||
any:
|
||||
- pattern: import os
|
||||
- pattern: import os.$_
|
||||
- pattern: from os import $_
|
||||
- pattern: from os.$_ import $_
|
||||
2
sgconfig.yml
Normal file
2
sgconfig.yml
Normal file
@@ -0,0 +1,2 @@
|
||||
ruleDirs:
|
||||
- rules
|
||||
@@ -100,10 +100,6 @@ pub struct Binding {
|
||||
pub used: Option<(usize, Range)>,
|
||||
}
|
||||
|
||||
pub trait CheckLocator {
|
||||
fn locate_check(&self, default: Range) -> Range;
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord)]
|
||||
pub enum ImportKind {
|
||||
Import,
|
||||
|
||||
592
src/check_ast.rs
592
src/check_ast.rs
File diff suppressed because it is too large
Load Diff
103
src/checks.rs
103
src/checks.rs
@@ -79,6 +79,8 @@ pub enum CheckCode {
|
||||
// flake8-bugbear
|
||||
B002,
|
||||
B003,
|
||||
B004,
|
||||
B005,
|
||||
B006,
|
||||
B007,
|
||||
B008,
|
||||
@@ -115,6 +117,17 @@ pub enum CheckCode {
|
||||
Q001,
|
||||
Q002,
|
||||
Q003,
|
||||
// flake8-annotations
|
||||
ANN001,
|
||||
ANN002,
|
||||
ANN003,
|
||||
ANN101,
|
||||
ANN102,
|
||||
ANN201,
|
||||
ANN202,
|
||||
ANN204,
|
||||
ANN205,
|
||||
ANN206,
|
||||
// pyupgrade
|
||||
U001,
|
||||
U002,
|
||||
@@ -205,6 +218,7 @@ pub enum CheckCategory {
|
||||
Flake8Builtins,
|
||||
Flake8Print,
|
||||
Flake8Quotes,
|
||||
Flake8Annotations,
|
||||
Ruff,
|
||||
Meta,
|
||||
}
|
||||
@@ -219,6 +233,7 @@ impl CheckCategory {
|
||||
CheckCategory::Flake8Comprehensions => "flake8-comprehensions",
|
||||
CheckCategory::Flake8Print => "flake8-print",
|
||||
CheckCategory::Flake8Quotes => "flake8-quotes",
|
||||
CheckCategory::Flake8Annotations => "flake8-annotations",
|
||||
CheckCategory::Pyupgrade => "pyupgrade",
|
||||
CheckCategory::Pydocstyle => "pydocstyle",
|
||||
CheckCategory::PEP8Naming => "pep8-naming",
|
||||
@@ -242,6 +257,9 @@ impl CheckCategory {
|
||||
}
|
||||
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::Flake8Annotations => {
|
||||
Some("https://pypi.org/project/flake8-annotations/2.9.1/")
|
||||
}
|
||||
CheckCategory::Pyupgrade => Some("https://pypi.org/project/pyupgrade/3.2.0/"),
|
||||
CheckCategory::Pydocstyle => Some("https://pypi.org/project/pydocstyle/6.1.1/"),
|
||||
CheckCategory::PEP8Naming => Some("https://pypi.org/project/pep8-naming/0.13.2/"),
|
||||
@@ -321,6 +339,8 @@ pub enum CheckKind {
|
||||
// flake8-bugbear
|
||||
UnaryPrefixIncrement,
|
||||
AssignmentToOsEnviron,
|
||||
UnreliableCallableCheck,
|
||||
StripWithMultiCharacters,
|
||||
MutableArgumentDefault,
|
||||
UnusedLoopControlVariable(String),
|
||||
FunctionCallArgumentDefault,
|
||||
@@ -357,6 +377,17 @@ pub enum CheckKind {
|
||||
BadQuotesMultilineString(Quote),
|
||||
BadQuotesDocstring(Quote),
|
||||
AvoidQuoteEscape,
|
||||
// flake8-annotations
|
||||
MissingTypeFunctionArgument,
|
||||
MissingTypeArgs,
|
||||
MissingTypeKwargs,
|
||||
MissingTypeSelf,
|
||||
MissingTypeCls,
|
||||
MissingReturnTypePublicFunction,
|
||||
MissingReturnTypePrivateFunction,
|
||||
MissingReturnTypeMagicMethod,
|
||||
MissingReturnTypeStaticMethod,
|
||||
MissingReturnTypeClassMethod,
|
||||
// pyupgrade
|
||||
TypeOfPrimitive(Primitive),
|
||||
UnnecessaryAbspath,
|
||||
@@ -514,6 +545,8 @@ impl CheckCode {
|
||||
// flake8-bugbear
|
||||
CheckCode::B002 => CheckKind::UnaryPrefixIncrement,
|
||||
CheckCode::B003 => CheckKind::AssignmentToOsEnviron,
|
||||
CheckCode::B004 => CheckKind::UnreliableCallableCheck,
|
||||
CheckCode::B005 => CheckKind::StripWithMultiCharacters,
|
||||
CheckCode::B006 => CheckKind::MutableArgumentDefault,
|
||||
CheckCode::B007 => CheckKind::UnusedLoopControlVariable("i".to_string()),
|
||||
CheckCode::B008 => CheckKind::FunctionCallArgumentDefault,
|
||||
@@ -565,6 +598,17 @@ impl CheckCode {
|
||||
CheckCode::Q001 => CheckKind::BadQuotesMultilineString(Quote::Double),
|
||||
CheckCode::Q002 => CheckKind::BadQuotesDocstring(Quote::Double),
|
||||
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
|
||||
CheckCode::U001 => CheckKind::UselessMetaclassType,
|
||||
CheckCode::U002 => CheckKind::UnnecessaryAbspath,
|
||||
@@ -714,6 +758,8 @@ impl CheckCode {
|
||||
CheckCode::A003 => CheckCategory::Flake8Builtins,
|
||||
CheckCode::B002 => CheckCategory::Flake8Bugbear,
|
||||
CheckCode::B003 => CheckCategory::Flake8Bugbear,
|
||||
CheckCode::B004 => CheckCategory::Flake8Bugbear,
|
||||
CheckCode::B005 => CheckCategory::Flake8Bugbear,
|
||||
CheckCode::B006 => CheckCategory::Flake8Bugbear,
|
||||
CheckCode::B007 => CheckCategory::Flake8Bugbear,
|
||||
CheckCode::B008 => CheckCategory::Flake8Bugbear,
|
||||
@@ -747,6 +793,16 @@ impl CheckCode {
|
||||
CheckCode::Q001 => CheckCategory::Flake8Quotes,
|
||||
CheckCode::Q002 => 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::U002 => CheckCategory::Pyupgrade,
|
||||
CheckCode::U003 => CheckCategory::Pyupgrade,
|
||||
@@ -879,6 +935,8 @@ impl CheckKind {
|
||||
// flake8-bugbear
|
||||
CheckKind::UnaryPrefixIncrement => &CheckCode::B002,
|
||||
CheckKind::AssignmentToOsEnviron => &CheckCode::B003,
|
||||
CheckKind::UnreliableCallableCheck => &CheckCode::B004,
|
||||
CheckKind::StripWithMultiCharacters => &CheckCode::B005,
|
||||
CheckKind::MutableArgumentDefault => &CheckCode::B006,
|
||||
CheckKind::UnusedLoopControlVariable(_) => &CheckCode::B007,
|
||||
CheckKind::FunctionCallArgumentDefault => &CheckCode::B008,
|
||||
@@ -915,6 +973,17 @@ impl CheckKind {
|
||||
CheckKind::BadQuotesMultilineString(_) => &CheckCode::Q001,
|
||||
CheckKind::BadQuotesDocstring(_) => &CheckCode::Q002,
|
||||
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
|
||||
CheckKind::TypeOfPrimitive(_) => &CheckCode::U003,
|
||||
CheckKind::UnnecessaryAbspath => &CheckCode::U002,
|
||||
@@ -1152,6 +1221,13 @@ impl CheckKind {
|
||||
CheckKind::AssignmentToOsEnviron => {
|
||||
"Assigning to `os.environ` doesn't clear the environment.".to_string()
|
||||
}
|
||||
CheckKind::UnreliableCallableCheck => " Using `hasattr(x, '__call__')` to test if x \
|
||||
is callable is unreliable. Use `callable(x)` \
|
||||
for consistent results."
|
||||
.to_string(),
|
||||
CheckKind::StripWithMultiCharacters => "Using `.strip()` with multi-character strings \
|
||||
is misleading the reader."
|
||||
.to_string(),
|
||||
CheckKind::MutableArgumentDefault => {
|
||||
"Do not use mutable data structures for argument defaults.".to_string()
|
||||
}
|
||||
@@ -1300,6 +1376,33 @@ impl CheckKind {
|
||||
CheckKind::AvoidQuoteEscape => {
|
||||
"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
|
||||
CheckKind::TypeOfPrimitive(primitive) => {
|
||||
format!("Use `{}` instead of `type(...)`", primitive.builtin())
|
||||
|
||||
@@ -13,11 +13,30 @@ pub enum CheckCodePrefix {
|
||||
A001,
|
||||
A002,
|
||||
A003,
|
||||
ANN,
|
||||
ANN0,
|
||||
ANN00,
|
||||
ANN001,
|
||||
ANN002,
|
||||
ANN003,
|
||||
ANN1,
|
||||
ANN10,
|
||||
ANN101,
|
||||
ANN102,
|
||||
ANN2,
|
||||
ANN20,
|
||||
ANN201,
|
||||
ANN202,
|
||||
ANN204,
|
||||
ANN205,
|
||||
ANN206,
|
||||
B,
|
||||
B0,
|
||||
B00,
|
||||
B002,
|
||||
B003,
|
||||
B004,
|
||||
B005,
|
||||
B006,
|
||||
B007,
|
||||
B008,
|
||||
@@ -257,9 +276,51 @@ impl CheckCodePrefix {
|
||||
CheckCodePrefix::A001 => vec![CheckCode::A001],
|
||||
CheckCodePrefix::A002 => vec![CheckCode::A002],
|
||||
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![
|
||||
CheckCode::B002,
|
||||
CheckCode::B003,
|
||||
CheckCode::B004,
|
||||
CheckCode::B005,
|
||||
CheckCode::B006,
|
||||
CheckCode::B007,
|
||||
CheckCode::B008,
|
||||
@@ -275,6 +336,8 @@ impl CheckCodePrefix {
|
||||
CheckCodePrefix::B0 => vec![
|
||||
CheckCode::B002,
|
||||
CheckCode::B003,
|
||||
CheckCode::B004,
|
||||
CheckCode::B005,
|
||||
CheckCode::B006,
|
||||
CheckCode::B007,
|
||||
CheckCode::B008,
|
||||
@@ -290,12 +353,16 @@ impl CheckCodePrefix {
|
||||
CheckCodePrefix::B00 => vec![
|
||||
CheckCode::B002,
|
||||
CheckCode::B003,
|
||||
CheckCode::B004,
|
||||
CheckCode::B005,
|
||||
CheckCode::B006,
|
||||
CheckCode::B007,
|
||||
CheckCode::B008,
|
||||
],
|
||||
CheckCodePrefix::B002 => vec![CheckCode::B002],
|
||||
CheckCodePrefix::B003 => vec![CheckCode::B003],
|
||||
CheckCodePrefix::B004 => vec![CheckCode::B004],
|
||||
CheckCodePrefix::B005 => vec![CheckCode::B005],
|
||||
CheckCodePrefix::B006 => vec![CheckCode::B006],
|
||||
CheckCodePrefix::B007 => vec![CheckCode::B007],
|
||||
CheckCodePrefix::B008 => vec![CheckCode::B008],
|
||||
@@ -931,11 +998,30 @@ impl CheckCodePrefix {
|
||||
CheckCodePrefix::A001 => PrefixSpecificity::Explicit,
|
||||
CheckCodePrefix::A002 => 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::B0 => PrefixSpecificity::Hundreds,
|
||||
CheckCodePrefix::B00 => PrefixSpecificity::Tens,
|
||||
CheckCodePrefix::B002 => PrefixSpecificity::Explicit,
|
||||
CheckCodePrefix::B003 => PrefixSpecificity::Explicit,
|
||||
CheckCodePrefix::B004 => PrefixSpecificity::Explicit,
|
||||
CheckCodePrefix::B005 => PrefixSpecificity::Explicit,
|
||||
CheckCodePrefix::B006 => PrefixSpecificity::Explicit,
|
||||
CheckCodePrefix::B007 => PrefixSpecificity::Explicit,
|
||||
CheckCodePrefix::B008 => PrefixSpecificity::Explicit,
|
||||
|
||||
32
src/cli.rs
32
src/cli.rs
@@ -10,7 +10,7 @@ use crate::checks_gen::CheckCodePrefix;
|
||||
use crate::logging::LogLevel;
|
||||
use crate::printer::SerializationFormat;
|
||||
use crate::settings::configuration::Configuration;
|
||||
use crate::settings::types::{PatternPrefixPair, PythonVersion};
|
||||
use crate::settings::types::{PatternPrefixPair, PerFileIgnore, PythonVersion};
|
||||
|
||||
#[derive(Debug, Parser)]
|
||||
#[command(author, about = "ruff: An extremely fast Python linter.")]
|
||||
@@ -38,8 +38,10 @@ pub struct Cli {
|
||||
#[arg(short, long)]
|
||||
pub watch: bool,
|
||||
/// Attempt to automatically fix lint errors.
|
||||
#[arg(short, long)]
|
||||
pub fix: bool,
|
||||
#[arg(long, overrides_with("no_fix"))]
|
||||
fix: bool,
|
||||
#[clap(long, overrides_with("fix"), hide = true)]
|
||||
no_fix: bool,
|
||||
/// Disable cache reads.
|
||||
#[arg(short, long)]
|
||||
pub no_cache: bool,
|
||||
@@ -94,6 +96,22 @@ pub struct Cli {
|
||||
pub stdin_filename: Option<String>,
|
||||
}
|
||||
|
||||
impl Cli {
|
||||
// See: https://github.com/clap-rs/clap/issues/3146
|
||||
pub fn fix(&self) -> Option<bool> {
|
||||
resolve_bool_arg(self.fix, self.no_fix)
|
||||
}
|
||||
}
|
||||
|
||||
fn resolve_bool_arg(yes: bool, no: bool) -> Option<bool> {
|
||||
match (yes, no) {
|
||||
(true, false) => Some(true),
|
||||
(false, true) => Some(false),
|
||||
(false, false) => None,
|
||||
(..) => unreachable!("Clap should make this impossible"),
|
||||
}
|
||||
}
|
||||
|
||||
/// Map the CLI settings to a `LogLevel`.
|
||||
pub fn extract_log_level(cli: &Cli) -> LogLevel {
|
||||
if cli.silent {
|
||||
@@ -165,10 +183,11 @@ pub fn warn_on(
|
||||
}
|
||||
}
|
||||
|
||||
/// Collect a list of `PatternPrefixPair` structs as a `BTreeMap`.
|
||||
/// Convert a list of `PatternPrefixPair` structs to `PerFileIgnore`.
|
||||
pub fn collect_per_file_ignores(
|
||||
pairs: Vec<PatternPrefixPair>,
|
||||
) -> BTreeMap<String, Vec<CheckCodePrefix>> {
|
||||
project_root: &Option<PathBuf>,
|
||||
) -> Vec<PerFileIgnore> {
|
||||
let mut per_file_ignores: BTreeMap<String, Vec<CheckCodePrefix>> = BTreeMap::new();
|
||||
for pair in pairs {
|
||||
per_file_ignores
|
||||
@@ -177,4 +196,7 @@ pub fn collect_per_file_ignores(
|
||||
.push(pair.prefix);
|
||||
}
|
||||
per_file_ignores
|
||||
.iter()
|
||||
.map(|(pattern, prefixes)| PerFileIgnore::new(pattern, prefixes, project_root))
|
||||
.collect()
|
||||
}
|
||||
|
||||
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(())
|
||||
}
|
||||
}
|
||||
329
src/flake8_annotations/plugins.rs
Normal file
329
src/flake8_annotations/plugins.rs
Normal file
@@ -0,0 +1,329 @@
|
||||
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) {
|
||||
// TODO(charlie): Consider using the AST directly here rather than `Definition`.
|
||||
// We could adhere more closely to `flake8-annotations` by defining public
|
||||
// vs. secret vs. protected.
|
||||
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::ANN102) {
|
||||
checker.add_check(Check::new(
|
||||
CheckKind::MissingTypeCls,
|
||||
Range::from_located(arg),
|
||||
));
|
||||
}
|
||||
} else {
|
||||
if checker.settings.enabled.contains(&CheckCode::ANN101) {
|
||||
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,85 @@
|
||||
---
|
||||
source: src/flake8_annotations/mod.rs
|
||||
expression: checks
|
||||
---
|
||||
- kind: MissingReturnTypePublicFunction
|
||||
location:
|
||||
row: 4
|
||||
column: 0
|
||||
end_location:
|
||||
row: 9
|
||||
column: 0
|
||||
fix: ~
|
||||
- kind: MissingTypeFunctionArgument
|
||||
location:
|
||||
row: 4
|
||||
column: 8
|
||||
end_location:
|
||||
row: 4
|
||||
column: 9
|
||||
fix: ~
|
||||
- kind: MissingTypeFunctionArgument
|
||||
location:
|
||||
row: 4
|
||||
column: 11
|
||||
end_location:
|
||||
row: 4
|
||||
column: 12
|
||||
fix: ~
|
||||
- kind: MissingReturnTypePublicFunction
|
||||
location:
|
||||
row: 9
|
||||
column: 0
|
||||
end_location:
|
||||
row: 14
|
||||
column: 0
|
||||
fix: ~
|
||||
- kind: MissingTypeFunctionArgument
|
||||
location:
|
||||
row: 9
|
||||
column: 16
|
||||
end_location:
|
||||
row: 9
|
||||
column: 17
|
||||
fix: ~
|
||||
- kind: MissingTypeFunctionArgument
|
||||
location:
|
||||
row: 14
|
||||
column: 16
|
||||
end_location:
|
||||
row: 14
|
||||
column: 17
|
||||
fix: ~
|
||||
- kind: MissingReturnTypePublicFunction
|
||||
location:
|
||||
row: 19
|
||||
column: 0
|
||||
end_location:
|
||||
row: 24
|
||||
column: 0
|
||||
fix: ~
|
||||
- kind: MissingReturnTypePublicFunction
|
||||
location:
|
||||
row: 24
|
||||
column: 0
|
||||
end_location:
|
||||
row: 29
|
||||
column: 0
|
||||
fix: ~
|
||||
- kind: MissingTypeSelf
|
||||
location:
|
||||
row: 44
|
||||
column: 12
|
||||
end_location:
|
||||
row: 44
|
||||
column: 16
|
||||
fix: ~
|
||||
- kind: MissingTypeCls
|
||||
location:
|
||||
row: 54
|
||||
column: 12
|
||||
end_location:
|
||||
row: 54
|
||||
column: 15
|
||||
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: ~
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
use rustpython_ast::{Constant, Expr, ExprContext, ExprKind, Stmt, StmtKind};
|
||||
|
||||
use crate::ast::types::{CheckLocator, Range};
|
||||
use crate::ast::types::Range;
|
||||
use crate::autofix::Fix;
|
||||
use crate::check_ast::Checker;
|
||||
use crate::checks::{Check, CheckKind};
|
||||
@@ -42,10 +42,7 @@ pub fn assert_false(checker: &mut Checker, stmt: &Stmt, test: &Expr, msg: &Optio
|
||||
..
|
||||
} = &test.node
|
||||
{
|
||||
let mut check = Check::new(
|
||||
CheckKind::DoNotAssertFalse,
|
||||
checker.locate_check(Range::from_located(test)),
|
||||
);
|
||||
let mut check = Check::new(CheckKind::DoNotAssertFalse, Range::from_located(test));
|
||||
if checker.patch() {
|
||||
let mut generator = SourceGenerator::new();
|
||||
if let Ok(()) = generator.unparse_stmt(&assertion_error(msg)) {
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
use rustpython_ast::{ExprKind, Stmt, Withitem};
|
||||
|
||||
use crate::ast::helpers::match_name_or_attr;
|
||||
use crate::ast::types::{CheckLocator, Range};
|
||||
use crate::ast::types::Range;
|
||||
use crate::check_ast::Checker;
|
||||
use crate::checks::{Check, CheckKind};
|
||||
|
||||
@@ -17,7 +17,7 @@ pub fn assert_raises_exception(checker: &mut Checker, stmt: &Stmt, items: &[With
|
||||
{
|
||||
checker.add_check(Check::new(
|
||||
CheckKind::NoAssertRaisesException,
|
||||
checker.locate_check(Range::from_located(stmt)),
|
||||
Range::from_located(stmt),
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
use rustpython_ast::{Expr, ExprKind};
|
||||
|
||||
use crate::ast::types::{CheckLocator, Range};
|
||||
use crate::ast::types::Range;
|
||||
use crate::check_ast::Checker;
|
||||
use crate::checks::{Check, CheckKind};
|
||||
|
||||
@@ -14,7 +14,7 @@ pub fn assignment_to_os_environ(checker: &mut Checker, targets: &[Expr]) {
|
||||
if id == "os" {
|
||||
checker.add_check(Check::new(
|
||||
CheckKind::AssignmentToOsEnviron,
|
||||
checker.locate_check(Range::from_located(target)),
|
||||
Range::from_located(target),
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
use rustpython_ast::{Expr, ExprKind};
|
||||
|
||||
use crate::ast::types::{CheckLocator, Range};
|
||||
use crate::ast::types::Range;
|
||||
use crate::check_ast::Checker;
|
||||
use crate::checks::{Check, CheckKind};
|
||||
|
||||
@@ -9,7 +9,7 @@ pub fn cannot_raise_literal(checker: &mut Checker, expr: &Expr) {
|
||||
if let ExprKind::Constant { .. } = &expr.node {
|
||||
checker.add_check(Check::new(
|
||||
CheckKind::CannotRaiseLiteral,
|
||||
checker.locate_check(Range::from_located(expr)),
|
||||
Range::from_located(expr),
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,7 +4,7 @@ use itertools::Itertools;
|
||||
use rustpython_ast::{Excepthandler, ExcepthandlerKind, Expr, ExprContext, ExprKind, Stmt};
|
||||
|
||||
use crate::ast::helpers;
|
||||
use crate::ast::types::{CheckLocator, Range};
|
||||
use crate::ast::types::Range;
|
||||
use crate::autofix::Fix;
|
||||
use crate::check_ast::Checker;
|
||||
use crate::checks::{Check, CheckCode, CheckKind};
|
||||
@@ -47,7 +47,7 @@ pub fn duplicate_handler_exceptions(
|
||||
CheckKind::DuplicateHandlerException(
|
||||
duplicates.into_iter().sorted().collect::<Vec<String>>(),
|
||||
),
|
||||
checker.locate_check(Range::from_located(expr)),
|
||||
Range::from_located(expr),
|
||||
);
|
||||
if checker.patch() {
|
||||
// TODO(charlie): If we have a single element, remove the tuple.
|
||||
@@ -106,7 +106,7 @@ pub fn duplicate_exceptions(checker: &mut Checker, stmt: &Stmt, handlers: &[Exce
|
||||
for duplicate in duplicates.into_iter().sorted() {
|
||||
checker.add_check(Check::new(
|
||||
CheckKind::DuplicateTryBlockException(duplicate),
|
||||
checker.locate_check(Range::from_located(stmt)),
|
||||
Range::from_located(stmt),
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
use rustpython_ast::{Arguments, Constant, Expr, ExprKind};
|
||||
|
||||
use crate::ast::helpers::compose_call_path;
|
||||
use crate::ast::types::{CheckLocator, Range};
|
||||
use crate::ast::types::Range;
|
||||
use crate::ast::visitor;
|
||||
use crate::ast::visitor::Visitor;
|
||||
use crate::check_ast::Checker;
|
||||
@@ -91,6 +91,6 @@ pub fn function_call_argument_default(checker: &mut Checker, arguments: &Argumen
|
||||
visitor.visit_expr(expr);
|
||||
}
|
||||
for (check, range) in visitor.checks {
|
||||
checker.add_check(Check::new(check, checker.locate_check(range)));
|
||||
checker.add_check(Check::new(check, range));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6,7 +6,9 @@ pub use duplicate_exceptions::{duplicate_exceptions, duplicate_handler_exception
|
||||
pub use function_call_argument_default::function_call_argument_default;
|
||||
pub use mutable_argument_default::mutable_argument_default;
|
||||
pub use redundant_tuple_in_exception_handler::redundant_tuple_in_exception_handler;
|
||||
pub use strip_with_multi_characters::strip_with_multi_characters;
|
||||
pub use unary_prefix_increment::unary_prefix_increment;
|
||||
pub use unreliable_callable_check::unreliable_callable_check;
|
||||
pub use unused_loop_control_variable::unused_loop_control_variable;
|
||||
pub use useless_comparison::useless_comparison;
|
||||
pub use useless_expression::useless_expression;
|
||||
@@ -19,7 +21,9 @@ mod duplicate_exceptions;
|
||||
mod function_call_argument_default;
|
||||
mod mutable_argument_default;
|
||||
mod redundant_tuple_in_exception_handler;
|
||||
mod strip_with_multi_characters;
|
||||
mod unary_prefix_increment;
|
||||
mod unreliable_callable_check;
|
||||
mod unused_loop_control_variable;
|
||||
mod useless_comparison;
|
||||
mod useless_expression;
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
use rustpython_ast::{Arguments, Expr, ExprKind};
|
||||
|
||||
use crate::ast::types::{CheckLocator, Range};
|
||||
use crate::ast::types::Range;
|
||||
use crate::check_ast::Checker;
|
||||
use crate::checks::{Check, CheckKind};
|
||||
|
||||
@@ -45,14 +45,14 @@ pub fn mutable_argument_default(checker: &mut Checker, arguments: &Arguments) {
|
||||
| ExprKind::SetComp { .. } => {
|
||||
checker.add_check(Check::new(
|
||||
CheckKind::MutableArgumentDefault,
|
||||
checker.locate_check(Range::from_located(expr)),
|
||||
Range::from_located(expr),
|
||||
));
|
||||
}
|
||||
ExprKind::Call { func, .. } => {
|
||||
if is_mutable_func(func) {
|
||||
checker.add_check(Check::new(
|
||||
CheckKind::MutableArgumentDefault,
|
||||
checker.locate_check(Range::from_located(expr)),
|
||||
Range::from_located(expr),
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
use rustpython_ast::{Excepthandler, ExcepthandlerKind, ExprKind};
|
||||
|
||||
use crate::ast::types::{CheckLocator, Range};
|
||||
use crate::ast::types::Range;
|
||||
use crate::check_ast::Checker;
|
||||
use crate::checks::{Check, CheckKind};
|
||||
|
||||
@@ -13,7 +13,7 @@ pub fn redundant_tuple_in_exception_handler(checker: &mut Checker, handlers: &[E
|
||||
if elts.len() == 1 {
|
||||
checker.add_check(Check::new(
|
||||
CheckKind::RedundantTupleInExceptionHandler(elts[0].to_string()),
|
||||
checker.locate_check(Range::from_located(type_)),
|
||||
Range::from_located(type_),
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
28
src/flake8_bugbear/plugins/strip_with_multi_characters.rs
Normal file
28
src/flake8_bugbear/plugins/strip_with_multi_characters.rs
Normal file
@@ -0,0 +1,28 @@
|
||||
use itertools::Itertools;
|
||||
use rustpython_ast::{Constant, Expr, ExprKind};
|
||||
|
||||
use crate::ast::types::Range;
|
||||
use crate::check_ast::Checker;
|
||||
use crate::checks::{Check, CheckKind};
|
||||
|
||||
/// B005
|
||||
pub fn strip_with_multi_characters(checker: &mut Checker, expr: &Expr, func: &Expr, args: &[Expr]) {
|
||||
if let ExprKind::Attribute { attr, .. } = &func.node {
|
||||
if attr == "strip" || attr == "lstrip" || attr == "rstrip" {
|
||||
if args.len() == 1 {
|
||||
if let ExprKind::Constant {
|
||||
value: Constant::Str(value),
|
||||
..
|
||||
} = &args[0].node
|
||||
{
|
||||
if value.len() > 1 && value.chars().unique().count() != value.len() {
|
||||
checker.add_check(Check::new(
|
||||
CheckKind::StripWithMultiCharacters,
|
||||
Range::from_located(expr),
|
||||
));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,6 +1,6 @@
|
||||
use rustpython_ast::{Expr, ExprKind, Unaryop};
|
||||
|
||||
use crate::ast::types::{CheckLocator, Range};
|
||||
use crate::ast::types::Range;
|
||||
use crate::check_ast::Checker;
|
||||
use crate::checks::{Check, CheckKind};
|
||||
|
||||
@@ -11,7 +11,7 @@ pub fn unary_prefix_increment(checker: &mut Checker, expr: &Expr, op: &Unaryop,
|
||||
if matches!(op, Unaryop::UAdd) {
|
||||
checker.add_check(Check::new(
|
||||
CheckKind::UnaryPrefixIncrement,
|
||||
checker.locate_check(Range::from_located(expr)),
|
||||
Range::from_located(expr),
|
||||
))
|
||||
}
|
||||
}
|
||||
|
||||
27
src/flake8_bugbear/plugins/unreliable_callable_check.rs
Normal file
27
src/flake8_bugbear/plugins/unreliable_callable_check.rs
Normal file
@@ -0,0 +1,27 @@
|
||||
use rustpython_ast::{Constant, Expr, ExprKind};
|
||||
|
||||
use crate::ast::types::Range;
|
||||
use crate::check_ast::Checker;
|
||||
use crate::checks::{Check, CheckKind};
|
||||
|
||||
/// B004
|
||||
pub fn unreliable_callable_check(checker: &mut Checker, expr: &Expr, func: &Expr, args: &[Expr]) {
|
||||
if let ExprKind::Name { id, .. } = &func.node {
|
||||
if id == "getattr" || id == "hasattr" {
|
||||
if args.len() >= 2 {
|
||||
if let ExprKind::Constant {
|
||||
value: Constant::Str(s),
|
||||
..
|
||||
} = &args[1].node
|
||||
{
|
||||
if s == "__call__" {
|
||||
checker.add_check(Check::new(
|
||||
CheckKind::UnreliableCallableCheck,
|
||||
Range::from_located(expr),
|
||||
));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -2,7 +2,7 @@ use std::collections::BTreeMap;
|
||||
|
||||
use rustpython_ast::{Expr, ExprKind, Stmt};
|
||||
|
||||
use crate::ast::types::{CheckLocator, Range};
|
||||
use crate::ast::types::Range;
|
||||
use crate::ast::visitor;
|
||||
use crate::ast::visitor::Visitor;
|
||||
use crate::autofix::Fix;
|
||||
@@ -64,7 +64,7 @@ pub fn unused_loop_control_variable(checker: &mut Checker, target: &Expr, body:
|
||||
|
||||
let mut check = Check::new(
|
||||
CheckKind::UnusedLoopControlVariable(name.to_string()),
|
||||
checker.locate_check(Range::from_located(expr)),
|
||||
Range::from_located(expr),
|
||||
);
|
||||
if checker.patch() {
|
||||
// Prefix the variable name with an underscore.
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
use rustpython_ast::{Expr, ExprKind};
|
||||
|
||||
use crate::ast::types::{CheckLocator, Range};
|
||||
use crate::ast::types::Range;
|
||||
use crate::check_ast::Checker;
|
||||
use crate::checks::{Check, CheckKind};
|
||||
|
||||
@@ -8,7 +8,7 @@ pub fn useless_comparison(checker: &mut Checker, expr: &Expr) {
|
||||
if let ExprKind::Compare { left, .. } = &expr.node {
|
||||
checker.add_check(Check::new(
|
||||
CheckKind::UselessComparison,
|
||||
checker.locate_check(Range::from_located(left)),
|
||||
Range::from_located(left),
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
use rustpython_ast::{Constant, ExprKind, Stmt, StmtKind};
|
||||
|
||||
use crate::ast::types::{CheckLocator, Range};
|
||||
use crate::ast::types::Range;
|
||||
use crate::check_ast::Checker;
|
||||
use crate::checks::{Check, CheckKind};
|
||||
|
||||
@@ -12,7 +12,7 @@ pub fn useless_expression(checker: &mut Checker, body: &[Stmt]) {
|
||||
ExprKind::List { .. } | ExprKind::Dict { .. } | ExprKind::Set { .. } => {
|
||||
checker.add_check(Check::new(
|
||||
CheckKind::UselessExpression,
|
||||
checker.locate_check(Range::from_located(value)),
|
||||
Range::from_located(value),
|
||||
));
|
||||
}
|
||||
ExprKind::Constant { value: val, .. } => match &val {
|
||||
@@ -20,7 +20,7 @@ pub fn useless_expression(checker: &mut Checker, body: &[Stmt]) {
|
||||
_ => {
|
||||
checker.add_check(Check::new(
|
||||
CheckKind::UselessExpression,
|
||||
checker.locate_check(Range::from_located(value)),
|
||||
Range::from_located(value),
|
||||
));
|
||||
}
|
||||
},
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
use log::error;
|
||||
use rustpython_ast::{Expr, Stmt, StmtKind};
|
||||
|
||||
use crate::ast::types::{CheckLocator, Range};
|
||||
use crate::ast::types::Range;
|
||||
use crate::autofix::helpers;
|
||||
use crate::check_ast::Checker;
|
||||
use crate::checks::CheckCode;
|
||||
@@ -13,7 +13,7 @@ pub fn print_call(checker: &mut Checker, expr: &Expr, func: &Expr) {
|
||||
func,
|
||||
checker.settings.enabled.contains(&CheckCode::T201),
|
||||
checker.settings.enabled.contains(&CheckCode::T203),
|
||||
checker.locate_check(Range::from_located(expr)),
|
||||
Range::from_located(expr),
|
||||
) {
|
||||
if checker.patch() {
|
||||
let context = checker.binding_context();
|
||||
|
||||
@@ -135,172 +135,3 @@ pub fn quotes(
|
||||
|
||||
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 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;
|
||||
mod cst;
|
||||
mod docstrings;
|
||||
pub mod flake8_annotations;
|
||||
mod flake8_bugbear;
|
||||
mod flake8_builtins;
|
||||
mod flake8_comprehensions;
|
||||
|
||||
@@ -294,6 +294,8 @@ mod tests {
|
||||
#[test_case(CheckCode::A003, Path::new("A003.py"); "A003")]
|
||||
#[test_case(CheckCode::B002, Path::new("B002.py"); "B002")]
|
||||
#[test_case(CheckCode::B003, Path::new("B003.py"); "B003")]
|
||||
#[test_case(CheckCode::B004, Path::new("B004.py"); "B004")]
|
||||
#[test_case(CheckCode::B005, Path::new("B005.py"); "B005")]
|
||||
#[test_case(CheckCode::B006, Path::new("B006_B008.py"); "B006")]
|
||||
#[test_case(CheckCode::B007, Path::new("B007.py"); "B007")]
|
||||
#[test_case(CheckCode::B008, Path::new("B006_B008.py"); "B008")]
|
||||
|
||||
26
src/main.rs
26
src/main.rs
@@ -1,4 +1,3 @@
|
||||
use std::collections::BTreeMap;
|
||||
use std::io::{self, Read};
|
||||
use std::path::{Path, PathBuf};
|
||||
use std::process::ExitCode;
|
||||
@@ -8,7 +7,6 @@ use std::time::Instant;
|
||||
#[cfg(not(target_family = "wasm"))]
|
||||
use ::ruff::cache;
|
||||
use ::ruff::checks::{CheckCode, CheckKind};
|
||||
use ::ruff::checks_gen::CheckCodePrefix;
|
||||
use ::ruff::cli::{collect_per_file_ignores, extract_log_level, warn_on, Cli, Warnable};
|
||||
use ::ruff::fs::iter_python_files;
|
||||
use ::ruff::linter::{add_noqa_to_path, autoformat_path, lint_path, lint_stdin};
|
||||
@@ -222,7 +220,9 @@ fn autoformat(files: &[PathBuf], settings: &Settings) -> Result<usize> {
|
||||
}
|
||||
|
||||
fn inner_main() -> Result<ExitCode> {
|
||||
// Extract command-line arguments.
|
||||
let cli = Cli::parse();
|
||||
let fix = cli.fix();
|
||||
|
||||
let log_level = extract_log_level(&cli);
|
||||
set_up_logging(&log_level)?;
|
||||
@@ -241,7 +241,7 @@ fn inner_main() -> Result<ExitCode> {
|
||||
None => debug!("Unable to find pyproject.toml; using default settings..."),
|
||||
};
|
||||
|
||||
// Parse the settings from the pyproject.toml and command-line arguments.
|
||||
// Reconcile configuration from pyproject.toml and command-line arguments.
|
||||
let exclude: Vec<FilePattern> = cli
|
||||
.exclude
|
||||
.iter()
|
||||
@@ -252,8 +252,6 @@ fn inner_main() -> Result<ExitCode> {
|
||||
.iter()
|
||||
.map(|path| FilePattern::from_user(path, &project_root))
|
||||
.collect();
|
||||
let per_file_ignores: BTreeMap<String, Vec<CheckCodePrefix>> =
|
||||
collect_per_file_ignores(cli.per_file_ignores);
|
||||
|
||||
let mut configuration = Configuration::from_pyproject(&pyproject, &project_root)?;
|
||||
if !exclude.is_empty() {
|
||||
@@ -262,8 +260,9 @@ fn inner_main() -> Result<ExitCode> {
|
||||
if !extend_exclude.is_empty() {
|
||||
configuration.extend_exclude = extend_exclude;
|
||||
}
|
||||
if !per_file_ignores.is_empty() {
|
||||
configuration.per_file_ignores = per_file_ignores;
|
||||
if !cli.per_file_ignores.is_empty() {
|
||||
configuration.per_file_ignores =
|
||||
collect_per_file_ignores(cli.per_file_ignores, &project_root);
|
||||
}
|
||||
if !cli.select.is_empty() {
|
||||
warn_on(
|
||||
@@ -299,6 +298,9 @@ fn inner_main() -> Result<ExitCode> {
|
||||
if let Some(dummy_variable_rgx) = cli.dummy_variable_rgx {
|
||||
configuration.dummy_variable_rgx = dummy_variable_rgx;
|
||||
}
|
||||
if let Some(fix) = fix {
|
||||
configuration.fix = fix;
|
||||
}
|
||||
|
||||
if cli.show_settings && cli.show_files {
|
||||
eprintln!("Error: specify --show-settings or show-files (not both).");
|
||||
@@ -309,6 +311,8 @@ fn inner_main() -> Result<ExitCode> {
|
||||
return Ok(ExitCode::SUCCESS);
|
||||
}
|
||||
|
||||
// Extract settings for internal use.
|
||||
let autofix = configuration.fix;
|
||||
let settings = Settings::from_configuration(configuration);
|
||||
|
||||
if cli.show_files {
|
||||
@@ -321,7 +325,7 @@ fn inner_main() -> Result<ExitCode> {
|
||||
|
||||
let printer = Printer::new(&cli.format, &log_level);
|
||||
if cli.watch {
|
||||
if cli.fix {
|
||||
if autofix {
|
||||
eprintln!("Warning: --fix is not enabled in watch mode.");
|
||||
}
|
||||
|
||||
@@ -384,15 +388,15 @@ fn inner_main() -> Result<ExitCode> {
|
||||
let messages = if is_stdin {
|
||||
let filename = cli.stdin_filename.unwrap_or_else(|| "-".to_string());
|
||||
let path = Path::new(&filename);
|
||||
run_once_stdin(&settings, path, cli.fix)?
|
||||
run_once_stdin(&settings, path, autofix)?
|
||||
} else {
|
||||
run_once(&cli.files, &settings, !cli.no_cache, cli.fix)?
|
||||
run_once(&cli.files, &settings, !cli.no_cache, autofix)?
|
||||
};
|
||||
|
||||
// Always try to print violations (the printer itself may suppress output),
|
||||
// unless we're writing fixes via stdin (in which case, the transformed
|
||||
// source code goes to stdout).
|
||||
if !(is_stdin && cli.fix) {
|
||||
if !(is_stdin && autofix) {
|
||||
printer.write_once(&messages)?;
|
||||
}
|
||||
|
||||
|
||||
@@ -2,7 +2,7 @@ use itertools::izip;
|
||||
use rustpython_ast::Location;
|
||||
use rustpython_parser::ast::{Cmpop, Constant, Expr, ExprKind, Unaryop};
|
||||
|
||||
use crate::ast::types::{CheckLocator, Range};
|
||||
use crate::ast::types::Range;
|
||||
use crate::checks::{Check, CheckKind, RejectedCmpop};
|
||||
use crate::source_code_locator::SourceCodeLocator;
|
||||
|
||||
@@ -61,7 +61,6 @@ pub fn not_tests(
|
||||
operand: &Expr,
|
||||
check_not_in: bool,
|
||||
check_not_is: bool,
|
||||
locator: &dyn CheckLocator,
|
||||
) -> Vec<Check> {
|
||||
let mut checks: Vec<Check> = vec![];
|
||||
|
||||
@@ -73,7 +72,7 @@ pub fn not_tests(
|
||||
if check_not_in {
|
||||
checks.push(Check::new(
|
||||
CheckKind::NotInTest,
|
||||
locator.locate_check(Range::from_located(operand)),
|
||||
Range::from_located(operand),
|
||||
));
|
||||
}
|
||||
}
|
||||
@@ -81,7 +80,7 @@ pub fn not_tests(
|
||||
if check_not_is {
|
||||
checks.push(Check::new(
|
||||
CheckKind::NotIsTest,
|
||||
locator.locate_check(Range::from_located(operand)),
|
||||
Range::from_located(operand),
|
||||
));
|
||||
}
|
||||
}
|
||||
@@ -101,7 +100,6 @@ pub fn literal_comparisons(
|
||||
comparators: &[Expr],
|
||||
check_none_comparisons: bool,
|
||||
check_true_false_comparisons: bool,
|
||||
locator: &dyn CheckLocator,
|
||||
) -> Vec<Check> {
|
||||
let mut checks: Vec<Check> = vec![];
|
||||
|
||||
@@ -121,13 +119,13 @@ pub fn literal_comparisons(
|
||||
if matches!(op, Cmpop::Eq) {
|
||||
checks.push(Check::new(
|
||||
CheckKind::NoneComparison(RejectedCmpop::Eq),
|
||||
locator.locate_check(Range::from_located(comparator)),
|
||||
Range::from_located(comparator),
|
||||
));
|
||||
}
|
||||
if matches!(op, Cmpop::NotEq) {
|
||||
checks.push(Check::new(
|
||||
CheckKind::NoneComparison(RejectedCmpop::NotEq),
|
||||
locator.locate_check(Range::from_located(comparator)),
|
||||
Range::from_located(comparator),
|
||||
));
|
||||
}
|
||||
}
|
||||
@@ -141,13 +139,13 @@ pub fn literal_comparisons(
|
||||
if matches!(op, Cmpop::Eq) {
|
||||
checks.push(Check::new(
|
||||
CheckKind::TrueFalseComparison(value, RejectedCmpop::Eq),
|
||||
locator.locate_check(Range::from_located(comparator)),
|
||||
Range::from_located(comparator),
|
||||
));
|
||||
}
|
||||
if matches!(op, Cmpop::NotEq) {
|
||||
checks.push(Check::new(
|
||||
CheckKind::TrueFalseComparison(value, RejectedCmpop::NotEq),
|
||||
locator.locate_check(Range::from_located(comparator)),
|
||||
Range::from_located(comparator),
|
||||
));
|
||||
}
|
||||
}
|
||||
@@ -167,13 +165,13 @@ pub fn literal_comparisons(
|
||||
if matches!(op, Cmpop::Eq) {
|
||||
checks.push(Check::new(
|
||||
CheckKind::NoneComparison(RejectedCmpop::Eq),
|
||||
locator.locate_check(Range::from_located(comparator)),
|
||||
Range::from_located(comparator),
|
||||
));
|
||||
}
|
||||
if matches!(op, Cmpop::NotEq) {
|
||||
checks.push(Check::new(
|
||||
CheckKind::NoneComparison(RejectedCmpop::NotEq),
|
||||
locator.locate_check(Range::from_located(comparator)),
|
||||
Range::from_located(comparator),
|
||||
));
|
||||
}
|
||||
}
|
||||
@@ -187,13 +185,13 @@ pub fn literal_comparisons(
|
||||
if matches!(op, Cmpop::Eq) {
|
||||
checks.push(Check::new(
|
||||
CheckKind::TrueFalseComparison(value, RejectedCmpop::Eq),
|
||||
locator.locate_check(Range::from_located(comparator)),
|
||||
Range::from_located(comparator),
|
||||
));
|
||||
}
|
||||
if matches!(op, Cmpop::NotEq) {
|
||||
checks.push(Check::new(
|
||||
CheckKind::TrueFalseComparison(value, RejectedCmpop::NotEq),
|
||||
locator.locate_check(Range::from_located(comparator)),
|
||||
Range::from_located(comparator),
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -13,7 +13,7 @@ use crate::docstrings::definition::{Definition, DefinitionKind};
|
||||
use crate::docstrings::helpers;
|
||||
use crate::docstrings::sections::{section_contexts, SectionContext};
|
||||
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
|
||||
pub fn not_missing(
|
||||
@@ -1308,7 +1308,8 @@ fn missing_args(checker: &mut Checker, definition: &Definition, docstrings_args:
|
||||
.skip(
|
||||
// If this is a non-static method, skip `cls` or `self`.
|
||||
usize::from(
|
||||
matches!(definition.kind, DefinitionKind::Method(_)) && !is_static(parent),
|
||||
matches!(definition.kind, DefinitionKind::Method(_))
|
||||
&& !is_staticmethod(parent),
|
||||
),
|
||||
)
|
||||
.collect();
|
||||
|
||||
@@ -5,7 +5,7 @@ use rustpython_parser::ast::{
|
||||
Arg, Arguments, Constant, Excepthandler, ExcepthandlerKind, Expr, ExprKind, Stmt, StmtKind,
|
||||
};
|
||||
|
||||
use crate::ast::types::{BindingKind, CheckLocator, FunctionScope, Range, Scope, ScopeKind};
|
||||
use crate::ast::types::{BindingKind, FunctionScope, Range, Scope, ScopeKind};
|
||||
use crate::checks::{Check, CheckKind};
|
||||
|
||||
/// F631
|
||||
@@ -28,12 +28,30 @@ pub fn if_tuple(test: &Expr, location: Range) -> Option<Check> {
|
||||
None
|
||||
}
|
||||
|
||||
/// F821
|
||||
pub fn undefined_local(scopes: &[&Scope], name: &str) -> Option<Check> {
|
||||
let current = &scopes.last().expect("No current scope found.");
|
||||
if matches!(current.kind, ScopeKind::Function(_)) && !current.values.contains_key(name) {
|
||||
for scope in scopes.iter().rev().skip(1) {
|
||||
if matches!(scope.kind, ScopeKind::Function(_) | ScopeKind::Module) {
|
||||
if let Some(binding) = scope.values.get(name) {
|
||||
if let Some((scope_id, location)) = binding.used {
|
||||
if scope_id == current.id {
|
||||
return Some(Check::new(
|
||||
CheckKind::UndefinedLocal(name.to_string()),
|
||||
location,
|
||||
));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
None
|
||||
}
|
||||
|
||||
/// F841
|
||||
pub fn unused_variables(
|
||||
scope: &Scope,
|
||||
locator: &dyn CheckLocator,
|
||||
dummy_variable_rgx: &Regex,
|
||||
) -> Vec<Check> {
|
||||
pub fn unused_variables(scope: &Scope, dummy_variable_rgx: &Regex) -> Vec<Check> {
|
||||
let mut checks: Vec<Check> = vec![];
|
||||
|
||||
if matches!(
|
||||
@@ -53,7 +71,7 @@ pub fn unused_variables(
|
||||
{
|
||||
checks.push(Check::new(
|
||||
CheckKind::UnusedVariable(name.to_string()),
|
||||
locator.locate_check(binding.range),
|
||||
binding.range,
|
||||
));
|
||||
}
|
||||
}
|
||||
@@ -129,7 +147,6 @@ pub fn repeated_keys(
|
||||
keys: &[Expr],
|
||||
check_repeated_literals: bool,
|
||||
check_repeated_variables: bool,
|
||||
locator: &dyn CheckLocator,
|
||||
) -> Vec<Check> {
|
||||
let mut checks: Vec<Check> = vec![];
|
||||
|
||||
@@ -144,7 +161,7 @@ pub fn repeated_keys(
|
||||
if check_repeated_literals && v1 == v2 {
|
||||
checks.push(Check::new(
|
||||
CheckKind::MultiValueRepeatedKeyLiteral,
|
||||
locator.locate_check(Range::from_located(k2)),
|
||||
Range::from_located(k2),
|
||||
))
|
||||
}
|
||||
}
|
||||
@@ -152,7 +169,7 @@ pub fn repeated_keys(
|
||||
if check_repeated_variables && v1 == v2 {
|
||||
checks.push(Check::new(
|
||||
CheckKind::MultiValueRepeatedKeyVariable((*v2).to_string()),
|
||||
locator.locate_check(Range::from_located(k2)),
|
||||
Range::from_located(k2),
|
||||
))
|
||||
}
|
||||
}
|
||||
@@ -195,12 +212,7 @@ pub fn starred_expressions(
|
||||
}
|
||||
|
||||
/// F701
|
||||
pub fn break_outside_loop(
|
||||
stmt: &Stmt,
|
||||
parents: &[&Stmt],
|
||||
parent_stack: &[usize],
|
||||
locator: &dyn CheckLocator,
|
||||
) -> Option<Check> {
|
||||
pub fn break_outside_loop(stmt: &Stmt, parents: &[&Stmt], parent_stack: &[usize]) -> Option<Check> {
|
||||
let mut allowed: bool = false;
|
||||
let mut parent = stmt;
|
||||
for index in parent_stack.iter().rev() {
|
||||
@@ -228,7 +240,7 @@ pub fn break_outside_loop(
|
||||
if !allowed {
|
||||
Some(Check::new(
|
||||
CheckKind::BreakOutsideLoop,
|
||||
locator.locate_check(Range::from_located(stmt)),
|
||||
Range::from_located(stmt),
|
||||
))
|
||||
} else {
|
||||
None
|
||||
@@ -240,7 +252,6 @@ pub fn continue_outside_loop(
|
||||
stmt: &Stmt,
|
||||
parents: &[&Stmt],
|
||||
parent_stack: &[usize],
|
||||
locator: &dyn CheckLocator,
|
||||
) -> Option<Check> {
|
||||
let mut allowed: bool = false;
|
||||
let mut parent = stmt;
|
||||
@@ -269,7 +280,7 @@ pub fn continue_outside_loop(
|
||||
if !allowed {
|
||||
Some(Check::new(
|
||||
CheckKind::ContinueOutsideLoop,
|
||||
locator.locate_check(Range::from_located(stmt)),
|
||||
Range::from_located(stmt),
|
||||
))
|
||||
} else {
|
||||
None
|
||||
|
||||
@@ -1,13 +1,12 @@
|
||||
use rustpython_ast::{Expr, Stmt};
|
||||
|
||||
use crate::ast::types::{CheckLocator, Range};
|
||||
use crate::ast::types::Range;
|
||||
use crate::check_ast::Checker;
|
||||
use crate::pyflakes::checks;
|
||||
|
||||
/// F631
|
||||
pub fn assert_tuple(checker: &mut Checker, stmt: &Stmt, test: &Expr) {
|
||||
if let Some(check) = checks::assert_tuple(test, checker.locate_check(Range::from_located(stmt)))
|
||||
{
|
||||
if let Some(check) = checks::assert_tuple(test, Range::from_located(stmt)) {
|
||||
checker.add_check(check);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,12 +1,12 @@
|
||||
use rustpython_ast::{Expr, Stmt};
|
||||
|
||||
use crate::ast::types::{CheckLocator, Range};
|
||||
use crate::ast::types::Range;
|
||||
use crate::check_ast::Checker;
|
||||
use crate::pyflakes::checks;
|
||||
|
||||
/// F634
|
||||
pub fn if_tuple(checker: &mut Checker, stmt: &Stmt, test: &Expr) {
|
||||
if let Some(check) = checks::if_tuple(test, checker.locate_check(Range::from_located(stmt))) {
|
||||
if let Some(check) = checks::if_tuple(test, Range::from_located(stmt)) {
|
||||
checker.add_check(check);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
use rustpython_ast::{Expr, ExprKind};
|
||||
|
||||
use crate::ast::types::{CheckLocator, Range};
|
||||
use crate::ast::types::Range;
|
||||
use crate::autofix::Fix;
|
||||
use crate::check_ast::Checker;
|
||||
use crate::checks::{Check, CheckKind};
|
||||
@@ -27,10 +27,7 @@ fn match_not_implemented(expr: &Expr) -> Option<&Expr> {
|
||||
/// F901
|
||||
pub fn raise_not_implemented(checker: &mut Checker, expr: &Expr) {
|
||||
if let Some(expr) = match_not_implemented(expr) {
|
||||
let mut check = Check::new(
|
||||
CheckKind::RaiseNotImplemented,
|
||||
checker.locate_check(Range::from_located(expr)),
|
||||
);
|
||||
let mut check = Check::new(CheckKind::RaiseNotImplemented, Range::from_located(expr));
|
||||
if checker.patch() {
|
||||
check.amend(Fix::replacement(
|
||||
"NotImplementedError".to_string(),
|
||||
|
||||
@@ -175,7 +175,6 @@ static IMPORTED_SUBSCRIPTS: Lazy<BTreeMap<&'static str, BTreeSet<&'static str>>>
|
||||
("Counter", "typing_extensions"),
|
||||
("DefaultDict", "typing_extensions"),
|
||||
("Deque", "typing_extensions"),
|
||||
("Literal", "typing_extensions"),
|
||||
("Type", "typing_extensions"),
|
||||
// `weakref`
|
||||
("WeakKeyDictionary", "weakref"),
|
||||
|
||||
@@ -1,15 +1,13 @@
|
||||
use rustpython_ast::Expr;
|
||||
|
||||
use crate::ast::types::{CheckLocator, Range};
|
||||
use crate::ast::types::Range;
|
||||
use crate::autofix::Fix;
|
||||
use crate::check_ast::Checker;
|
||||
use crate::checks::CheckKind;
|
||||
use crate::pyupgrade::checks;
|
||||
|
||||
pub fn type_of_primitive(checker: &mut Checker, expr: &Expr, func: &Expr, args: &[Expr]) {
|
||||
if let Some(mut check) =
|
||||
checks::type_of_primitive(func, args, checker.locate_check(Range::from_located(expr)))
|
||||
{
|
||||
if let Some(mut check) = checks::type_of_primitive(func, args, Range::from_located(expr)) {
|
||||
if checker.patch() {
|
||||
if let CheckKind::TypeOfPrimitive(primitive) = &check.kind {
|
||||
check.amend(Fix::replacement(
|
||||
|
||||
@@ -1,14 +1,12 @@
|
||||
use rustpython_ast::Expr;
|
||||
|
||||
use crate::ast::types::{CheckLocator, Range};
|
||||
use crate::ast::types::Range;
|
||||
use crate::autofix::Fix;
|
||||
use crate::check_ast::Checker;
|
||||
use crate::pyupgrade::checks;
|
||||
|
||||
pub fn unnecessary_abspath(checker: &mut Checker, expr: &Expr, func: &Expr, args: &[Expr]) {
|
||||
if let Some(mut check) =
|
||||
checks::unnecessary_abspath(func, args, checker.locate_check(Range::from_located(expr)))
|
||||
{
|
||||
if let Some(mut check) = checks::unnecessary_abspath(func, args, Range::from_located(expr)) {
|
||||
if checker.patch() {
|
||||
check.amend(Fix::replacement(
|
||||
"__file__".to_string(),
|
||||
|
||||
@@ -1,17 +1,15 @@
|
||||
use log::error;
|
||||
use rustpython_ast::{Expr, Stmt};
|
||||
|
||||
use crate::ast::types::{CheckLocator, Range};
|
||||
use crate::ast::types::Range;
|
||||
use crate::autofix::helpers;
|
||||
use crate::check_ast::Checker;
|
||||
use crate::pyupgrade::checks;
|
||||
|
||||
pub fn useless_metaclass_type(checker: &mut Checker, stmt: &Stmt, value: &Expr, targets: &[Expr]) {
|
||||
if let Some(mut check) = checks::useless_metaclass_type(
|
||||
targets,
|
||||
value,
|
||||
checker.locate_check(Range::from_located(stmt)),
|
||||
) {
|
||||
if let Some(mut check) =
|
||||
checks::useless_metaclass_type(targets, value, Range::from_located(stmt))
|
||||
{
|
||||
if checker.patch() {
|
||||
let context = checker.binding_context();
|
||||
let deleted: Vec<&Stmt> = checker
|
||||
|
||||
@@ -2,7 +2,6 @@
|
||||
//! command-line options. Structure mirrors the user-facing representation of
|
||||
//! the various parameters.
|
||||
|
||||
use std::collections::BTreeMap;
|
||||
use std::path::PathBuf;
|
||||
|
||||
use anyhow::{anyhow, Result};
|
||||
@@ -11,8 +10,8 @@ use regex::Regex;
|
||||
|
||||
use crate::checks_gen::CheckCodePrefix;
|
||||
use crate::settings::pyproject::load_options;
|
||||
use crate::settings::types::{FilePattern, PythonVersion};
|
||||
use crate::{flake8_quotes, pep8_naming};
|
||||
use crate::settings::types::{FilePattern, PerFileIgnore, PythonVersion};
|
||||
use crate::{flake8_annotations, flake8_quotes, pep8_naming};
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct Configuration {
|
||||
@@ -21,12 +20,14 @@ pub struct Configuration {
|
||||
pub extend_exclude: Vec<FilePattern>,
|
||||
pub extend_ignore: Vec<CheckCodePrefix>,
|
||||
pub extend_select: Vec<CheckCodePrefix>,
|
||||
pub fix: bool,
|
||||
pub ignore: Vec<CheckCodePrefix>,
|
||||
pub line_length: usize,
|
||||
pub per_file_ignores: BTreeMap<String, Vec<CheckCodePrefix>>,
|
||||
pub per_file_ignores: Vec<PerFileIgnore>,
|
||||
pub select: Vec<CheckCodePrefix>,
|
||||
pub target_version: PythonVersion,
|
||||
// Plugins
|
||||
pub flake8_annotations: flake8_annotations::settings::Settings,
|
||||
pub flake8_quotes: flake8_quotes::settings::Settings,
|
||||
pub pep8_naming: pep8_naming::settings::Settings,
|
||||
}
|
||||
@@ -91,10 +92,25 @@ impl Configuration {
|
||||
.select
|
||||
.unwrap_or_else(|| vec![CheckCodePrefix::E, CheckCodePrefix::F]),
|
||||
extend_select: options.extend_select.unwrap_or_default(),
|
||||
fix: options.fix.unwrap_or_default(),
|
||||
ignore: options.ignore.unwrap_or_default(),
|
||||
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
|
||||
.map(|per_file_ignores| {
|
||||
per_file_ignores
|
||||
.iter()
|
||||
.map(|(pattern, prefixes)| {
|
||||
PerFileIgnore::new(pattern, prefixes, project_root)
|
||||
})
|
||||
.collect()
|
||||
})
|
||||
.unwrap_or_default(),
|
||||
// Plugins
|
||||
flake8_annotations: options
|
||||
.flake8_annotations
|
||||
.map(flake8_annotations::settings::Settings::from_options)
|
||||
.unwrap_or_default(),
|
||||
flake8_quotes: options
|
||||
.flake8_quotes
|
||||
.map(flake8_quotes::settings::Settings::from_options)
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
//! command-line options. Structure is optimized for internal usage, as opposed
|
||||
//! to external visibility or parsing.
|
||||
|
||||
use std::collections::{BTreeMap, BTreeSet};
|
||||
use std::collections::BTreeSet;
|
||||
use std::hash::{Hash, Hasher};
|
||||
|
||||
use regex::Regex;
|
||||
@@ -11,7 +11,7 @@ use crate::checks::CheckCode;
|
||||
use crate::checks_gen::{CheckCodePrefix, PrefixSpecificity};
|
||||
use crate::settings::configuration::Configuration;
|
||||
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 options;
|
||||
@@ -29,6 +29,7 @@ pub struct Settings {
|
||||
pub per_file_ignores: Vec<PerFileIgnore>,
|
||||
pub target_version: PythonVersion,
|
||||
// Plugins
|
||||
pub flake8_annotations: flake8_annotations::settings::Settings,
|
||||
pub flake8_quotes: flake8_quotes::settings::Settings,
|
||||
pub pep8_naming: pep8_naming::settings::Settings,
|
||||
}
|
||||
@@ -45,10 +46,11 @@ impl Settings {
|
||||
),
|
||||
exclude: config.exclude,
|
||||
extend_exclude: config.extend_exclude,
|
||||
flake8_annotations: config.flake8_annotations,
|
||||
flake8_quotes: config.flake8_quotes,
|
||||
line_length: config.line_length,
|
||||
pep8_naming: config.pep8_naming,
|
||||
per_file_ignores: resolve_per_file_ignores(&config.per_file_ignores),
|
||||
per_file_ignores: config.per_file_ignores,
|
||||
target_version: config.target_version,
|
||||
}
|
||||
}
|
||||
@@ -62,6 +64,7 @@ impl Settings {
|
||||
line_length: 88,
|
||||
per_file_ignores: Default::default(),
|
||||
target_version: PythonVersion::Py310,
|
||||
flake8_annotations: Default::default(),
|
||||
flake8_quotes: Default::default(),
|
||||
pep8_naming: Default::default(),
|
||||
}
|
||||
@@ -76,6 +79,7 @@ impl Settings {
|
||||
line_length: 88,
|
||||
per_file_ignores: Default::default(),
|
||||
target_version: PythonVersion::Py310,
|
||||
flake8_annotations: Default::default(),
|
||||
flake8_quotes: Default::default(),
|
||||
pep8_naming: Default::default(),
|
||||
}
|
||||
@@ -95,6 +99,7 @@ impl Hash for Settings {
|
||||
}
|
||||
self.target_version.hash(state);
|
||||
// Add plugin properties in alphabetical order.
|
||||
self.flake8_annotations.hash(state);
|
||||
self.flake8_quotes.hash(state);
|
||||
self.pep8_naming.hash(state);
|
||||
}
|
||||
@@ -143,15 +148,6 @@ fn resolve_codes(
|
||||
codes
|
||||
}
|
||||
|
||||
fn resolve_per_file_ignores(
|
||||
per_file_ignores: &BTreeMap<String, Vec<CheckCodePrefix>>,
|
||||
) -> Vec<PerFileIgnore> {
|
||||
per_file_ignores
|
||||
.iter()
|
||||
.map(|(pattern, prefixes)| PerFileIgnore::new(pattern, prefixes, &None))
|
||||
.collect()
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use std::collections::BTreeSet;
|
||||
|
||||
@@ -6,22 +6,24 @@ use serde::{Deserialize, Serialize};
|
||||
|
||||
use crate::checks_gen::CheckCodePrefix;
|
||||
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)]
|
||||
#[serde(deny_unknown_fields, rename_all = "kebab-case")]
|
||||
pub struct Options {
|
||||
pub line_length: Option<usize>,
|
||||
pub dummy_variable_rgx: Option<String>,
|
||||
pub exclude: Option<Vec<String>>,
|
||||
pub extend_exclude: Option<Vec<String>>,
|
||||
pub select: Option<Vec<CheckCodePrefix>>,
|
||||
pub extend_select: Option<Vec<CheckCodePrefix>>,
|
||||
pub ignore: Option<Vec<CheckCodePrefix>>,
|
||||
pub extend_ignore: Option<Vec<CheckCodePrefix>>,
|
||||
pub extend_select: Option<Vec<CheckCodePrefix>>,
|
||||
pub fix: Option<bool>,
|
||||
pub ignore: Option<Vec<CheckCodePrefix>>,
|
||||
pub line_length: Option<usize>,
|
||||
pub per_file_ignores: Option<BTreeMap<String, Vec<CheckCodePrefix>>>,
|
||||
pub dummy_variable_rgx: Option<String>,
|
||||
pub select: Option<Vec<CheckCodePrefix>>,
|
||||
pub target_version: Option<PythonVersion>,
|
||||
// Plugins
|
||||
pub flake8_annotations: Option<flake8_annotations::settings::Options>,
|
||||
pub flake8_quotes: Option<flake8_quotes::settings::Options>,
|
||||
pub pep8_naming: Option<pep8_naming::settings::Options>,
|
||||
}
|
||||
|
||||
@@ -134,6 +134,7 @@ mod tests {
|
||||
Some(Tools {
|
||||
ruff: Some(Options {
|
||||
line_length: None,
|
||||
fix: None,
|
||||
exclude: None,
|
||||
extend_exclude: None,
|
||||
select: None,
|
||||
@@ -143,6 +144,7 @@ mod tests {
|
||||
per_file_ignores: None,
|
||||
dummy_variable_rgx: None,
|
||||
target_version: None,
|
||||
flake8_annotations: None,
|
||||
flake8_quotes: None,
|
||||
pep8_naming: None,
|
||||
})
|
||||
@@ -161,6 +163,7 @@ line-length = 79
|
||||
Some(Tools {
|
||||
ruff: Some(Options {
|
||||
line_length: Some(79),
|
||||
fix: None,
|
||||
exclude: None,
|
||||
extend_exclude: None,
|
||||
select: None,
|
||||
@@ -170,6 +173,7 @@ line-length = 79
|
||||
per_file_ignores: None,
|
||||
dummy_variable_rgx: None,
|
||||
target_version: None,
|
||||
flake8_annotations: None,
|
||||
flake8_quotes: None,
|
||||
pep8_naming: None,
|
||||
})
|
||||
@@ -188,6 +192,7 @@ exclude = ["foo.py"]
|
||||
Some(Tools {
|
||||
ruff: Some(Options {
|
||||
line_length: None,
|
||||
fix: None,
|
||||
exclude: Some(vec!["foo.py".to_string()]),
|
||||
extend_exclude: None,
|
||||
select: None,
|
||||
@@ -197,6 +202,7 @@ exclude = ["foo.py"]
|
||||
per_file_ignores: None,
|
||||
dummy_variable_rgx: None,
|
||||
target_version: None,
|
||||
flake8_annotations: None,
|
||||
flake8_quotes: None,
|
||||
pep8_naming: None,
|
||||
})
|
||||
@@ -215,6 +221,7 @@ select = ["E501"]
|
||||
Some(Tools {
|
||||
ruff: Some(Options {
|
||||
line_length: None,
|
||||
fix: None,
|
||||
exclude: None,
|
||||
extend_exclude: None,
|
||||
select: Some(vec![CheckCodePrefix::E501]),
|
||||
@@ -224,6 +231,7 @@ select = ["E501"]
|
||||
per_file_ignores: None,
|
||||
dummy_variable_rgx: None,
|
||||
target_version: None,
|
||||
flake8_annotations: None,
|
||||
flake8_quotes: None,
|
||||
pep8_naming: None,
|
||||
})
|
||||
@@ -243,6 +251,7 @@ ignore = ["E501"]
|
||||
Some(Tools {
|
||||
ruff: Some(Options {
|
||||
line_length: None,
|
||||
fix: None,
|
||||
exclude: None,
|
||||
extend_exclude: None,
|
||||
select: None,
|
||||
@@ -252,6 +261,7 @@ ignore = ["E501"]
|
||||
per_file_ignores: None,
|
||||
dummy_variable_rgx: None,
|
||||
target_version: None,
|
||||
flake8_annotations: None,
|
||||
flake8_quotes: None,
|
||||
pep8_naming: None,
|
||||
})
|
||||
@@ -310,6 +320,7 @@ other-attribute = 1
|
||||
config,
|
||||
Options {
|
||||
line_length: Some(88),
|
||||
fix: None,
|
||||
exclude: None,
|
||||
extend_exclude: Some(vec![
|
||||
"excluded.py".to_string(),
|
||||
@@ -326,6 +337,7 @@ other-attribute = 1
|
||||
),])),
|
||||
dummy_variable_rgx: None,
|
||||
target_version: None,
|
||||
flake8_annotations: None,
|
||||
flake8_quotes: Some(flake8_quotes::settings::Options {
|
||||
inline_quotes: Some(Quote::Single),
|
||||
multiline_quotes: Some(Quote::Double),
|
||||
|
||||
@@ -1,13 +1,13 @@
|
||||
//! Structs to render user-facing settings.
|
||||
|
||||
use std::collections::BTreeMap;
|
||||
use std::path::PathBuf;
|
||||
|
||||
use regex::Regex;
|
||||
|
||||
use crate::checks::CheckCode;
|
||||
use crate::checks_gen::CheckCodePrefix;
|
||||
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.
|
||||
#[derive(Debug)]
|
||||
@@ -40,12 +40,14 @@ pub struct UserConfiguration {
|
||||
pub extend_exclude: Vec<Exclusion>,
|
||||
pub extend_ignore: Vec<CheckCodePrefix>,
|
||||
pub extend_select: Vec<CheckCodePrefix>,
|
||||
pub fix: bool,
|
||||
pub ignore: Vec<CheckCodePrefix>,
|
||||
pub line_length: usize,
|
||||
pub per_file_ignores: BTreeMap<String, Vec<CheckCodePrefix>>,
|
||||
pub per_file_ignores: Vec<(Exclusion, Vec<CheckCode>)>,
|
||||
pub select: Vec<CheckCodePrefix>,
|
||||
pub target_version: PythonVersion,
|
||||
// Plugins
|
||||
pub flake8_annotations: flake8_annotations::settings::Settings,
|
||||
pub flake8_quotes: flake8_quotes::settings::Settings,
|
||||
pub pep8_naming: pep8_naming::settings::Settings,
|
||||
// Non-settings exposed to the user
|
||||
@@ -73,11 +75,22 @@ impl UserConfiguration {
|
||||
.collect(),
|
||||
extend_ignore: configuration.extend_ignore,
|
||||
extend_select: configuration.extend_select,
|
||||
fix: configuration.fix,
|
||||
ignore: configuration.ignore,
|
||||
line_length: configuration.line_length,
|
||||
per_file_ignores: configuration.per_file_ignores,
|
||||
per_file_ignores: configuration
|
||||
.per_file_ignores
|
||||
.into_iter()
|
||||
.map(|per_file_ignore| {
|
||||
(
|
||||
Exclusion::from_file_pattern(per_file_ignore.pattern),
|
||||
Vec::from_iter(per_file_ignore.codes),
|
||||
)
|
||||
})
|
||||
.collect(),
|
||||
select: configuration.select,
|
||||
target_version: configuration.target_version,
|
||||
flake8_annotations: configuration.flake8_annotations,
|
||||
flake8_quotes: configuration.flake8_quotes,
|
||||
pep8_naming: configuration.pep8_naming,
|
||||
project_root,
|
||||
|
||||
21
src/snapshots/ruff__linter__tests__B004_B004.py.snap
Normal file
21
src/snapshots/ruff__linter__tests__B004_B004.py.snap
Normal file
@@ -0,0 +1,21 @@
|
||||
---
|
||||
source: src/linter.rs
|
||||
expression: checks
|
||||
---
|
||||
- kind: UnreliableCallableCheck
|
||||
location:
|
||||
row: 3
|
||||
column: 7
|
||||
end_location:
|
||||
row: 3
|
||||
column: 29
|
||||
fix: ~
|
||||
- kind: UnreliableCallableCheck
|
||||
location:
|
||||
row: 5
|
||||
column: 7
|
||||
end_location:
|
||||
row: 5
|
||||
column: 36
|
||||
fix: ~
|
||||
|
||||
53
src/snapshots/ruff__linter__tests__B005_B005.py.snap
Normal file
53
src/snapshots/ruff__linter__tests__B005_B005.py.snap
Normal file
@@ -0,0 +1,53 @@
|
||||
---
|
||||
source: src/linter.rs
|
||||
expression: checks
|
||||
---
|
||||
- kind: StripWithMultiCharacters
|
||||
location:
|
||||
row: 4
|
||||
column: 0
|
||||
end_location:
|
||||
row: 4
|
||||
column: 24
|
||||
fix: ~
|
||||
- kind: StripWithMultiCharacters
|
||||
location:
|
||||
row: 7
|
||||
column: 0
|
||||
end_location:
|
||||
row: 7
|
||||
column: 17
|
||||
fix: ~
|
||||
- kind: StripWithMultiCharacters
|
||||
location:
|
||||
row: 10
|
||||
column: 0
|
||||
end_location:
|
||||
row: 10
|
||||
column: 25
|
||||
fix: ~
|
||||
- kind: StripWithMultiCharacters
|
||||
location:
|
||||
row: 13
|
||||
column: 0
|
||||
end_location:
|
||||
row: 13
|
||||
column: 18
|
||||
fix: ~
|
||||
- kind: StripWithMultiCharacters
|
||||
location:
|
||||
row: 16
|
||||
column: 0
|
||||
end_location:
|
||||
row: 16
|
||||
column: 25
|
||||
fix: ~
|
||||
- kind: StripWithMultiCharacters
|
||||
location:
|
||||
row: 19
|
||||
column: 0
|
||||
end_location:
|
||||
row: 19
|
||||
column: 18
|
||||
fix: ~
|
||||
|
||||
@@ -28,13 +28,24 @@ pub struct VisibleScope {
|
||||
}
|
||||
|
||||
/// 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 {
|
||||
StmtKind::FunctionDef { decorator_list, .. }
|
||||
| StmtKind::AsyncFunctionDef { decorator_list, .. } => decorator_list
|
||||
.iter()
|
||||
.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"),
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user