mirror of https://github.com/astral-sh/ruff
Implement autofix for dict and tuple comprehensions (#555)
This commit is contained in:
parent
416c338237
commit
6a180b95d1
10
README.md
10
README.md
|
|
@ -394,14 +394,14 @@ The 🛠 emoji indicates that a rule is automatically fixable by the `--fix` com
|
||||||
| ---- | ---- | ------- | --- |
|
| ---- | ---- | ------- | --- |
|
||||||
| C400 | UnnecessaryGeneratorList | Unnecessary generator (rewrite as a `list` comprehension) | 🛠 |
|
| C400 | UnnecessaryGeneratorList | Unnecessary generator (rewrite as a `list` comprehension) | 🛠 |
|
||||||
| C401 | UnnecessaryGeneratorSet | Unnecessary generator (rewrite as a `set` comprehension) | 🛠 |
|
| C401 | UnnecessaryGeneratorSet | Unnecessary generator (rewrite as a `set` comprehension) | 🛠 |
|
||||||
| C402 | UnnecessaryGeneratorDict | Unnecessary generator (rewrite as a `dict` comprehension) | |
|
| C402 | UnnecessaryGeneratorDict | Unnecessary generator (rewrite as a `dict` comprehension) | 🛠 |
|
||||||
| C403 | UnnecessaryListComprehensionSet | Unnecessary `list` comprehension (rewrite as a `set` comprehension) | 🛠 |
|
| C403 | UnnecessaryListComprehensionSet | Unnecessary `list` comprehension (rewrite as a `set` comprehension) | 🛠 |
|
||||||
| C404 | UnnecessaryListComprehensionDict | Unnecessary `list` comprehension (rewrite as a `dict` comprehension) | |
|
| C404 | UnnecessaryListComprehensionDict | Unnecessary `list` comprehension (rewrite as a `dict` comprehension) | |
|
||||||
| C405 | UnnecessaryLiteralSet | Unnecessary `(list\|tuple)` literal (rewrite as a `set` literal) | 🛠 |
|
| C405 | UnnecessaryLiteralSet | Unnecessary `(list\|tuple)` literal (rewrite as a `set` literal) | 🛠 |
|
||||||
| C406 | UnnecessaryLiteralDict | Unnecessary `(list\|tuple)` literal (rewrite as a `dict` literal) | |
|
| C406 | UnnecessaryLiteralDict | Unnecessary `(list\|tuple)` literal (rewrite as a `dict` literal) | |
|
||||||
| C408 | UnnecessaryCollectionCall | Unnecessary `(dict\|list\|tuple)` call (rewrite as a literal) | 🛠 |
|
| C408 | UnnecessaryCollectionCall | Unnecessary `(dict\|list\|tuple)` call (rewrite as a literal) | 🛠 |
|
||||||
| C409 | UnnecessaryLiteralWithinTupleCall | Unnecessary `(list\|tuple)` literal passed to `tuple()` (remove the outer call to `tuple()`) | |
|
| C409 | UnnecessaryLiteralWithinTupleCall | Unnecessary `(list\|tuple)` literal passed to `tuple()` (remove the outer call to `tuple()`) | 🛠 |
|
||||||
| C410 | UnnecessaryLiteralWithinListCall | Unnecessary `(list\|tuple)` literal passed to `list()` (rewrite as a `list` literal) | |
|
| C410 | UnnecessaryLiteralWithinListCall | Unnecessary `(list\|tuple)` literal passed to `list()` (rewrite as a `list` literal) | 🛠 |
|
||||||
| C411 | UnnecessaryListCall | Unnecessary `list` call (remove the outer call to `list()`) | 🛠 |
|
| C411 | UnnecessaryListCall | Unnecessary `list` call (remove the outer call to `list()`) | 🛠 |
|
||||||
| C413 | UnnecessaryCallAroundSorted | Unnecessary `(list\|reversed)` call around `sorted()` | |
|
| C413 | UnnecessaryCallAroundSorted | Unnecessary `(list\|reversed)` call around `sorted()` | |
|
||||||
| C414 | UnnecessaryDoubleCastOrProcess | Unnecessary `(list\|reversed\|set\|sorted\|tuple)` call within `(list\|set\|sorted\|tuple)()` | |
|
| C414 | UnnecessaryDoubleCastOrProcess | Unnecessary `(list\|reversed\|set\|sorted\|tuple)` call within `(list\|set\|sorted\|tuple)()` | |
|
||||||
|
|
@ -521,7 +521,7 @@ including:
|
||||||
- [`flake8-quotes`](https://pypi.org/project/flake8-quotes/)
|
- [`flake8-quotes`](https://pypi.org/project/flake8-quotes/)
|
||||||
- [`flake8-comprehensions`](https://pypi.org/project/flake8-comprehensions/)
|
- [`flake8-comprehensions`](https://pypi.org/project/flake8-comprehensions/)
|
||||||
- [`flake8-bugbear`](https://pypi.org/project/flake8-bugbear/) (10/32)
|
- [`flake8-bugbear`](https://pypi.org/project/flake8-bugbear/) (10/32)
|
||||||
- [`pyupgrade`](https://pypi.org/project/pyupgrade/) (9/34)
|
- [`pyupgrade`](https://pypi.org/project/pyupgrade/) (10/34)
|
||||||
- [`autoflake`](https://pypi.org/project/autoflake/) (1/7)
|
- [`autoflake`](https://pypi.org/project/autoflake/) (1/7)
|
||||||
|
|
||||||
Beyond rule-set parity, Ruff suffers from the following limitations vis-à-vis Flake8:
|
Beyond rule-set parity, Ruff suffers from the following limitations vis-à-vis Flake8:
|
||||||
|
|
@ -545,7 +545,7 @@ Today, Ruff can be used to replace Flake8 when used with any of the following pl
|
||||||
- [`flake8-bugbear`](https://pypi.org/project/flake8-bugbear/) (10/32)
|
- [`flake8-bugbear`](https://pypi.org/project/flake8-bugbear/) (10/32)
|
||||||
|
|
||||||
Ruff also implements the functionality that you get from [`yesqa`](https://github.com/asottile/yesqa),
|
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/) (9/34).
|
and a subset of the rules implemented in [`pyupgrade`](https://pypi.org/project/pyupgrade/) (10/34).
|
||||||
|
|
||||||
If you're looking to use Ruff, but rely on an unsupported Flake8 plugin, free to file an Issue.
|
If you're looking to use Ruff, but rely on an unsupported Flake8 plugin, free to file an Issue.
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,2 +1,5 @@
|
||||||
dict((x, x) for x in range(3))
|
dict((x, x) for x in range(3))
|
||||||
|
dict(
|
||||||
|
(x, x) for x in range(3)
|
||||||
|
)
|
||||||
dict(((x, x) for x in range(3)), z=3)
|
dict(((x, x) for x in range(3)), z=3)
|
||||||
|
|
|
||||||
|
|
@ -1,3 +1,10 @@
|
||||||
t1 = tuple([1, 2])
|
t1 = tuple([])
|
||||||
t2 = tuple((1, 2))
|
t2 = tuple([1, 2])
|
||||||
t3 = tuple([])
|
t3 = tuple((1, 2))
|
||||||
|
t4 = tuple([
|
||||||
|
1,
|
||||||
|
2
|
||||||
|
])
|
||||||
|
t5 = tuple(
|
||||||
|
(1, 2)
|
||||||
|
)
|
||||||
|
|
|
||||||
|
|
@ -1015,7 +1015,12 @@ where
|
||||||
|
|
||||||
if self.settings.enabled.contains(&CheckCode::C402) {
|
if self.settings.enabled.contains(&CheckCode::C402) {
|
||||||
if let Some(check) = flake8_comprehensions::checks::unnecessary_generator_dict(
|
if let Some(check) = flake8_comprehensions::checks::unnecessary_generator_dict(
|
||||||
expr, func, args, keywords,
|
expr,
|
||||||
|
func,
|
||||||
|
args,
|
||||||
|
keywords,
|
||||||
|
self.locator,
|
||||||
|
self.patch(),
|
||||||
) {
|
) {
|
||||||
self.checks.push(check);
|
self.checks.push(check);
|
||||||
};
|
};
|
||||||
|
|
@ -1083,7 +1088,11 @@ where
|
||||||
if self.settings.enabled.contains(&CheckCode::C409) {
|
if self.settings.enabled.contains(&CheckCode::C409) {
|
||||||
if let Some(check) =
|
if let Some(check) =
|
||||||
flake8_comprehensions::checks::unnecessary_literal_within_tuple_call(
|
flake8_comprehensions::checks::unnecessary_literal_within_tuple_call(
|
||||||
expr, func, args,
|
expr,
|
||||||
|
func,
|
||||||
|
args,
|
||||||
|
self.locator,
|
||||||
|
self.patch(),
|
||||||
)
|
)
|
||||||
{
|
{
|
||||||
self.checks.push(check);
|
self.checks.push(check);
|
||||||
|
|
@ -1093,7 +1102,11 @@ where
|
||||||
if self.settings.enabled.contains(&CheckCode::C410) {
|
if self.settings.enabled.contains(&CheckCode::C410) {
|
||||||
if let Some(check) =
|
if let Some(check) =
|
||||||
flake8_comprehensions::checks::unnecessary_literal_within_list_call(
|
flake8_comprehensions::checks::unnecessary_literal_within_list_call(
|
||||||
expr, func, args,
|
expr,
|
||||||
|
func,
|
||||||
|
args,
|
||||||
|
self.locator,
|
||||||
|
self.patch(),
|
||||||
)
|
)
|
||||||
{
|
{
|
||||||
self.checks.push(check);
|
self.checks.push(check);
|
||||||
|
|
|
||||||
|
|
@ -1470,11 +1470,14 @@ impl CheckKind {
|
||||||
| CheckKind::TypeOfPrimitive(_)
|
| CheckKind::TypeOfPrimitive(_)
|
||||||
| CheckKind::UnnecessaryAbspath
|
| CheckKind::UnnecessaryAbspath
|
||||||
| CheckKind::UnnecessaryCollectionCall(_)
|
| CheckKind::UnnecessaryCollectionCall(_)
|
||||||
|
| CheckKind::UnnecessaryGeneratorDict
|
||||||
| CheckKind::UnnecessaryGeneratorList
|
| CheckKind::UnnecessaryGeneratorList
|
||||||
| CheckKind::UnnecessaryGeneratorSet
|
| CheckKind::UnnecessaryGeneratorSet
|
||||||
| CheckKind::UnnecessaryListCall
|
| CheckKind::UnnecessaryListCall
|
||||||
| CheckKind::UnnecessaryListComprehensionSet
|
| CheckKind::UnnecessaryListComprehensionSet
|
||||||
| CheckKind::UnnecessaryLiteralSet(_)
|
| CheckKind::UnnecessaryLiteralSet(_)
|
||||||
|
| CheckKind::UnnecessaryLiteralWithinListCall(_)
|
||||||
|
| CheckKind::UnnecessaryLiteralWithinTupleCall(_)
|
||||||
| CheckKind::UnusedImport(_, false)
|
| CheckKind::UnusedImport(_, false)
|
||||||
| CheckKind::UnusedLoopControlVariable(_)
|
| CheckKind::UnusedLoopControlVariable(_)
|
||||||
| CheckKind::UnusedNOQA(_)
|
| CheckKind::UnusedNOQA(_)
|
||||||
|
|
|
||||||
|
|
@ -104,15 +104,24 @@ pub fn unnecessary_generator_dict(
|
||||||
func: &Expr,
|
func: &Expr,
|
||||||
args: &[Expr],
|
args: &[Expr],
|
||||||
keywords: &[Keyword],
|
keywords: &[Keyword],
|
||||||
|
locator: &SourceCodeLocator,
|
||||||
|
fix: bool,
|
||||||
) -> Option<Check> {
|
) -> Option<Check> {
|
||||||
let argument = exactly_one_argument_with_matching_function("dict", func, args, keywords)?;
|
let argument = exactly_one_argument_with_matching_function("dict", func, args, keywords)?;
|
||||||
if let ExprKind::GeneratorExp { elt, .. } = argument {
|
if let ExprKind::GeneratorExp { elt, .. } = argument {
|
||||||
match &elt.node {
|
match &elt.node {
|
||||||
ExprKind::Tuple { elts, .. } if elts.len() == 2 => {
|
ExprKind::Tuple { elts, .. } if elts.len() == 2 => {
|
||||||
return Some(Check::new(
|
let mut check = Check::new(
|
||||||
CheckKind::UnnecessaryGeneratorDict,
|
CheckKind::UnnecessaryGeneratorDict,
|
||||||
Range::from_located(expr),
|
Range::from_located(expr),
|
||||||
));
|
);
|
||||||
|
if fix {
|
||||||
|
match fixes::fix_unnecessary_generator_dict(locator, expr) {
|
||||||
|
Ok(fix) => check.amend(fix),
|
||||||
|
Err(e) => error!("Failed to generate fix: {}", e),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return Some(check);
|
||||||
}
|
}
|
||||||
_ => {}
|
_ => {}
|
||||||
}
|
}
|
||||||
|
|
@ -266,6 +275,8 @@ pub fn unnecessary_literal_within_tuple_call(
|
||||||
expr: &Expr,
|
expr: &Expr,
|
||||||
func: &Expr,
|
func: &Expr,
|
||||||
args: &[Expr],
|
args: &[Expr],
|
||||||
|
locator: &SourceCodeLocator,
|
||||||
|
fix: bool,
|
||||||
) -> Option<Check> {
|
) -> Option<Check> {
|
||||||
let argument = first_argument_with_matching_function("tuple", func, args)?;
|
let argument = first_argument_with_matching_function("tuple", func, args)?;
|
||||||
let argument_kind = match argument {
|
let argument_kind = match argument {
|
||||||
|
|
@ -273,10 +284,17 @@ pub fn unnecessary_literal_within_tuple_call(
|
||||||
ExprKind::List { .. } => "list",
|
ExprKind::List { .. } => "list",
|
||||||
_ => return None,
|
_ => return None,
|
||||||
};
|
};
|
||||||
Some(Check::new(
|
let mut check = Check::new(
|
||||||
CheckKind::UnnecessaryLiteralWithinTupleCall(argument_kind.to_string()),
|
CheckKind::UnnecessaryLiteralWithinTupleCall(argument_kind.to_string()),
|
||||||
Range::from_located(expr),
|
Range::from_located(expr),
|
||||||
))
|
);
|
||||||
|
if fix {
|
||||||
|
match fixes::fix_unnecessary_literal_within_tuple_call(locator, expr) {
|
||||||
|
Ok(fix) => check.amend(fix),
|
||||||
|
Err(e) => error!("Failed to generate fix: {}", e),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Some(check)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// C410
|
/// C410
|
||||||
|
|
@ -284,6 +302,8 @@ pub fn unnecessary_literal_within_list_call(
|
||||||
expr: &Expr,
|
expr: &Expr,
|
||||||
func: &Expr,
|
func: &Expr,
|
||||||
args: &[Expr],
|
args: &[Expr],
|
||||||
|
locator: &SourceCodeLocator,
|
||||||
|
fix: bool,
|
||||||
) -> Option<Check> {
|
) -> Option<Check> {
|
||||||
let argument = first_argument_with_matching_function("list", func, args)?;
|
let argument = first_argument_with_matching_function("list", func, args)?;
|
||||||
let argument_kind = match argument {
|
let argument_kind = match argument {
|
||||||
|
|
@ -291,10 +311,17 @@ pub fn unnecessary_literal_within_list_call(
|
||||||
ExprKind::List { .. } => "list",
|
ExprKind::List { .. } => "list",
|
||||||
_ => return None,
|
_ => return None,
|
||||||
};
|
};
|
||||||
Some(Check::new(
|
let mut check = Check::new(
|
||||||
CheckKind::UnnecessaryLiteralWithinListCall(argument_kind.to_string()),
|
CheckKind::UnnecessaryLiteralWithinListCall(argument_kind.to_string()),
|
||||||
Range::from_located(expr),
|
Range::from_located(expr),
|
||||||
))
|
);
|
||||||
|
if fix {
|
||||||
|
match fixes::fix_unnecessary_literal_within_list_call(locator, expr) {
|
||||||
|
Ok(fix) => check.amend(fix),
|
||||||
|
Err(e) => error!("Failed to generate fix: {}", e),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Some(check)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// C411
|
/// C411
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,8 @@
|
||||||
use anyhow::Result;
|
use anyhow::Result;
|
||||||
use libcst_native::{
|
use libcst_native::{
|
||||||
Arg, Codegen, Dict, Expression, LeftCurlyBrace, LeftSquareBracket, List, ListComp,
|
Arg, Codegen, Dict, DictComp, Element, Expression, LeftCurlyBrace, LeftParen,
|
||||||
RightCurlyBrace, RightSquareBracket, Set, SetComp, SmallStatement, Statement, Tuple,
|
LeftSquareBracket, List, ListComp, ParenthesizableWhitespace, RightCurlyBrace, RightParen,
|
||||||
|
RightSquareBracket, Set, SetComp, SimpleWhitespace, SmallStatement, Statement, Tuple,
|
||||||
};
|
};
|
||||||
use rustpython_ast::Expr;
|
use rustpython_ast::Expr;
|
||||||
|
|
||||||
|
|
@ -32,7 +33,7 @@ pub fn fix_unnecessary_generator_list(locator: &SourceCodeLocator, expr: &Expr)
|
||||||
"Expected node to be: SmallStatement::Expr."
|
"Expected node to be: SmallStatement::Expr."
|
||||||
));
|
));
|
||||||
};
|
};
|
||||||
let call = if let Expression::Call(call) = &mut body.value {
|
let call = if let Expression::Call(call) = &body.value {
|
||||||
call
|
call
|
||||||
} else {
|
} else {
|
||||||
return Err(anyhow::anyhow!("Expected node to be: Expression::Call."));
|
return Err(anyhow::anyhow!("Expected node to be: Expression::Call."));
|
||||||
|
|
@ -41,7 +42,7 @@ pub fn fix_unnecessary_generator_list(locator: &SourceCodeLocator, expr: &Expr)
|
||||||
value,
|
value,
|
||||||
whitespace_after_arg,
|
whitespace_after_arg,
|
||||||
..
|
..
|
||||||
}) = call.args.first_mut()
|
}) = call.args.first()
|
||||||
{
|
{
|
||||||
(value, whitespace_after_arg)
|
(value, whitespace_after_arg)
|
||||||
} else {
|
} else {
|
||||||
|
|
@ -101,7 +102,7 @@ pub fn fix_unnecessary_generator_set(locator: &SourceCodeLocator, expr: &Expr) -
|
||||||
"Expected node to be: SmallStatement::Expr."
|
"Expected node to be: SmallStatement::Expr."
|
||||||
));
|
));
|
||||||
};
|
};
|
||||||
let call = if let Expression::Call(call) = &mut body.value {
|
let call = if let Expression::Call(call) = &body.value {
|
||||||
call
|
call
|
||||||
} else {
|
} else {
|
||||||
return Err(anyhow::anyhow!("Expected node to be: Expression::Call."));
|
return Err(anyhow::anyhow!("Expected node to be: Expression::Call."));
|
||||||
|
|
@ -110,7 +111,7 @@ pub fn fix_unnecessary_generator_set(locator: &SourceCodeLocator, expr: &Expr) -
|
||||||
value,
|
value,
|
||||||
whitespace_after_arg,
|
whitespace_after_arg,
|
||||||
..
|
..
|
||||||
}) = call.args.first_mut()
|
}) = call.args.first()
|
||||||
{
|
{
|
||||||
(value, whitespace_after_arg)
|
(value, whitespace_after_arg)
|
||||||
} else {
|
} else {
|
||||||
|
|
@ -147,6 +148,96 @@ pub fn fix_unnecessary_generator_set(locator: &SourceCodeLocator, expr: &Expr) -
|
||||||
))
|
))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// (C402) Convert `dict((x, x) for x in range(3))` to `{x: x for x in
|
||||||
|
/// range(3)}`.
|
||||||
|
pub fn fix_unnecessary_generator_dict(locator: &SourceCodeLocator, expr: &Expr) -> Result<Fix> {
|
||||||
|
let mut tree = match libcst_native::parse_module(
|
||||||
|
locator.slice_source_code_range(&Range::from_located(expr)),
|
||||||
|
None,
|
||||||
|
) {
|
||||||
|
Ok(m) => m,
|
||||||
|
Err(_) => return Err(anyhow::anyhow!("Failed to extract CST from source.")),
|
||||||
|
};
|
||||||
|
let body = if let Some(Statement::Simple(body)) = tree.body.first_mut() {
|
||||||
|
body
|
||||||
|
} else {
|
||||||
|
return Err(anyhow::anyhow!("Expected node to be: Statement::Simple."));
|
||||||
|
};
|
||||||
|
let body = if let Some(SmallStatement::Expr(body)) = body.body.first_mut() {
|
||||||
|
body
|
||||||
|
} else {
|
||||||
|
return Err(anyhow::anyhow!(
|
||||||
|
"Expected node to be: SmallStatement::Expr."
|
||||||
|
));
|
||||||
|
};
|
||||||
|
let call = if let Expression::Call(call) = &body.value {
|
||||||
|
call
|
||||||
|
} else {
|
||||||
|
return Err(anyhow::anyhow!("Expected node to be: Expression::Call."));
|
||||||
|
};
|
||||||
|
let (arg, whitespace_after_arg) = if let Some(Arg {
|
||||||
|
value,
|
||||||
|
whitespace_after_arg,
|
||||||
|
..
|
||||||
|
}) = call.args.first()
|
||||||
|
{
|
||||||
|
(value, whitespace_after_arg)
|
||||||
|
} else {
|
||||||
|
return Err(anyhow::anyhow!("Expected node to be: Arg."));
|
||||||
|
};
|
||||||
|
let generator_exp = if let Expression::GeneratorExp(generator_exp) = &arg {
|
||||||
|
generator_exp
|
||||||
|
} else {
|
||||||
|
return Err(anyhow::anyhow!(
|
||||||
|
"Expected node to be: Expression::GeneratorExp."
|
||||||
|
));
|
||||||
|
};
|
||||||
|
let tuple = if let Expression::Tuple(tuple) = &generator_exp.elt.as_ref() {
|
||||||
|
tuple
|
||||||
|
} else {
|
||||||
|
return Err(anyhow::anyhow!("Expected node to be: Expression::Tuple."));
|
||||||
|
};
|
||||||
|
let key = if let Some(Element::Simple { value, .. }) = &tuple.elements.get(0) {
|
||||||
|
value
|
||||||
|
} else {
|
||||||
|
return Err(anyhow::anyhow!(
|
||||||
|
"Expected tuple to contain a key as the first element."
|
||||||
|
));
|
||||||
|
};
|
||||||
|
let value = if let Some(Element::Simple { value, .. }) = &tuple.elements.get(1) {
|
||||||
|
value
|
||||||
|
} else {
|
||||||
|
return Err(anyhow::anyhow!(
|
||||||
|
"Expected tuple to contain a key as the second element."
|
||||||
|
));
|
||||||
|
};
|
||||||
|
|
||||||
|
body.value = Expression::DictComp(Box::new(DictComp {
|
||||||
|
key: Box::new(key.clone()),
|
||||||
|
value: Box::new(value.clone()),
|
||||||
|
for_in: generator_exp.for_in.clone(),
|
||||||
|
lbrace: LeftCurlyBrace {
|
||||||
|
whitespace_after: call.whitespace_before_args.clone(),
|
||||||
|
},
|
||||||
|
rbrace: RightCurlyBrace {
|
||||||
|
whitespace_before: whitespace_after_arg.clone(),
|
||||||
|
},
|
||||||
|
lpar: Default::default(),
|
||||||
|
rpar: Default::default(),
|
||||||
|
whitespace_before_colon: Default::default(),
|
||||||
|
whitespace_after_colon: ParenthesizableWhitespace::SimpleWhitespace(SimpleWhitespace(" ")),
|
||||||
|
}));
|
||||||
|
|
||||||
|
let mut state = Default::default();
|
||||||
|
tree.codegen(&mut state);
|
||||||
|
|
||||||
|
Ok(Fix::replacement(
|
||||||
|
state.to_string(),
|
||||||
|
expr.location,
|
||||||
|
expr.end_location.unwrap(),
|
||||||
|
))
|
||||||
|
}
|
||||||
|
|
||||||
/// (C403) Convert `set([x for x in y])` to `{x for x in y}`.
|
/// (C403) Convert `set([x for x in y])` to `{x for x in y}`.
|
||||||
pub fn fix_unnecessary_list_comprehension_set(
|
pub fn fix_unnecessary_list_comprehension_set(
|
||||||
locator: &SourceCodeLocator,
|
locator: &SourceCodeLocator,
|
||||||
|
|
@ -173,7 +264,7 @@ pub fn fix_unnecessary_list_comprehension_set(
|
||||||
"Expected node to be: SmallStatement::Expr."
|
"Expected node to be: SmallStatement::Expr."
|
||||||
));
|
));
|
||||||
};
|
};
|
||||||
let call = if let Expression::Call(call) = &mut body.value {
|
let call = if let Expression::Call(call) = &body.value {
|
||||||
call
|
call
|
||||||
} else {
|
} else {
|
||||||
return Err(anyhow::anyhow!("Expected node to be: Expression::Call."));
|
return Err(anyhow::anyhow!("Expected node to be: Expression::Call."));
|
||||||
|
|
@ -182,7 +273,7 @@ pub fn fix_unnecessary_list_comprehension_set(
|
||||||
value,
|
value,
|
||||||
whitespace_after_arg,
|
whitespace_after_arg,
|
||||||
..
|
..
|
||||||
}) = call.args.first_mut()
|
}) = call.args.first()
|
||||||
{
|
{
|
||||||
(value, whitespace_after_arg)
|
(value, whitespace_after_arg)
|
||||||
} else {
|
} else {
|
||||||
|
|
@ -370,6 +461,168 @@ pub fn fix_unnecessary_collection_call(locator: &SourceCodeLocator, expr: &Expr)
|
||||||
))
|
))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// (C409) Convert `tuple([1, 2])` to `tuple(1, 2)`
|
||||||
|
pub fn fix_unnecessary_literal_within_tuple_call(
|
||||||
|
locator: &SourceCodeLocator,
|
||||||
|
expr: &Expr,
|
||||||
|
) -> Result<Fix> {
|
||||||
|
let mut tree = match libcst_native::parse_module(
|
||||||
|
locator.slice_source_code_range(&Range::from_located(expr)),
|
||||||
|
None,
|
||||||
|
) {
|
||||||
|
Ok(m) => m,
|
||||||
|
Err(_) => return Err(anyhow::anyhow!("Failed to extract CST from source.")),
|
||||||
|
};
|
||||||
|
let body = if let Some(Statement::Simple(body)) = tree.body.first_mut() {
|
||||||
|
body
|
||||||
|
} else {
|
||||||
|
return Err(anyhow::anyhow!("Expected node to be: Statement::Simple."));
|
||||||
|
};
|
||||||
|
let body = if let Some(SmallStatement::Expr(body)) = body.body.first_mut() {
|
||||||
|
body
|
||||||
|
} else {
|
||||||
|
return Err(anyhow::anyhow!(
|
||||||
|
"Expected node to be: SmallStatement::Expr."
|
||||||
|
));
|
||||||
|
};
|
||||||
|
let call = if let Expression::Call(call) = &body.value {
|
||||||
|
call
|
||||||
|
} else {
|
||||||
|
return Err(anyhow::anyhow!("Expected node to be: Expression::Call."));
|
||||||
|
};
|
||||||
|
let arg = if let Some(Arg { value, .. }) = call.args.first() {
|
||||||
|
value
|
||||||
|
} else {
|
||||||
|
return Err(anyhow::anyhow!("Expected node to be: Arg."));
|
||||||
|
};
|
||||||
|
let (elements, whitespace_after, whitespace_before) = match arg {
|
||||||
|
Expression::Tuple(inner) => (
|
||||||
|
&inner.elements,
|
||||||
|
&inner
|
||||||
|
.lpar
|
||||||
|
.first()
|
||||||
|
.ok_or_else(|| anyhow::anyhow!("Expected at least one set of parentheses."))?
|
||||||
|
.whitespace_after,
|
||||||
|
&inner
|
||||||
|
.rpar
|
||||||
|
.first()
|
||||||
|
.ok_or_else(|| anyhow::anyhow!("Expected at least one set of parentheses."))?
|
||||||
|
.whitespace_before,
|
||||||
|
),
|
||||||
|
Expression::List(inner) => (
|
||||||
|
&inner.elements,
|
||||||
|
&inner.lbracket.whitespace_after,
|
||||||
|
&inner.rbracket.whitespace_before,
|
||||||
|
),
|
||||||
|
_ => {
|
||||||
|
return Err(anyhow::anyhow!(
|
||||||
|
"Expected node to be: Expression::Tuple | Expression::List."
|
||||||
|
))
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
body.value = Expression::Tuple(Box::new(Tuple {
|
||||||
|
elements: elements.clone(),
|
||||||
|
lpar: vec![LeftParen {
|
||||||
|
whitespace_after: whitespace_after.clone(),
|
||||||
|
}],
|
||||||
|
rpar: vec![RightParen {
|
||||||
|
whitespace_before: whitespace_before.clone(),
|
||||||
|
}],
|
||||||
|
}));
|
||||||
|
|
||||||
|
let mut state = Default::default();
|
||||||
|
tree.codegen(&mut state);
|
||||||
|
|
||||||
|
Ok(Fix::replacement(
|
||||||
|
state.to_string(),
|
||||||
|
expr.location,
|
||||||
|
expr.end_location.unwrap(),
|
||||||
|
))
|
||||||
|
}
|
||||||
|
|
||||||
|
/// (C410) Convert `list([1, 2])` to `[1, 2]`
|
||||||
|
pub fn fix_unnecessary_literal_within_list_call(
|
||||||
|
locator: &SourceCodeLocator,
|
||||||
|
expr: &Expr,
|
||||||
|
) -> Result<Fix> {
|
||||||
|
let mut tree = match libcst_native::parse_module(
|
||||||
|
locator.slice_source_code_range(&Range::from_located(expr)),
|
||||||
|
None,
|
||||||
|
) {
|
||||||
|
Ok(m) => m,
|
||||||
|
Err(_) => return Err(anyhow::anyhow!("Failed to extract CST from source.")),
|
||||||
|
};
|
||||||
|
let body = if let Some(Statement::Simple(body)) = tree.body.first_mut() {
|
||||||
|
body
|
||||||
|
} else {
|
||||||
|
return Err(anyhow::anyhow!("Expected node to be: Statement::Simple."));
|
||||||
|
};
|
||||||
|
let body = if let Some(SmallStatement::Expr(body)) = body.body.first_mut() {
|
||||||
|
body
|
||||||
|
} else {
|
||||||
|
return Err(anyhow::anyhow!(
|
||||||
|
"Expected node to be: SmallStatement::Expr."
|
||||||
|
));
|
||||||
|
};
|
||||||
|
let call = if let Expression::Call(call) = &body.value {
|
||||||
|
call
|
||||||
|
} else {
|
||||||
|
return Err(anyhow::anyhow!("Expected node to be: Expression::Call."));
|
||||||
|
};
|
||||||
|
let arg = if let Some(Arg { value, .. }) = call.args.first() {
|
||||||
|
value
|
||||||
|
} else {
|
||||||
|
return Err(anyhow::anyhow!("Expected node to be: Arg."));
|
||||||
|
};
|
||||||
|
let (elements, whitespace_after, whitespace_before) = match arg {
|
||||||
|
Expression::Tuple(inner) => (
|
||||||
|
&inner.elements,
|
||||||
|
&inner
|
||||||
|
.lpar
|
||||||
|
.first()
|
||||||
|
.ok_or_else(|| anyhow::anyhow!("Expected at least one set of parentheses."))?
|
||||||
|
.whitespace_after,
|
||||||
|
&inner
|
||||||
|
.rpar
|
||||||
|
.first()
|
||||||
|
.ok_or_else(|| anyhow::anyhow!("Expected at least one set of parentheses."))?
|
||||||
|
.whitespace_before,
|
||||||
|
),
|
||||||
|
Expression::List(inner) => (
|
||||||
|
&inner.elements,
|
||||||
|
&inner.lbracket.whitespace_after,
|
||||||
|
&inner.rbracket.whitespace_before,
|
||||||
|
),
|
||||||
|
_ => {
|
||||||
|
return Err(anyhow::anyhow!(
|
||||||
|
"Expected node to be: Expression::Tuple | Expression::List."
|
||||||
|
))
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
body.value = Expression::List(Box::new(List {
|
||||||
|
elements: elements.clone(),
|
||||||
|
lbracket: LeftSquareBracket {
|
||||||
|
whitespace_after: whitespace_after.clone(),
|
||||||
|
},
|
||||||
|
rbracket: RightSquareBracket {
|
||||||
|
whitespace_before: whitespace_before.clone(),
|
||||||
|
},
|
||||||
|
lpar: Default::default(),
|
||||||
|
rpar: Default::default(),
|
||||||
|
}));
|
||||||
|
|
||||||
|
let mut state = Default::default();
|
||||||
|
tree.codegen(&mut state);
|
||||||
|
|
||||||
|
Ok(Fix::replacement(
|
||||||
|
state.to_string(),
|
||||||
|
expr.location,
|
||||||
|
expr.end_location.unwrap(),
|
||||||
|
))
|
||||||
|
}
|
||||||
|
|
||||||
/// (C411) Convert `list([i for i in x])` to `[i for i in x]`.
|
/// (C411) Convert `list([i for i in x])` to `[i for i in x]`.
|
||||||
pub fn fix_unnecessary_list_call(locator: &SourceCodeLocator, expr: &Expr) -> Result<Fix> {
|
pub fn fix_unnecessary_list_call(locator: &SourceCodeLocator, expr: &Expr) -> Result<Fix> {
|
||||||
// Module(SimpleStatementLine(Expr(Call(List|Tuple)))) ->
|
// Module(SimpleStatementLine(Expr(Call(List|Tuple)))) ->
|
||||||
|
|
@ -393,12 +646,12 @@ pub fn fix_unnecessary_list_call(locator: &SourceCodeLocator, expr: &Expr) -> Re
|
||||||
"Expected node to be: SmallStatement::Expr."
|
"Expected node to be: SmallStatement::Expr."
|
||||||
));
|
));
|
||||||
};
|
};
|
||||||
let call = if let Expression::Call(call) = &mut body.value {
|
let call = if let Expression::Call(call) = &body.value {
|
||||||
call
|
call
|
||||||
} else {
|
} else {
|
||||||
return Err(anyhow::anyhow!("Expected node to be: Expression::Call."));
|
return Err(anyhow::anyhow!("Expected node to be: Expression::Call."));
|
||||||
};
|
};
|
||||||
let arg = if let Some(Arg { value, .. }) = call.args.first_mut() {
|
let arg = if let Some(Arg { value, .. }) = call.args.first() {
|
||||||
value
|
value
|
||||||
} else {
|
} else {
|
||||||
return Err(anyhow::anyhow!("Expected node to be: Arg."));
|
return Err(anyhow::anyhow!("Expected node to be: Arg."));
|
||||||
|
|
|
||||||
|
|
@ -9,5 +9,31 @@ expression: checks
|
||||||
end_location:
|
end_location:
|
||||||
row: 1
|
row: 1
|
||||||
column: 30
|
column: 30
|
||||||
fix: ~
|
fix:
|
||||||
|
patch:
|
||||||
|
content: "{x: x for x in range(3)}"
|
||||||
|
location:
|
||||||
|
row: 1
|
||||||
|
column: 0
|
||||||
|
end_location:
|
||||||
|
row: 1
|
||||||
|
column: 30
|
||||||
|
applied: false
|
||||||
|
- kind: UnnecessaryGeneratorDict
|
||||||
|
location:
|
||||||
|
row: 2
|
||||||
|
column: 0
|
||||||
|
end_location:
|
||||||
|
row: 4
|
||||||
|
column: 1
|
||||||
|
fix:
|
||||||
|
patch:
|
||||||
|
content: "{\n x: x for x in range(3)\n}"
|
||||||
|
location:
|
||||||
|
row: 2
|
||||||
|
column: 0
|
||||||
|
end_location:
|
||||||
|
row: 4
|
||||||
|
column: 1
|
||||||
|
applied: false
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -9,24 +9,87 @@ expression: checks
|
||||||
column: 5
|
column: 5
|
||||||
end_location:
|
end_location:
|
||||||
row: 1
|
row: 1
|
||||||
column: 18
|
column: 14
|
||||||
fix: ~
|
fix:
|
||||||
- kind:
|
patch:
|
||||||
UnnecessaryLiteralWithinTupleCall: tuple
|
content: ()
|
||||||
location:
|
location:
|
||||||
row: 2
|
row: 1
|
||||||
column: 5
|
column: 5
|
||||||
end_location:
|
end_location:
|
||||||
row: 2
|
row: 1
|
||||||
column: 18
|
column: 14
|
||||||
fix: ~
|
applied: false
|
||||||
- kind:
|
- kind:
|
||||||
UnnecessaryLiteralWithinTupleCall: list
|
UnnecessaryLiteralWithinTupleCall: list
|
||||||
|
location:
|
||||||
|
row: 2
|
||||||
|
column: 5
|
||||||
|
end_location:
|
||||||
|
row: 2
|
||||||
|
column: 18
|
||||||
|
fix:
|
||||||
|
patch:
|
||||||
|
content: "(1, 2)"
|
||||||
|
location:
|
||||||
|
row: 2
|
||||||
|
column: 5
|
||||||
|
end_location:
|
||||||
|
row: 2
|
||||||
|
column: 18
|
||||||
|
applied: false
|
||||||
|
- kind:
|
||||||
|
UnnecessaryLiteralWithinTupleCall: tuple
|
||||||
location:
|
location:
|
||||||
row: 3
|
row: 3
|
||||||
column: 5
|
column: 5
|
||||||
end_location:
|
end_location:
|
||||||
row: 3
|
row: 3
|
||||||
column: 14
|
column: 18
|
||||||
fix: ~
|
fix:
|
||||||
|
patch:
|
||||||
|
content: "(1, 2)"
|
||||||
|
location:
|
||||||
|
row: 3
|
||||||
|
column: 5
|
||||||
|
end_location:
|
||||||
|
row: 3
|
||||||
|
column: 18
|
||||||
|
applied: false
|
||||||
|
- kind:
|
||||||
|
UnnecessaryLiteralWithinTupleCall: list
|
||||||
|
location:
|
||||||
|
row: 4
|
||||||
|
column: 5
|
||||||
|
end_location:
|
||||||
|
row: 7
|
||||||
|
column: 2
|
||||||
|
fix:
|
||||||
|
patch:
|
||||||
|
content: "(\n 1,\n 2\n)"
|
||||||
|
location:
|
||||||
|
row: 4
|
||||||
|
column: 5
|
||||||
|
end_location:
|
||||||
|
row: 7
|
||||||
|
column: 2
|
||||||
|
applied: false
|
||||||
|
- kind:
|
||||||
|
UnnecessaryLiteralWithinTupleCall: tuple
|
||||||
|
location:
|
||||||
|
row: 8
|
||||||
|
column: 5
|
||||||
|
end_location:
|
||||||
|
row: 10
|
||||||
|
column: 1
|
||||||
|
fix:
|
||||||
|
patch:
|
||||||
|
content: "(1, 2)"
|
||||||
|
location:
|
||||||
|
row: 8
|
||||||
|
column: 5
|
||||||
|
end_location:
|
||||||
|
row: 10
|
||||||
|
column: 1
|
||||||
|
applied: false
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -10,7 +10,16 @@ expression: checks
|
||||||
end_location:
|
end_location:
|
||||||
row: 1
|
row: 1
|
||||||
column: 17
|
column: 17
|
||||||
fix: ~
|
fix:
|
||||||
|
patch:
|
||||||
|
content: "[1, 2]"
|
||||||
|
location:
|
||||||
|
row: 1
|
||||||
|
column: 5
|
||||||
|
end_location:
|
||||||
|
row: 1
|
||||||
|
column: 17
|
||||||
|
applied: false
|
||||||
- kind:
|
- kind:
|
||||||
UnnecessaryLiteralWithinListCall: tuple
|
UnnecessaryLiteralWithinListCall: tuple
|
||||||
location:
|
location:
|
||||||
|
|
@ -19,7 +28,16 @@ expression: checks
|
||||||
end_location:
|
end_location:
|
||||||
row: 2
|
row: 2
|
||||||
column: 17
|
column: 17
|
||||||
fix: ~
|
fix:
|
||||||
|
patch:
|
||||||
|
content: "[1, 2]"
|
||||||
|
location:
|
||||||
|
row: 2
|
||||||
|
column: 5
|
||||||
|
end_location:
|
||||||
|
row: 2
|
||||||
|
column: 17
|
||||||
|
applied: false
|
||||||
- kind:
|
- kind:
|
||||||
UnnecessaryLiteralWithinListCall: list
|
UnnecessaryLiteralWithinListCall: list
|
||||||
location:
|
location:
|
||||||
|
|
@ -28,7 +46,16 @@ expression: checks
|
||||||
end_location:
|
end_location:
|
||||||
row: 3
|
row: 3
|
||||||
column: 13
|
column: 13
|
||||||
fix: ~
|
fix:
|
||||||
|
patch:
|
||||||
|
content: "[]"
|
||||||
|
location:
|
||||||
|
row: 3
|
||||||
|
column: 5
|
||||||
|
end_location:
|
||||||
|
row: 3
|
||||||
|
column: 13
|
||||||
|
applied: false
|
||||||
- kind:
|
- kind:
|
||||||
UnnecessaryLiteralWithinListCall: tuple
|
UnnecessaryLiteralWithinListCall: tuple
|
||||||
location:
|
location:
|
||||||
|
|
@ -37,5 +64,14 @@ expression: checks
|
||||||
end_location:
|
end_location:
|
||||||
row: 4
|
row: 4
|
||||||
column: 13
|
column: 13
|
||||||
fix: ~
|
fix:
|
||||||
|
patch:
|
||||||
|
content: "[]"
|
||||||
|
location:
|
||||||
|
row: 4
|
||||||
|
column: 5
|
||||||
|
end_location:
|
||||||
|
row: 4
|
||||||
|
column: 13
|
||||||
|
applied: false
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue