mirror of
https://github.com/astral-sh/ruff.git
synced 2025-08-04 02:38:25 +00:00
[pyupgrade
] Comments within parenthesized value ranges should not affect applicability (UP040
) (#16027)
## Summary Follow-up to #16026. Previously, the fix for this would be marked as unsafe, even though all comments are preserved: ```python # .pyi T: TypeAlias = ( # Comment int | str ) ``` Now it is safe: comments within the parenthesized range no longer affect applicability. ## Test Plan `cargo nextest run` and `cargo insta test`. --------- Co-authored-by: Dylan <53534755+dylwil3@users.noreply.github.com>
This commit is contained in:
parent
19f3424a1a
commit
a29009e4ed
3 changed files with 67 additions and 22 deletions
|
@ -12,3 +12,13 @@ x: TypeAlias = tuple[
|
|||
int, # preserved
|
||||
float,
|
||||
]
|
||||
|
||||
T: TypeAlias = ( # comment0
|
||||
# comment1
|
||||
int # comment2
|
||||
# comment3
|
||||
| # comment4
|
||||
# comment5
|
||||
str # comment6
|
||||
# comment7
|
||||
) # comment8
|
||||
|
|
|
@ -170,25 +170,12 @@ pub(crate) fn non_pep695_type_alias_type(checker: &Checker, stmt: &StmtAssign) {
|
|||
return;
|
||||
};
|
||||
|
||||
// it would be easier to check for comments in the whole `stmt.range`, but because
|
||||
// `create_diagnostic` uses the full source text of `value`, comments within `value` are
|
||||
// actually preserved. thus, we have to check for comments in `stmt` but outside of `value`
|
||||
let pre_value = TextRange::new(stmt.start(), value.start());
|
||||
let post_value = TextRange::new(value.end(), stmt.end());
|
||||
let comment_ranges = checker.comment_ranges();
|
||||
let safety = if comment_ranges.intersects(pre_value) || comment_ranges.intersects(post_value) {
|
||||
Applicability::Unsafe
|
||||
} else {
|
||||
Applicability::Safe
|
||||
};
|
||||
|
||||
checker.report_diagnostic(create_diagnostic(
|
||||
checker,
|
||||
stmt.into(),
|
||||
&target_name.id,
|
||||
value,
|
||||
&vars,
|
||||
safety,
|
||||
TypeAliasKind::TypeAliasType,
|
||||
));
|
||||
}
|
||||
|
@ -248,13 +235,6 @@ pub(crate) fn non_pep695_type_alias(checker: &Checker, stmt: &StmtAnnAssign) {
|
|||
name,
|
||||
value,
|
||||
&vars,
|
||||
// The fix is only safe in a type stub because new-style aliases have different runtime behavior
|
||||
// See https://github.com/astral-sh/ruff/issues/6434
|
||||
if checker.source_type.is_stub() {
|
||||
Applicability::Safe
|
||||
} else {
|
||||
Applicability::Unsafe
|
||||
},
|
||||
TypeAliasKind::TypeAlias,
|
||||
));
|
||||
}
|
||||
|
@ -266,19 +246,45 @@ fn create_diagnostic(
|
|||
name: &Name,
|
||||
value: &Expr,
|
||||
type_vars: &[TypeVar],
|
||||
applicability: Applicability,
|
||||
type_alias_kind: TypeAliasKind,
|
||||
) -> Diagnostic {
|
||||
let source = checker.source();
|
||||
let comment_ranges = checker.comment_ranges();
|
||||
|
||||
let range_with_parentheses =
|
||||
parenthesized_range(value.into(), stmt.into(), checker.comment_ranges(), source)
|
||||
parenthesized_range(value.into(), stmt.into(), comment_ranges, source)
|
||||
.unwrap_or(value.range());
|
||||
|
||||
let content = format!(
|
||||
"type {name}{type_params} = {value}",
|
||||
type_params = DisplayTypeVars { type_vars, source },
|
||||
value = &source[range_with_parentheses]
|
||||
);
|
||||
let edit = Edit::range_replacement(content, stmt.range());
|
||||
|
||||
let applicability =
|
||||
if type_alias_kind == TypeAliasKind::TypeAlias && !checker.source_type.is_stub() {
|
||||
// The fix is always unsafe in non-stubs
|
||||
// because new-style aliases have different runtime behavior.
|
||||
// See https://github.com/astral-sh/ruff/issues/6434
|
||||
Applicability::Unsafe
|
||||
} else {
|
||||
// In stub files, or in non-stub files for `TypeAliasType` assignments,
|
||||
// the fix is only unsafe if it would delete comments.
|
||||
//
|
||||
// it would be easier to check for comments in the whole `stmt.range`, but because
|
||||
// `create_diagnostic` uses the full source text of `value`, comments within `value` are
|
||||
// actually preserved. thus, we have to check for comments in `stmt` but outside of `value`
|
||||
let pre_value = TextRange::new(stmt.start(), range_with_parentheses.start());
|
||||
let post_value = TextRange::new(range_with_parentheses.end(), stmt.end());
|
||||
|
||||
if comment_ranges.intersects(pre_value) || comment_ranges.intersects(post_value) {
|
||||
Applicability::Unsafe
|
||||
} else {
|
||||
Applicability::Safe
|
||||
}
|
||||
};
|
||||
|
||||
Diagnostic::new(
|
||||
NonPEP695TypeAlias {
|
||||
name: name.to_string(),
|
||||
|
|
|
@ -48,6 +48,8 @@ UP040.pyi:11:1: UP040 [*] Type alias `x` uses `TypeAlias` annotation instead of
|
|||
13 | | float,
|
||||
14 | | ]
|
||||
| |_^ UP040
|
||||
15 |
|
||||
16 | T: TypeAlias = ( # comment0
|
||||
|
|
||||
= help: Use the `type` keyword
|
||||
|
||||
|
@ -60,3 +62,30 @@ UP040.pyi:11:1: UP040 [*] Type alias `x` uses `TypeAlias` annotation instead of
|
|||
12 12 | int, # preserved
|
||||
13 13 | float,
|
||||
14 14 | ]
|
||||
|
||||
UP040.pyi:16:1: UP040 [*] Type alias `T` uses `TypeAlias` annotation instead of the `type` keyword
|
||||
|
|
||||
14 | ]
|
||||
15 |
|
||||
16 | / T: TypeAlias = ( # comment0
|
||||
17 | | # comment1
|
||||
18 | | int # comment2
|
||||
19 | | # comment3
|
||||
20 | | | # comment4
|
||||
21 | | # comment5
|
||||
22 | | str # comment6
|
||||
23 | | # comment7
|
||||
24 | | ) # comment8
|
||||
| |_^ UP040
|
||||
|
|
||||
= help: Use the `type` keyword
|
||||
|
||||
ℹ Safe fix
|
||||
13 13 | float,
|
||||
14 14 | ]
|
||||
15 15 |
|
||||
16 |-T: TypeAlias = ( # comment0
|
||||
16 |+type T = ( # comment0
|
||||
17 17 | # comment1
|
||||
18 18 | int # comment2
|
||||
19 19 | # comment3
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue