Skip to content

Commit

Permalink
Enabel autofix for Deque
Browse files Browse the repository at this point in the history
  • Loading branch information
charliermarsh committed May 14, 2023
1 parent fdf0b99 commit 8a6d742
Show file tree
Hide file tree
Showing 8 changed files with 234 additions and 85 deletions.
8 changes: 8 additions & 0 deletions crates/ruff/resources/test/fixtures/pyupgrade/UP006.py
Original file line number Diff line number Diff line change
Expand Up @@ -56,3 +56,11 @@ def f(x: "List['Li' 'st[str]']") -> None:

def f(x: "Li" "st['List[str]']") -> None:
...


def f(x: typing.Deque[str]) -> None:
...


def f(x: typing.DefaultDict[str, str]) -> None:
...
2 changes: 0 additions & 2 deletions crates/ruff/src/checkers/ast/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2327,7 +2327,6 @@ where
|| (self.settings.target_version >= PythonVersion::Py37
&& self.ctx.future_annotations()
&& self.ctx.in_annotation()))
&& analyze::typing::is_pep585_builtin(expr, &self.ctx)
{
pyupgrade::rules::use_pep585_annotation(self, expr);
}
Expand Down Expand Up @@ -2378,7 +2377,6 @@ where
|| (self.settings.target_version >= PythonVersion::Py37
&& self.ctx.future_annotations()
&& self.ctx.in_annotation()))
&& analyze::typing::is_pep585_builtin(expr, &self.ctx)
{
pyupgrade::rules::use_pep585_annotation(self, expr);
}
Expand Down
69 changes: 41 additions & 28 deletions crates/ruff/src/rules/pyupgrade/rules/use_pep585_annotation.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,58 +2,71 @@ use rustpython_parser::ast::Expr;

use ruff_diagnostics::{AutofixKind, Diagnostic, Edit, Fix, Violation};
use ruff_macros::{derive_message_formats, violation};
use ruff_python_semantic::analyze::typing::{to_pep585_generic, ModuleMember, SymbolReplacement};

use crate::autofix::actions::get_or_import_symbol;
use crate::checkers::ast::Checker;
use crate::registry::AsRule;

#[violation]
pub struct NonPEP585Annotation {
name: String,
from: ModuleMember<'static>,
to: ModuleMember<'static>,
}

impl Violation for NonPEP585Annotation {
const AUTOFIX: AutofixKind = AutofixKind::Sometimes;

#[derive_message_formats]
fn message(&self) -> String {
let NonPEP585Annotation { name } = self;
format!(
"Use `{}` instead of `{}` for type annotations",
name.to_lowercase(),
name,
)
let NonPEP585Annotation { from, to } = self;
format!("Use `{to}` instead of `{from}` for type annotation")
}

fn autofix_title(&self) -> Option<String> {
let NonPEP585Annotation { name } = self;
Some(format!("Replace `{name}` with `{}`", name.to_lowercase()))
let NonPEP585Annotation { to, .. } = self;
Some(format!("Replace with `{to}`"))
}
}

