-
Notifications
You must be signed in to change notification settings - Fork 1.2k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Implement unnecessary-literal-within-dict-call (C418)
- Loading branch information
1 parent
d8718dc
commit 2eaa31f
Showing
11 changed files
with
248 additions
and
4 deletions.
There are no files selected for viewing
10 changes: 10 additions & 0 deletions
10
crates/ruff/resources/test/fixtures/flake8_comprehensions/C418.py
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,10 @@ | ||
dict({}) | ||
dict({'a': 1}) | ||
dict({'x': 1 for x in range(10)}) | ||
dict( | ||
{'x': 1 for x in range(10)} | ||
) | ||
|
||
dict({}, a=1) | ||
dict({x: 1 for x in range(1)}, a=1) | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
99 changes: 99 additions & 0 deletions
99
crates/ruff/src/rules/flake8_comprehensions/rules/unnecessary_literal_within_dict_call.rs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,99 @@ | ||
use rustpython_parser::ast::{Expr, ExprKind, Keyword}; | ||
use std::fmt; | ||
|
||
use ruff_diagnostics::{AlwaysAutofixableViolation, Diagnostic}; | ||
use ruff_macros::{derive_message_formats, violation}; | ||
use ruff_python_ast::types::Range; | ||
|
||
use crate::checkers::ast::Checker; | ||
use crate::registry::AsRule; | ||
use crate::rules::flake8_comprehensions::fixes; | ||
|
||
use super::helpers; | ||
|
||
#[derive(Debug, PartialEq, Eq)] | ||
pub enum DictKind { | ||
Literal, | ||
Comprehension, | ||
} | ||
|
||
impl fmt::Display for DictKind { | ||
fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result { | ||
match self { | ||
DictKind::Literal => fmt.write_str("literal"), | ||
DictKind::Comprehension => fmt.write_str("comprehension"), | ||
} | ||
} | ||
} | ||
|
||
/// ## What it does | ||
/// Checks for `dict` calls that take unnecessary `dict` literals or `dict` | ||
/// comprehensions as arguments. | ||
/// | ||
/// ## Why is it bad? | ||
/// It's unnecessary to wrap a `dict` literal or comprehension within a `dict` | ||
/// call, since the literal or comprehension syntax already returns a `dict`. | ||
/// | ||
/// ## Examples | ||
/// ```python | ||
/// dict({}) | ||
/// dict({"a": 1}) | ||
/// ``` | ||
/// | ||
/// Use instead: | ||
/// ```python | ||
/// {} | ||
/// {"a": 1} | ||
/// ``` | ||
#[violation] | ||
pub struct UnnecessaryLiteralWithinDictCall { | ||
pub kind: DictKind, | ||
} | ||
|
||
impl AlwaysAutofixableViolation for UnnecessaryLiteralWithinDictCall { | ||
#[derive_message_formats] | ||
fn message(&self) -> String { | ||
let UnnecessaryLiteralWithinDictCall { kind } = self; | ||
format!("Unnecessary `dict` {kind} passed to `dict()` (remove the outer call to `dict()`)") | ||
} | ||
|
||
fn autofix_title(&self) -> String { | ||
"Remove outer `dict` call".to_string() | ||
} | ||
} | ||
|
||
/// C418 | ||
pub fn unnecessary_literal_within_dict_call( | ||
checker: &mut Checker, | ||
expr: &Expr, | ||
func: &Expr, | ||
args: &[Expr], | ||
keywords: &[Keyword], | ||
) { | ||
if !keywords.is_empty() { | ||
return; | ||
} | ||
let Some(argument) = helpers::first_argument_with_matching_function("dict", func, args) else { | ||
return; | ||
}; | ||
if !checker.ctx.is_builtin("dict") { | ||
return; | ||
} | ||
let argument_kind = match argument { | ||
ExprKind::DictComp { .. } => DictKind::Comprehension, | ||
ExprKind::Dict { .. } => DictKind::Literal, | ||
_ => return, | ||
}; | ||
let mut diagnostic = Diagnostic::new( | ||
UnnecessaryLiteralWithinDictCall { | ||
kind: argument_kind, | ||
}, | ||
Range::from(expr), | ||
); | ||
if checker.patch(diagnostic.kind.rule()) { | ||
diagnostic.try_set_fix(|| { | ||
fixes::fix_unnecessary_literal_within_dict_call(checker.locator, checker.stylist, expr) | ||
}); | ||
} | ||
checker.diagnostics.push(diagnostic); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
83 changes: 83 additions & 0 deletions
83
...ke8_comprehensions/snapshots/ruff__rules__flake8_comprehensions__tests__C418_C418.py.snap
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,83 @@ | ||
--- | ||
source: crates/ruff/src/rules/flake8_comprehensions/mod.rs | ||
--- | ||
C418.py:1:1: C418 [*] Unnecessary `dict` literal passed to `dict()` (remove the outer call to `dict()`) | ||
| | ||
1 | dict({}) | ||
| ^^^^^^^^ C418 | ||
2 | dict({'a': 1}) | ||
3 | dict({'x': 1 for x in range(10)}) | ||
| | ||
= help: Remove outer `dict` call | ||
|
||
ℹ Suggested fix | ||
1 |-dict({}) | ||
1 |+{} | ||
2 2 | dict({'a': 1}) | ||
3 3 | dict({'x': 1 for x in range(10)}) | ||
4 4 | dict( | ||
|
||
C418.py:2:1: C418 [*] Unnecessary `dict` literal passed to `dict()` (remove the outer call to `dict()`) | ||
| | ||
2 | dict({}) | ||
3 | dict({'a': 1}) | ||
| ^^^^^^^^^^^^^^ C418 | ||
4 | dict({'x': 1 for x in range(10)}) | ||
5 | dict( | ||
| | ||
= help: Remove outer `dict` call | ||
|
||
ℹ Suggested fix | ||
1 1 | dict({}) | ||
2 |-dict({'a': 1}) | ||
2 |+{'a': 1} | ||
3 3 | dict({'x': 1 for x in range(10)}) | ||
4 4 | dict( | ||
5 5 | {'x': 1 for x in range(10)} | ||
|
||
C418.py:3:1: C418 [*] Unnecessary `dict` comprehension passed to `dict()` (remove the outer call to `dict()`) | ||
| | ||
3 | dict({}) | ||
4 | dict({'a': 1}) | ||
5 | dict({'x': 1 for x in range(10)}) | ||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ C418 | ||
6 | dict( | ||
7 | {'x': 1 for x in range(10)} | ||
| | ||
= help: Remove outer `dict` call | ||
|
||
ℹ Suggested fix | ||
1 1 | dict({}) | ||
2 2 | dict({'a': 1}) | ||
3 |-dict({'x': 1 for x in range(10)}) | ||
3 |+{'x': 1 for x in range(10)} | ||
4 4 | dict( | ||
5 5 | {'x': 1 for x in range(10)} | ||
6 6 | ) | ||
|
||
C418.py:4:1: C418 [*] Unnecessary `dict` comprehension passed to `dict()` (remove the outer call to `dict()`) | ||
| | ||
4 | dict({'a': 1}) | ||
5 | dict({'x': 1 for x in range(10)}) | ||
6 | / dict( | ||
7 | | {'x': 1 for x in range(10)} | ||
8 | | ) | ||
| |_^ C418 | ||
9 | | ||
10 | dict({}, a=1) | ||
| | ||
= help: Remove outer `dict` call | ||
|
||
ℹ Suggested fix | ||
1 1 | dict({}) | ||
2 2 | dict({'a': 1}) | ||
3 3 | dict({'x': 1 for x in range(10)}) | ||
4 |-dict( | ||
5 |- {'x': 1 for x in range(10)} | ||
6 |-) | ||
4 |+{'x': 1 for x in range(10)} | ||
7 5 | | ||
8 6 | dict({}, a=1) | ||
9 7 | dict({x: 1 for x in range(1)}, a=1) | ||
|
||
|