mirror of https://github.com/astral-sh/ruff
[`pyupgrade`] Handle end-of-line comments for `quoted-annotation` (`UP037`) (#15824)
This PR uses the tokens of the parsed annotation available in the `Checker`, instead of re-lexing (using `SimpleTokenizer`) the annotation. This avoids some limitations of the `SimpleTokenizer`, such as not being able to handle number and string literals. Closes #15816 .
This commit is contained in:
parent
7a10a40b0d
commit
56f956a238
|
|
@ -106,3 +106,17 @@ x = TypeVar("x", "str", "int")
|
||||||
x = cast("str", x)
|
x = cast("str", x)
|
||||||
|
|
||||||
X = List["MyClass"]
|
X = List["MyClass"]
|
||||||
|
|
||||||
|
# Handle end of line comment in string annotation
|
||||||
|
# See https://github.com/astral-sh/ruff/issues/15816
|
||||||
|
def f() -> "Literal[0]#":
|
||||||
|
return 0
|
||||||
|
|
||||||
|
def g(x: "Literal['abc']#") -> None:
|
||||||
|
return
|
||||||
|
|
||||||
|
def f() -> """Literal[0]
|
||||||
|
#
|
||||||
|
|
||||||
|
""":
|
||||||
|
return 0
|
||||||
|
|
|
||||||
|
|
@ -1,3 +1,4 @@
|
||||||
|
use ruff_python_parser::TokenKind;
|
||||||
use ruff_text_size::{TextLen, TextRange, TextSize};
|
use ruff_text_size::{TextLen, TextRange, TextSize};
|
||||||
|
|
||||||
use crate::checkers::ast::Checker;
|
use crate::checkers::ast::Checker;
|
||||||
|
|
@ -5,7 +6,6 @@ use ruff_diagnostics::{AlwaysFixableViolation, Diagnostic, Edit, Fix};
|
||||||
use ruff_macros::{derive_message_formats, ViolationMetadata};
|
use ruff_macros::{derive_message_formats, ViolationMetadata};
|
||||||
use ruff_python_ast::Stmt;
|
use ruff_python_ast::Stmt;
|
||||||
use ruff_python_semantic::SemanticModel;
|
use ruff_python_semantic::SemanticModel;
|
||||||
use ruff_python_trivia::{SimpleToken, SimpleTokenKind, SimpleTokenizer};
|
|
||||||
use ruff_source_file::LineRanges;
|
use ruff_source_file::LineRanges;
|
||||||
|
|
||||||
/// ## What it does
|
/// ## What it does
|
||||||
|
|
@ -87,14 +87,12 @@ pub(crate) fn quoted_annotation(checker: &mut Checker, annotation: &str, range:
|
||||||
let placeholder_range = TextRange::up_to(annotation.text_len());
|
let placeholder_range = TextRange::up_to(annotation.text_len());
|
||||||
let spans_multiple_lines = annotation.contains_line_break(placeholder_range);
|
let spans_multiple_lines = annotation.contains_line_break(placeholder_range);
|
||||||
|
|
||||||
let tokenizer = SimpleTokenizer::new(annotation, placeholder_range);
|
let last_token_is_comment = checker
|
||||||
let last_token_is_comment = matches!(
|
.tokens()
|
||||||
tokenizer.last(),
|
// The actual last token will always be a logical newline,
|
||||||
Some(SimpleToken {
|
// so we check the second to last
|
||||||
kind: SimpleTokenKind::Comment,
|
.get(checker.tokens().len().saturating_sub(2))
|
||||||
..
|
.is_some_and(|tok| tok.kind() == TokenKind::Comment);
|
||||||
})
|
|
||||||
);
|
|
||||||
|
|
||||||
let new_content = match (spans_multiple_lines, last_token_is_comment) {
|
let new_content = match (spans_multiple_lines, last_token_is_comment) {
|
||||||
(_, false) if in_parameter_annotation(range.start(), checker.semantic()) => {
|
(_, false) if in_parameter_annotation(range.start(), checker.semantic()) => {
|
||||||
|
|
|
||||||
|
|
@ -554,4 +554,72 @@ UP037_0.py:67:45: UP037 [*] Remove quotes from type annotation
|
||||||
67 |+x: NamedTuple(typename="X", fields=[("foo", int)])
|
67 |+x: NamedTuple(typename="X", fields=[("foo", int)])
|
||||||
68 68 |
|
68 68 |
|
||||||
69 69 | X: MyCallable("X")
|
69 69 | X: MyCallable("X")
|
||||||
70 70 |
|
70 70 |
|
||||||
|
|
||||||
|
UP037_0.py:112:12: UP037 [*] Remove quotes from type annotation
|
||||||
|
|
|
||||||
|
110 | # Handle end of line comment in string annotation
|
||||||
|
111 | # See https://github.com/astral-sh/ruff/issues/15816
|
||||||
|
112 | def f() -> "Literal[0]#":
|
||||||
|
| ^^^^^^^^^^^^^ UP037
|
||||||
|
113 | return 0
|
||||||
|
|
|
||||||
|
= help: Remove quotes
|
||||||
|
|
||||||
|
ℹ Safe fix
|
||||||
|
109 109 |
|
||||||
|
110 110 | # Handle end of line comment in string annotation
|
||||||
|
111 111 | # See https://github.com/astral-sh/ruff/issues/15816
|
||||||
|
112 |-def f() -> "Literal[0]#":
|
||||||
|
112 |+def f() -> (Literal[0]#
|
||||||
|
113 |+):
|
||||||
|
113 114 | return 0
|
||||||
|
114 115 |
|
||||||
|
115 116 | def g(x: "Literal['abc']#") -> None:
|
||||||
|
|
||||||
|
UP037_0.py:115:10: UP037 [*] Remove quotes from type annotation
|
||||||
|
|
|
||||||
|
113 | return 0
|
||||||
|
114 |
|
||||||
|
115 | def g(x: "Literal['abc']#") -> None:
|
||||||
|
| ^^^^^^^^^^^^^^^^^ UP037
|
||||||
|
116 | return
|
||||||
|
|
|
||||||
|
= help: Remove quotes
|
||||||
|
|
||||||
|
ℹ Safe fix
|
||||||
|
112 112 | def f() -> "Literal[0]#":
|
||||||
|
113 113 | return 0
|
||||||
|
114 114 |
|
||||||
|
115 |-def g(x: "Literal['abc']#") -> None:
|
||||||
|
115 |+def g(x: (Literal['abc']#
|
||||||
|
116 |+)) -> None:
|
||||||
|
116 117 | return
|
||||||
|
117 118 |
|
||||||
|
118 119 | def f() -> """Literal[0]
|
||||||
|
|
||||||
|
UP037_0.py:118:12: UP037 [*] Remove quotes from type annotation
|
||||||
|
|
|
||||||
|
116 | return
|
||||||
|
117 |
|
||||||
|
118 | def f() -> """Literal[0]
|
||||||
|
| ____________^
|
||||||
|
119 | | #
|
||||||
|
120 | |
|
||||||
|
121 | | """:
|
||||||
|
| |_______^ UP037
|
||||||
|
122 | return 0
|
||||||
|
|
|
||||||
|
= help: Remove quotes
|
||||||
|
|
||||||
|
ℹ Safe fix
|
||||||
|
115 115 | def g(x: "Literal['abc']#") -> None:
|
||||||
|
116 116 | return
|
||||||
|
117 117 |
|
||||||
|
118 |-def f() -> """Literal[0]
|
||||||
|
118 |+def f() -> (Literal[0]
|
||||||
|
119 119 | #
|
||||||
|
120 120 |
|
||||||
|
121 |- """:
|
||||||
|
121 |+ ):
|
||||||
|
122 122 | return 0
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue