mirror of https://github.com/astral-sh/ruff
[ty] Preserve quoting style when autofixing `TypedDict` keys (#21682)
This commit is contained in:
parent
b5b4917d7f
commit
594b7b04d3
|
|
@ -52,6 +52,9 @@ mdtest path: crates/ty_python_semantic/resources/mdtest/typed_dict.md
|
||||||
38 |
|
38 |
|
||||||
39 | def write_to_readonly_key(employee: Employee):
|
39 | def write_to_readonly_key(employee: Employee):
|
||||||
40 | employee["id"] = 42 # error: [invalid-assignment]
|
40 | employee["id"] = 42 # error: [invalid-assignment]
|
||||||
|
41 | def write_to_non_existing_key_single_quotes(person: Person):
|
||||||
|
42 | # error: [invalid-key]
|
||||||
|
43 | person['naem'] = "Alice" # fmt: skip
|
||||||
```
|
```
|
||||||
|
|
||||||
# Diagnostics
|
# Diagnostics
|
||||||
|
|
@ -217,6 +220,8 @@ error[invalid-assignment]: Cannot assign to key "id" on TypedDict `Employee`
|
||||||
| -------- ^^^^ key is marked read-only
|
| -------- ^^^^ key is marked read-only
|
||||||
| |
|
| |
|
||||||
| TypedDict `Employee`
|
| TypedDict `Employee`
|
||||||
|
41 | def write_to_non_existing_key_single_quotes(person: Person):
|
||||||
|
42 | # error: [invalid-key]
|
||||||
|
|
|
|
||||||
info: Item declaration
|
info: Item declaration
|
||||||
--> src/mdtest_snippet.py:36:5
|
--> src/mdtest_snippet.py:36:5
|
||||||
|
|
@ -229,3 +234,24 @@ info: Item declaration
|
||||||
info: rule `invalid-assignment` is enabled by default
|
info: rule `invalid-assignment` is enabled by default
|
||||||
|
|
||||||
```
|
```
|
||||||
|
|
||||||
|
```
|
||||||
|
error[invalid-key]: Unknown key "naem" for TypedDict `Person`
|
||||||
|
--> src/mdtest_snippet.py:43:5
|
||||||
|
|
|
||||||
|
41 | def write_to_non_existing_key_single_quotes(person: Person):
|
||||||
|
42 | # error: [invalid-key]
|
||||||
|
43 | person['naem'] = "Alice" # fmt: skip
|
||||||
|
| ------ ^^^^^^ Did you mean 'name'?
|
||||||
|
| |
|
||||||
|
| TypedDict `Person`
|
||||||
|
|
|
||||||
|
info: rule `invalid-key` is enabled by default
|
||||||
|
40 | employee["id"] = 42 # error: [invalid-assignment]
|
||||||
|
41 | def write_to_non_existing_key_single_quotes(person: Person):
|
||||||
|
42 | # error: [invalid-key]
|
||||||
|
- person['naem'] = "Alice" # fmt: skip
|
||||||
|
43 + person['name'] = "Alice" # fmt: skip
|
||||||
|
note: This is an unsafe fix and may change runtime behavior
|
||||||
|
|
||||||
|
```
|
||||||
|
|
|
||||||
|
|
@ -1502,6 +1502,14 @@ def write_to_readonly_key(employee: Employee):
|
||||||
employee["id"] = 42 # error: [invalid-assignment]
|
employee["id"] = 42 # error: [invalid-assignment]
|
||||||
```
|
```
|
||||||
|
|
||||||
|
If the key uses single quotes, the autofix preserves that quoting style:
|
||||||
|
|
||||||
|
```py
|
||||||
|
def write_to_non_existing_key_single_quotes(person: Person):
|
||||||
|
# error: [invalid-key]
|
||||||
|
person['naem'] = "Alice" # fmt: skip
|
||||||
|
```
|
||||||
|
|
||||||
## Import aliases
|
## Import aliases
|
||||||
|
|
||||||
`TypedDict` can be imported with aliases and should work correctly:
|
`TypedDict` can be imported with aliases and should work correctly:
|
||||||
|
|
|
||||||
|
|
@ -40,7 +40,7 @@ use ruff_db::{
|
||||||
use ruff_diagnostics::{Edit, Fix};
|
use ruff_diagnostics::{Edit, Fix};
|
||||||
use ruff_python_ast::name::Name;
|
use ruff_python_ast::name::Name;
|
||||||
use ruff_python_ast::parenthesize::parentheses_iterator;
|
use ruff_python_ast::parenthesize::parentheses_iterator;
|
||||||
use ruff_python_ast::{self as ast, AnyNodeRef};
|
use ruff_python_ast::{self as ast, AnyNodeRef, StringFlags};
|
||||||
use ruff_python_trivia::CommentRanges;
|
use ruff_python_trivia::CommentRanges;
|
||||||
use ruff_text_size::{Ranged, TextRange};
|
use ruff_text_size::{Ranged, TextRange};
|
||||||
use rustc_hash::FxHashSet;
|
use rustc_hash::FxHashSet;
|
||||||
|
|
@ -3456,11 +3456,15 @@ pub(crate) fn report_invalid_key_on_typed_dict<'db>(
|
||||||
|
|
||||||
let existing_keys = items.keys();
|
let existing_keys = items.keys();
|
||||||
if let Some(suggestion) = did_you_mean(existing_keys, key) {
|
if let Some(suggestion) = did_you_mean(existing_keys, key) {
|
||||||
if key_node.is_expr_string_literal() {
|
if let AnyNodeRef::ExprStringLiteral(literal) = key_node {
|
||||||
|
let quoted_suggestion = format!(
|
||||||
|
"{quote}{suggestion}{quote}",
|
||||||
|
quote = literal.value.first_literal_flags().quote_str()
|
||||||
|
);
|
||||||
diagnostic
|
diagnostic
|
||||||
.set_primary_message(format_args!("Did you mean \"{suggestion}\"?"));
|
.set_primary_message(format_args!("Did you mean {quoted_suggestion}?"));
|
||||||
diagnostic.set_fix(Fix::unsafe_edit(Edit::range_replacement(
|
diagnostic.set_fix(Fix::unsafe_edit(Edit::range_replacement(
|
||||||
format!("\"{suggestion}\""),
|
quoted_suggestion,
|
||||||
key_node.range(),
|
key_node.range(),
|
||||||
)));
|
)));
|
||||||
} else {
|
} else {
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue