RUF031: Ignore unparenthesized tuples in subscripts when the subscript is obviously a type annotation or type alias (#12762)

This commit is contained in:
Alex Waygood 2024-08-09 20:31:27 +01:00 committed by GitHub
parent c4e651921b
commit 83db48d316
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
4 changed files with 42 additions and 4 deletions

View file

@ -33,4 +33,12 @@ d[1,2,:]
# Should keep these parentheses in
# Python <=3.10 to avoid syntax error.
# https://github.com/astral-sh/ruff/issues/12776
d[(*foo,bar)]
d[(*foo,bar)]
x: dict[str, int] # tuples inside type annotations should never be altered
import typing
type Y = typing.Literal[1, 2]
Z: typing.TypeAlias = dict[int, int]
class Foo(dict[str, int]): pass

View file

@ -33,4 +33,12 @@ d[1,2,:]
# Should keep these parentheses in
# Python <=3.10 to avoid syntax error.
# https://github.com/astral-sh/ruff/issues/12776
d[(*foo,bar)]
d[(*foo,bar)]
x: dict[str, int] # tuples inside type annotations should never be altered
import typing
type Y = typing.Literal[1, 2]
Z: typing.TypeAlias = dict[int, int]
class Foo(dict[str, int]): pass

View file

@ -13,6 +13,10 @@ use crate::{checkers::ast::Checker, settings::types::PythonVersion};
/// [`lint.ruff.parenthesize-tuple-in-subscript`]. By default, the use of
/// parentheses is considered a violation.
///
/// This rule is not applied inside "typing contexts" (type annotations,
/// type aliases and subscripted class bases), as these have their own specific
/// conventions around them.
///
/// ## Why is this bad?
/// It is good to be consistent and, depending on the codebase, one or the other
/// convention may be preferred.
@ -58,16 +62,20 @@ impl AlwaysFixableViolation for IncorrectlyParenthesizedTupleInSubscript {
/// RUF031
pub(crate) fn subscript_with_parenthesized_tuple(checker: &mut Checker, subscript: &ExprSubscript) {
let prefer_parentheses = checker.settings.ruff.parenthesize_tuple_in_subscript;
let Some(tuple_subscript) = subscript.slice.as_tuple_expr() else {
return;
};
if tuple_subscript.parenthesized == prefer_parentheses || tuple_subscript.elts.is_empty() {
return;
}
// Adding parentheses in the presence of a slice leads to a syntax error.
if prefer_parentheses && tuple_subscript.elts.iter().any(Expr::is_slice_expr) {
return;
}
// Removing parentheses in the presence of unpacking leads
// to a syntax error in Python 3.10.
// This is no longer a syntax error starting in Python 3.11
@ -78,6 +86,14 @@ pub(crate) fn subscript_with_parenthesized_tuple(checker: &mut Checker, subscrip
{
return;
}
// subscripts in annotations, type definitions or class bases are typing subscripts.
// These have their own special conventions; skip applying the rule in these cases.
let semantic = checker.semantic();
if semantic.in_annotation() || semantic.in_type_definition() || semantic.in_class_base() {
return;
}
let locator = checker.locator();
let source_range = subscript.slice.range();
let new_source = if prefer_parentheses {
@ -86,6 +102,7 @@ pub(crate) fn subscript_with_parenthesized_tuple(checker: &mut Checker, subscrip
locator.slice(source_range)[1..source_range.len().to_usize() - 1].to_string()
};
let edit = Edit::range_replacement(new_source, source_range);
checker.diagnostics.push(
Diagnostic::new(
IncorrectlyParenthesizedTupleInSubscript { prefer_parentheses },

View file

@ -174,8 +174,10 @@ RUF031.py:36:3: RUF031 [*] Avoid parentheses for tuples in subscripts.
|
34 | # Python <=3.10 to avoid syntax error.
35 | # https://github.com/astral-sh/ruff/issues/12776
36 | d[(*foo,bar)]
36 | d[(*foo,bar)]
| ^^^^^^^^^^ RUF031
37 |
38 | x: dict[str, int] # tuples inside type annotations should never be altered
|
= help: Remove the parentheses.
@ -183,5 +185,8 @@ RUF031.py:36:3: RUF031 [*] Avoid parentheses for tuples in subscripts.
33 33 | # Should keep these parentheses in
34 34 | # Python <=3.10 to avoid syntax error.
35 35 | # https://github.com/astral-sh/ruff/issues/12776
36 |-d[(*foo,bar)]
36 |-d[(*foo,bar)]
36 |+d[*foo,bar]
37 37 |
38 38 | x: dict[str, int] # tuples inside type annotations should never be altered
39 39 |