mirror of https://github.com/astral-sh/ruff
[`pyupgrade`] Mark fixes for `convert-typed-dict-functional-to-class` and `convert-named-tuple-functional-to-class` as unsafe if they will remove comments (`UP013`, `UP014`) (#14842)
This commit is contained in:
parent
8d9e408dbb
commit
9b8ceb9a2e
|
|
@ -41,3 +41,8 @@ MyType = TypedDict("MyType", {})
|
|||
|
||||
# Empty dict call
|
||||
MyType = TypedDict("MyType", dict())
|
||||
|
||||
# Unsafe fix if comments are present
|
||||
X = TypedDict("X", {
|
||||
"some_config": int, # important
|
||||
})
|
||||
|
|
|
|||
|
|
@ -31,3 +31,9 @@ S3File = NamedTuple(
|
|||
("dataHPK",* str),
|
||||
],
|
||||
)
|
||||
|
||||
# Unsafe fix if comments are present
|
||||
X = NamedTuple("X", [
|
||||
("some_config", int), # important
|
||||
])
|
||||
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
use log::debug;
|
||||
|
||||
use ruff_diagnostics::{Diagnostic, Edit, Fix, FixAvailability, Violation};
|
||||
use ruff_diagnostics::{Applicability, Diagnostic, Edit, Fix, FixAvailability, Violation};
|
||||
use ruff_macros::{derive_message_formats, ViolationMetadata};
|
||||
use ruff_python_ast::helpers::is_dunder;
|
||||
use ruff_python_ast::name::Name;
|
||||
|
|
@ -8,6 +8,7 @@ use ruff_python_ast::{self as ast, Arguments, Expr, ExprContext, Identifier, Key
|
|||
use ruff_python_codegen::Generator;
|
||||
use ruff_python_semantic::SemanticModel;
|
||||
use ruff_python_stdlib::identifiers::is_identifier;
|
||||
use ruff_python_trivia::CommentRanges;
|
||||
use ruff_source_file::LineRanges;
|
||||
use ruff_text_size::{Ranged, TextRange};
|
||||
|
||||
|
|
@ -41,6 +42,11 @@ use crate::checkers::ast::Checker;
|
|||
/// b: str
|
||||
/// ```
|
||||
///
|
||||
/// ## Fix safety
|
||||
/// This rule's fix is marked as unsafe if there are any comments within the
|
||||
/// range of the `NamedTuple` definition, as these will be dropped by the
|
||||
/// autofix.
|
||||
///
|
||||
/// ## References
|
||||
/// - [Python documentation: `typing.NamedTuple`](https://docs.python.org/3/library/typing.html#typing.NamedTuple)
|
||||
#[derive(ViolationMetadata)]
|
||||
|
|
@ -121,6 +127,7 @@ pub(crate) fn convert_named_tuple_functional_to_class(
|
|||
fields,
|
||||
base_class,
|
||||
checker.generator(),
|
||||
checker.comment_ranges(),
|
||||
));
|
||||
}
|
||||
checker.diagnostics.push(diagnostic);
|
||||
|
|
@ -241,9 +248,17 @@ fn convert_to_class(
|
|||
body: Vec<Stmt>,
|
||||
base_class: &Expr,
|
||||
generator: Generator,
|
||||
comment_ranges: &CommentRanges,
|
||||
) -> Fix {
|
||||
Fix::safe_edit(Edit::range_replacement(
|
||||
generator.stmt(&create_class_def_stmt(typename, body, base_class)),
|
||||
stmt.range(),
|
||||
))
|
||||
Fix::applicable_edit(
|
||||
Edit::range_replacement(
|
||||
generator.stmt(&create_class_def_stmt(typename, body, base_class)),
|
||||
stmt.range(),
|
||||
),
|
||||
if comment_ranges.intersects(stmt.range()) {
|
||||
Applicability::Unsafe
|
||||
} else {
|
||||
Applicability::Safe
|
||||
},
|
||||
)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,10 +1,11 @@
|
|||
use ruff_diagnostics::{Diagnostic, Edit, Fix, FixAvailability, Violation};
|
||||
use ruff_diagnostics::{Applicability, Diagnostic, Edit, Fix, FixAvailability, Violation};
|
||||
use ruff_macros::{derive_message_formats, ViolationMetadata};
|
||||
use ruff_python_ast::helpers::is_dunder;
|
||||
use ruff_python_ast::{self as ast, Arguments, Expr, ExprContext, Identifier, Keyword, Stmt};
|
||||
use ruff_python_codegen::Generator;
|
||||
use ruff_python_semantic::SemanticModel;
|
||||
use ruff_python_stdlib::identifiers::is_identifier;
|
||||
use ruff_python_trivia::CommentRanges;
|
||||
use ruff_source_file::LineRanges;
|
||||
use ruff_text_size::{Ranged, TextRange};
|
||||
|
||||
|
|
@ -37,6 +38,11 @@ use crate::checkers::ast::Checker;
|
|||
/// b: str
|
||||
/// ```
|
||||
///
|
||||
/// ## Fix safety
|
||||
/// This rule's fix is marked as unsafe if there are any comments within the
|
||||
/// range of the `TypedDict` definition, as these will be dropped by the
|
||||
/// autofix.
|
||||
///
|
||||
/// ## References
|
||||
/// - [Python documentation: `typing.TypedDict`](https://docs.python.org/3/library/typing.html#typing.TypedDict)
|
||||
#[derive(ViolationMetadata)]
|
||||
|
|
@ -91,6 +97,7 @@ pub(crate) fn convert_typed_dict_functional_to_class(
|
|||
total_keyword,
|
||||
base_class,
|
||||
checker.generator(),
|
||||
checker.comment_ranges(),
|
||||
));
|
||||
}
|
||||
checker.diagnostics.push(diagnostic);
|
||||
|
|
@ -265,14 +272,22 @@ fn convert_to_class(
|
|||
total_keyword: Option<&Keyword>,
|
||||
base_class: &Expr,
|
||||
generator: Generator,
|
||||
comment_ranges: &CommentRanges,
|
||||
) -> Fix {
|
||||
Fix::safe_edit(Edit::range_replacement(
|
||||
generator.stmt(&create_class_def_stmt(
|
||||
class_name,
|
||||
body,
|
||||
total_keyword,
|
||||
base_class,
|
||||
)),
|
||||
stmt.range(),
|
||||
))
|
||||
Fix::applicable_edit(
|
||||
Edit::range_replacement(
|
||||
generator.stmt(&create_class_def_stmt(
|
||||
class_name,
|
||||
body,
|
||||
total_keyword,
|
||||
base_class,
|
||||
)),
|
||||
stmt.range(),
|
||||
),
|
||||
if comment_ranges.intersects(stmt.range()) {
|
||||
Applicability::Unsafe
|
||||
} else {
|
||||
Applicability::Safe
|
||||
},
|
||||
)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,6 +1,5 @@
|
|||
---
|
||||
source: crates/ruff_linter/src/rules/pyupgrade/mod.rs
|
||||
snapshot_kind: text
|
||||
---
|
||||
UP013.py:5:1: UP013 [*] Convert `MyType` from `TypedDict` functional to class syntax
|
||||
|
|
||||
|
|
@ -242,6 +241,8 @@ UP013.py:43:1: UP013 [*] Convert `MyType` from `TypedDict` functional to class s
|
|||
42 | # Empty dict call
|
||||
43 | MyType = TypedDict("MyType", dict())
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ UP013
|
||||
44 |
|
||||
45 | # Unsafe fix if comments are present
|
||||
|
|
||||
= help: Convert `MyType` to class syntax
|
||||
|
||||
|
|
@ -252,3 +253,26 @@ UP013.py:43:1: UP013 [*] Convert `MyType` from `TypedDict` functional to class s
|
|||
43 |-MyType = TypedDict("MyType", dict())
|
||||
43 |+class MyType(TypedDict):
|
||||
44 |+ pass
|
||||
44 45 |
|
||||
45 46 | # Unsafe fix if comments are present
|
||||
46 47 | X = TypedDict("X", {
|
||||
|
||||
UP013.py:46:1: UP013 [*] Convert `X` from `TypedDict` functional to class syntax
|
||||
|
|
||||
45 | # Unsafe fix if comments are present
|
||||
46 | / X = TypedDict("X", {
|
||||
47 | | "some_config": int, # important
|
||||
48 | | })
|
||||
| |__^ UP013
|
||||
|
|
||||
= help: Convert `X` to class syntax
|
||||
|
||||
ℹ Unsafe fix
|
||||
43 43 | MyType = TypedDict("MyType", dict())
|
||||
44 44 |
|
||||
45 45 | # Unsafe fix if comments are present
|
||||
46 |-X = TypedDict("X", {
|
||||
47 |- "some_config": int, # important
|
||||
48 |-})
|
||||
46 |+class X(TypedDict):
|
||||
47 |+ some_config: int
|
||||
|
|
|
|||
|
|
@ -1,6 +1,5 @@
|
|||
---
|
||||
source: crates/ruff_linter/src/rules/pyupgrade/mod.rs
|
||||
snapshot_kind: text
|
||||
---
|
||||
UP014.py:5:1: UP014 [*] Convert `MyType` from `NamedTuple` functional to class syntax
|
||||
|
|
||||
|
|
@ -109,3 +108,24 @@ UP014.py:20:1: UP014 [*] Convert `MyType` from `NamedTuple` functional to class
|
|||
21 23 |
|
||||
22 24 | # unfixable
|
||||
23 25 | MyType = typing.NamedTuple("MyType", [("a", int)], [("b", str)])
|
||||
|
||||
UP014.py:36:1: UP014 [*] Convert `X` from `NamedTuple` functional to class syntax
|
||||
|
|
||||
35 | # Unsafe fix if comments are present
|
||||
36 | / X = NamedTuple("X", [
|
||||
37 | | ("some_config", int), # important
|
||||
38 | | ])
|
||||
| |__^ UP014
|
||||
|
|
||||
= help: Convert `X` to class syntax
|
||||
|
||||
ℹ Unsafe fix
|
||||
33 33 | )
|
||||
34 34 |
|
||||
35 35 | # Unsafe fix if comments are present
|
||||
36 |-X = NamedTuple("X", [
|
||||
37 |- ("some_config", int), # important
|
||||
38 |-])
|
||||
36 |+class X(NamedTuple):
|
||||
37 |+ some_config: int
|
||||
39 38 |
|
||||
|
|
|
|||
Loading…
Reference in New Issue