mirror of
https://github.com/astral-sh/ruff
synced 2026-01-23 22:40:48 -05:00
Pyupgrade: Extraneous parenthesis (#1926)
This commit is contained in:
@@ -54,6 +54,7 @@ mod tests {
|
||||
#[test_case(Rule::FormatLiterals, Path::new("UP030_1.py"); "UP030_1")]
|
||||
#[test_case(Rule::FString, Path::new("UP032.py"); "UP032")]
|
||||
#[test_case(Rule::FunctoolsCache, Path::new("UP033.py"); "UP033")]
|
||||
#[test_case(Rule::ExtraneousParentheses, Path::new("UP034.py"); "UP034")]
|
||||
fn rules(rule_code: Rule, path: &Path) -> Result<()> {
|
||||
let snapshot = format!("{}_{}", rule_code.code(), path.to_string_lossy());
|
||||
let diagnostics = test_path(
|
||||
|
||||
143
src/rules/pyupgrade/rules/extraneous_parentheses.rs
Normal file
143
src/rules/pyupgrade/rules/extraneous_parentheses.rs
Normal file
@@ -0,0 +1,143 @@
|
||||
use rustpython_parser::lexer::{LexResult, Tok};
|
||||
|
||||
use crate::ast::types::Range;
|
||||
use crate::fix::Fix;
|
||||
use crate::registry::{Diagnostic, Rule};
|
||||
use crate::settings::{flags, Settings};
|
||||
use crate::source_code::Locator;
|
||||
use crate::violations;
|
||||
|
||||
// See: https://github.com/asottile/pyupgrade/blob/97ed6fb3cf2e650d4f762ba231c3f04c41797710/pyupgrade/_main.py#L148
|
||||
fn match_extraneous_parentheses(tokens: &[LexResult], mut i: usize) -> Option<(usize, usize)> {
|
||||
i += 1;
|
||||
|
||||
loop {
|
||||
if i >= tokens.len() {
|
||||
return None;
|
||||
}
|
||||
let Ok((_, tok, _)) = &tokens[i] else {
|
||||
return None;
|
||||
};
|
||||
match tok {
|
||||
Tok::Comment(..) | Tok::NonLogicalNewline => {
|
||||
i += 1;
|
||||
}
|
||||
Tok::Lpar => {
|
||||
break;
|
||||
}
|
||||
_ => {
|
||||
return None;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Store the location of the extraneous opening parenthesis.
|
||||
let start = i;
|
||||
|
||||
// Verify that we're not in a tuple or coroutine.
|
||||
let mut depth = 1;
|
||||
while depth > 0 {
|
||||
i += 1;
|
||||
if i >= tokens.len() {
|
||||
return None;
|
||||
}
|
||||
let Ok((_, tok, _)) = &tokens[i] else {
|
||||
return None;
|
||||
};
|
||||
|
||||
// If we find a comma or a yield at depth 1 or 2, it's a tuple or coroutine.
|
||||
if depth == 1 && matches!(tok, Tok::Comma | Tok::Yield) {
|
||||
return None;
|
||||
} else if matches!(tok, Tok::Lpar | Tok::Lbrace | Tok::Lsqb) {
|
||||
depth += 1;
|
||||
} else if matches!(tok, Tok::Rpar | Tok::Rbrace | Tok::Rsqb) {
|
||||
depth -= 1;
|
||||
}
|
||||
}
|
||||
|
||||
// Store the location of the extraneous closing parenthesis.
|
||||
let end = i;
|
||||
|
||||
// Verify that we're not in an empty tuple.
|
||||
if (start + 1..i).all(|i| {
|
||||
matches!(
|
||||
tokens[i],
|
||||
Ok((_, Tok::Comment(..) | Tok::NonLogicalNewline, _))
|
||||
)
|
||||
}) {
|
||||
return None;
|
||||
}
|
||||
|
||||
// Find the next non-coding token.
|
||||
i += 1;
|
||||
loop {
|
||||
if i >= tokens.len() {
|
||||
return None;
|
||||
}
|
||||
let Ok((_, tok, _)) = &tokens[i] else {
|
||||
return None;
|
||||
};
|
||||
match tok {
|
||||
Tok::Comment(..) | Tok::NonLogicalNewline => {
|
||||
i += 1;
|
||||
}
|
||||
_ => {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if i >= tokens.len() {
|
||||
return None;
|
||||
}
|
||||
let Ok((_, tok, _)) = &tokens[i] else {
|
||||
return None;
|
||||
};
|
||||
if matches!(tok, Tok::Rpar) {
|
||||
Some((start, end))
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
/// UP034
|
||||
pub fn extraneous_parentheses(
|
||||
tokens: &[LexResult],
|
||||
locator: &Locator,
|
||||
settings: &Settings,
|
||||
autofix: flags::Autofix,
|
||||
) -> Vec<Diagnostic> {
|
||||
let mut diagnostics = vec![];
|
||||
let mut i = 0;
|
||||
while i < tokens.len() {
|
||||
if matches!(tokens[i], Ok((_, Tok::Lpar, _))) {
|
||||
if let Some((start, end)) = match_extraneous_parentheses(tokens, i) {
|
||||
i = end + 1;
|
||||
let Ok((start, ..)) = &tokens[start] else {
|
||||
return diagnostics;
|
||||
};
|
||||
let Ok((.., end)) = &tokens[end] else {
|
||||
return diagnostics;
|
||||
};
|
||||
let mut diagnostic =
|
||||
Diagnostic::new(violations::ExtraneousParentheses, Range::new(*start, *end));
|
||||
if matches!(autofix, flags::Autofix::Enabled)
|
||||
&& settings.rules.should_fix(&Rule::ExtraneousParentheses)
|
||||
{
|
||||
let contents = locator.slice_source_code_range(&Range::new(*start, *end));
|
||||
diagnostic.amend(Fix::replacement(
|
||||
contents[1..contents.len() - 1].to_string(),
|
||||
*start,
|
||||
*end,
|
||||
));
|
||||
}
|
||||
diagnostics.push(diagnostic);
|
||||
} else {
|
||||
i += 1;
|
||||
}
|
||||
} else {
|
||||
i += 1;
|
||||
}
|
||||
}
|
||||
diagnostics
|
||||
}
|
||||
@@ -2,6 +2,7 @@ pub(crate) use convert_named_tuple_functional_to_class::convert_named_tuple_func
|
||||
pub(crate) use convert_typed_dict_functional_to_class::convert_typed_dict_functional_to_class;
|
||||
pub(crate) use datetime_utc_alias::datetime_utc_alias;
|
||||
pub(crate) use deprecated_unittest_alias::deprecated_unittest_alias;
|
||||
pub(crate) use extraneous_parentheses::extraneous_parentheses;
|
||||
pub(crate) use f_strings::f_strings;
|
||||
pub(crate) use format_literals::format_literals;
|
||||
pub(crate) use functools_cache::functools_cache;
|
||||
@@ -43,6 +44,7 @@ mod convert_named_tuple_functional_to_class;
|
||||
mod convert_typed_dict_functional_to_class;
|
||||
mod datetime_utc_alias;
|
||||
mod deprecated_unittest_alias;
|
||||
mod extraneous_parentheses;
|
||||
mod f_strings;
|
||||
mod format_literals;
|
||||
mod functools_cache;
|
||||
|
||||
@@ -0,0 +1,175 @@
|
||||
---
|
||||
source: src/rules/pyupgrade/mod.rs
|
||||
expression: diagnostics
|
||||
---
|
||||
- kind:
|
||||
ExtraneousParentheses: ~
|
||||
location:
|
||||
row: 2
|
||||
column: 6
|
||||
end_location:
|
||||
row: 2
|
||||
column: 13
|
||||
fix:
|
||||
content: "\"foo\""
|
||||
location:
|
||||
row: 2
|
||||
column: 6
|
||||
end_location:
|
||||
row: 2
|
||||
column: 13
|
||||
parent: ~
|
||||
- kind:
|
||||
ExtraneousParentheses: ~
|
||||
location:
|
||||
row: 5
|
||||
column: 6
|
||||
end_location:
|
||||
row: 5
|
||||
column: 26
|
||||
fix:
|
||||
content: "\"hell((goodybe))o\""
|
||||
location:
|
||||
row: 5
|
||||
column: 6
|
||||
end_location:
|
||||
row: 5
|
||||
column: 26
|
||||
parent: ~
|
||||
- kind:
|
||||
ExtraneousParentheses: ~
|
||||
location:
|
||||
row: 8
|
||||
column: 6
|
||||
end_location:
|
||||
row: 8
|
||||
column: 15
|
||||
fix:
|
||||
content: "(\"foo\")"
|
||||
location:
|
||||
row: 8
|
||||
column: 6
|
||||
end_location:
|
||||
row: 8
|
||||
column: 15
|
||||
parent: ~
|
||||
- kind:
|
||||
ExtraneousParentheses: ~
|
||||
location:
|
||||
row: 11
|
||||
column: 6
|
||||
end_location:
|
||||
row: 11
|
||||
column: 13
|
||||
fix:
|
||||
content: ((1))
|
||||
location:
|
||||
row: 11
|
||||
column: 6
|
||||
end_location:
|
||||
row: 11
|
||||
column: 13
|
||||
parent: ~
|
||||
- kind:
|
||||
ExtraneousParentheses: ~
|
||||
location:
|
||||
row: 14
|
||||
column: 6
|
||||
end_location:
|
||||
row: 14
|
||||
column: 25
|
||||
fix:
|
||||
content: "\"foo{}\".format(1)"
|
||||
location:
|
||||
row: 14
|
||||
column: 6
|
||||
end_location:
|
||||
row: 14
|
||||
column: 25
|
||||
parent: ~
|
||||
- kind:
|
||||
ExtraneousParentheses: ~
|
||||
location:
|
||||
row: 18
|
||||
column: 4
|
||||
end_location:
|
||||
row: 18
|
||||
column: 23
|
||||
fix:
|
||||
content: "\"foo{}\".format(1)"
|
||||
location:
|
||||
row: 18
|
||||
column: 4
|
||||
end_location:
|
||||
row: 18
|
||||
column: 23
|
||||
parent: ~
|
||||
- kind:
|
||||
ExtraneousParentheses: ~
|
||||
location:
|
||||
row: 23
|
||||
column: 4
|
||||
end_location:
|
||||
row: 25
|
||||
column: 5
|
||||
fix:
|
||||
content: "\n \"foo\"\n "
|
||||
location:
|
||||
row: 23
|
||||
column: 4
|
||||
end_location:
|
||||
row: 25
|
||||
column: 5
|
||||
parent: ~
|
||||
- kind:
|
||||
ExtraneousParentheses: ~
|
||||
location:
|
||||
row: 30
|
||||
column: 12
|
||||
end_location:
|
||||
row: 30
|
||||
column: 23
|
||||
fix:
|
||||
content: (yield 1)
|
||||
location:
|
||||
row: 30
|
||||
column: 12
|
||||
end_location:
|
||||
row: 30
|
||||
column: 23
|
||||
parent: ~
|
||||
- kind:
|
||||
ExtraneousParentheses: ~
|
||||
location:
|
||||
row: 35
|
||||
column: 8
|
||||
end_location:
|
||||
row: 35
|
||||
column: 27
|
||||
fix:
|
||||
content: "\"foo{}\".format(1)"
|
||||
location:
|
||||
row: 35
|
||||
column: 8
|
||||
end_location:
|
||||
row: 35
|
||||
column: 27
|
||||
parent: ~
|
||||
- kind:
|
||||
ExtraneousParentheses: ~
|
||||
location:
|
||||
row: 39
|
||||
column: 6
|
||||
end_location:
|
||||
row: 39
|
||||
column: 27
|
||||
fix:
|
||||
content: x for x in range(3)
|
||||
location:
|
||||
row: 39
|
||||
column: 6
|
||||
end_location:
|
||||
row: 39
|
||||
column: 27
|
||||
parent: ~
|
||||
|
||||
Reference in New Issue
Block a user