/// UP006
pub(crate) fn use_pep585_annotation(checker: &mut Checker, expr: &Expr) {
if let Some(binding) = checker
.ctx
.resolve_call_path(expr)
.and_then(|call_path| call_path.last().copied())
{
let fixable = !checker.ctx.in_complex_string_type_definition();
let mut diagnostic = Diagnostic::new(
NonPEP585Annotation {
name: binding.to_string(),
},
expr.range(),
);
if fixable && checker.patch(diagnostic.kind.rule()) {
let binding = binding.to_lowercase();
if checker.ctx.is_builtin(&binding) {
#[allow(deprecated)]
diagnostic.set_fix(Fix::unspecified(Edit::range_replacement(
binding,
let Some(SymbolReplacement { from, to }) = to_pep585_generic(expr, &checker.ctx) else {
return;
};

let fixable = !checker.ctx.in_complex_string_type_definition();
let mut diagnostic = Diagnostic::new(
NonPEP585Annotation {
from: from.clone(),
to: to.clone(),
},
expr.range(),
);
if fixable && checker.patch(diagnostic.kind.rule()) {
if to.is_builtin() {
// Built-in type, like `list`.
if checker.ctx.is_builtin(to.member()) {
diagnostic.set_fix(Fix::automatic(Edit::range_replacement(
to.member().to_string(),
expr.range(),
)));
}
} else {
// Imported type, like `collections.deque`.
diagnostic.try_set_fix(|| {
let (import_edit, binding) = get_or_import_symbol(
to.module(),
to.member(),
expr.start(),
&checker.ctx,
&checker.importer,
checker.locator,
)?;
let reference_edit = Edit::range_replacement(binding, expr.range());
Ok(Fix::suggested_edits(import_edit, [reference_edit]))
});
}
checker.diagnostics.push(diagnostic);
}
checker.diagnostics.push(diagnostic);
}
Original file line number Diff line number Diff line change
@@ -1,13 +1,13 @@
---
source: crates/ruff/src/rules/pyupgrade/mod.rs
---
UP006.py:4:10: UP006 [*] Use `list` instead of `List` for type annotations
UP006.py:4:10: UP006 [*] Use `list` instead of `typing.List` for type annotation
|
4 | def f(x: typing.List[str]) -> None:
| ^^^^^^^^^^^ UP006
5 | ...
|
= help: Replace `List` with `list`
= help: Replace with `list`

Suggested fix
1 1 | import typing
Expand All @@ -19,13 +19,13 @@ UP006.py:4:10: UP006 [*] Use `list` instead of `List` for type annotations
6 6 |
7 7 |

UP006.py:11:10: UP006 [*] Use `list` instead of `List` for type annotations
UP006.py:11:10: UP006 [*] Use `list` instead of `typing.List` for type annotation
|
11 | def f(x: List[str]) -> None:
| ^^^^ UP006
12 | ...
|
= help: Replace `List` with `list`
= help: Replace with `list`

Suggested fix
8 8 | from typing import List
Expand All @@ -37,13 +37,13 @@ UP006.py:11:10: UP006 [*] Use `list` instead of `List` for type annotations
13 13 |
14 14 |

UP006.py:18:10: UP006 [*] Use `list` instead of `List` for type annotations
UP006.py:18:10: UP006 [*] Use `list` instead of `typing.List` for type annotation
|
18 | def f(x: t.List[str]) -> None:
| ^^^^^^ UP006
19 | ...
|
= help: Replace `List` with `list`
= help: Replace with `list`

Suggested fix
15 15 | import typing as t
Expand All @@ -55,13 +55,13 @@ UP006.py:18:10: UP006 [*] Use `list` instead of `List` for type annotations
20 20 |
21 21 |

UP006.py:25:10: UP006 [*] Use `list` instead of `List` for type annotations
UP006.py:25:10: UP006 [*] Use `list` instead of `typing.List` for type annotation
|
25 | def f(x: IList[str]) -> None:
| ^^^^^ UP006
26 | ...
|
= help: Replace `List` with `list`
= help: Replace with `list`

Suggested fix
22 22 | from typing import List as IList
Expand All @@ -73,13 +73,13 @@ UP006.py:25:10: UP006 [*] Use `list` instead of `List` for type annotations
27 27 |
28 28 |

UP006.py:29:11: UP006 [*] Use `list` instead of `List` for type annotations
UP006.py:29:11: UP006 [*] Use `list` instead of `typing.List` for type annotation
|
29 | def f(x: "List[str]") -> None:
| ^^^^ UP006
30 | ...
|
= help: Replace `List` with `list`
= help: Replace with `list`

Suggested fix
26 26 | ...
Expand All @@ -91,13 +91,13 @@ UP006.py:29:11: UP006 [*] Use `list` instead of `List` for type annotations
31 31 |
32 32 |

UP006.py:33:12: UP006 [*] Use `list` instead of `List` for type annotations
UP006.py:33:12: UP006 [*] Use `list` instead of `typing.List` for type annotation
|
33 | def f(x: r"List[str]") -> None:
| ^^^^ UP006
34 | ...
|
= help: Replace `List` with `list`
= help: Replace with `list`

Suggested fix
30 30 | ...
Expand All @@ -109,13 +109,13 @@ UP006.py:33:12: UP006 [*] Use `list` instead of `List` for type annotations
35 35 |
36 36 |

UP006.py:37:11: UP006 [*] Use `list` instead of `List` for type annotations
UP006.py:37:11: UP006 [*] Use `list` instead of `typing.List` for type annotation
|
37 | def f(x: "List[str]") -> None:
| ^^^^ UP006
38 | ...
|
= help: Replace `List` with `list`
= help: Replace with `list`

Suggested fix
34 34 | ...
Expand All @@ -127,13 +127,13 @@ UP006.py:37:11: UP006 [*] Use `list` instead of `List` for type annotations
39 39 |
40 40 |

UP006.py:41:13: UP006 [*] Use `list` instead of `List` for type annotations
UP006.py:41:13: UP006 [*] Use `list` instead of `typing.List` for type annotation
|
41 | def f(x: """List[str]""") -> None:
| ^^^^ UP006
42 | ...
|
= help: Replace `List` with `list`
= help: Replace with `list`

Suggested fix
38 38 | ...
Expand All @@ -145,21 +145,21 @@ UP006.py:41:13: UP006 [*] Use `list` instead of `List` for type annotations
43 43 |
44 44 |

UP006.py:45:10: UP006 Use `list` instead of `List` for type annotations
UP006.py:45:10: UP006 Use `list` instead of `typing.List` for type annotation
|
45 | def f(x: "Li" "st[str]") -> None:
| ^^^^^^^^^^^^^^ UP006
46 | ...
|
= help: Replace `List` with `list`
= help: Replace with `list`

UP006.py:49:11: UP006 [*] Use `list` instead of `List` for type annotations
UP006.py:49:11: UP006 [*] Use `list` instead of `typing.List` for type annotation
|
49 | def f(x: "List['List[str]']") -> None:
| ^^^^ UP006
50 | ...
|
= help: Replace `List` with `list`
= help: Replace with `list`

Suggested fix
46 46 | ...
Expand All @@ -171,13 +171,13 @@ UP006.py:49:11: UP006 [*] Use `list` instead of `List` for type annotations
51 51 |
52 52 |

UP006.py:49:17: UP006 [*] Use `list` instead of `List` for type annotations
UP006.py:49:17: UP006 [*] Use `list` instead of `typing.List` for type annotation
|
49 | def f(x: "List['List[str]']") -> None:
| ^^^^ UP006
50 | ...
|
= help: Replace `List` with `list`
= help: Replace with `list`

Suggested fix
46 46 | ...
Expand All @@ -189,13 +189,13 @@ UP006.py:49:17: UP006 [*] Use `list` instead of `List` for type annotations
51 51 |
52 52 |

UP006.py:53:11: UP006 [*] Use `list` instead of `List` for type annotations
UP006.py:53:11: UP006 [*] Use `list` instead of `typing.List` for type annotation
|
53 | def f(x: "List['Li' 'st[str]']") -> None:
| ^^^^ UP006
54 | ...
|
= help: Replace `List` with `list`
= help: Replace with `list`

Suggested fix
50 50 | ...
Expand All @@ -207,28 +207,78 @@ UP006.py:53:11: UP006 [*] Use `list` instead of `List` for type annotations
55 55 |
56 56 |

UP006.py:53:16: UP006 Use `list` instead of `List` for type annotations
UP006.py:53:16: UP006 Use `list` instead of `typing.List` for type annotation
|
53 | def f(x: "List['Li' 'st[str]']") -> None:
| ^^^^^^^^^^^^^^ UP006
54 | ...
|
= help: Replace `List` with `list`
= help: Replace with `list`

UP006.py:57:10: UP006 Use `list` instead of `List` for type annotations
UP006.py:57:10: UP006 Use `list` instead of `typing.List` for type annotation
|
57 | def f(x: "Li" "st['List[str]']") -> None:
| ^^^^^^^^^^^^^^^^^^^^^^ UP006
58 | ...
|
= help: Replace `List` with `list`
= help: Replace with `list`

UP006.py:57:10: UP006 Use `list` instead of `List` for type annotations
UP006.py:57:10: UP006 Use `list` instead of `typing.List` for type annotation
|
57 | def f(x: "Li" "st['List[str]']") -> None:
| ^^^^^^^^^^^^^^^^^^^^^^ UP006
58 | ...
|
= help: Replace `List` with `list`
= help: Replace with `list`

UP006.py:61:10: UP006 [*] Use `collections.deque` instead of `typing.Deque` for type annotation
|
61 | def f(x: typing.Deque[str]) -> None:
| ^^^^^^^^^^^^ UP006
62 | ...
|
= help: Replace with `collections.deque`

Suggested fix
20 20 |
21 21 |
22 22 | from typing import List as IList
23 |+import collections
23 24 |
24 25 |
25 26 | def f(x: IList[str]) -> None:
--------------------------------------------------------------------------------
58 59 | ...
59 60 |
60 61 |
61 |-def f(x: typing.Deque[str]) -> None:
62 |+def f(x: collections.deque[str]) -> None:
62 63 | ...
63 64 |
64 65 |

UP006.py:65:10: UP006 [*] Use `collections.defaultdict` instead of `typing.DefaultDict` for type annotation
|
65 | def f(x: typing.DefaultDict[str, str]) -> None:
| ^^^^^^^^^^^^^^^^^^ UP006
66 | ...
|
= help: Replace with `collections.defaultdict`

Suggested fix
20 20 |
21 21 |
22 22 | from typing import List as IList
23 |+import collections
23 24 |
24 25 |
25 26 | def f(x: IList[str]) -> None:
--------------------------------------------------------------------------------
62 63 | ...
63 64 |
64 65 |
65 |-def f(x: typing.DefaultDict[str, str]) -> None:
66 |+def f(x: collections.defaultdict[str, str]) -> None:
66 67 | ...


Loading

0 comments on commit 8a6d742

Please sign in to comment.