mirror of
https://github.com/astral-sh/ruff.git
synced 2025-08-02 01:42:25 +00:00
Enable automatic rewrites of typing.Deque
and typing.DefaultDict
(#4420)
This commit is contained in:
parent
838ba1ca3d
commit
2414469ac3
9 changed files with 370 additions and 212 deletions
|
@ -56,3 +56,11 @@ def f(x: "List['Li' 'st[str]']") -> None:
|
||||||
|
|
||||||
def f(x: "Li" "st['List[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:
|
||||||
|
...
|
||||||
|
|
|
@ -2257,26 +2257,40 @@ where
|
||||||
match &expr.node {
|
match &expr.node {
|
||||||
ExprKind::Subscript(ast::ExprSubscript { value, slice, .. }) => {
|
ExprKind::Subscript(ast::ExprSubscript { value, slice, .. }) => {
|
||||||
// Ex) Optional[...], Union[...]
|
// Ex) Optional[...], Union[...]
|
||||||
if self
|
if self.settings.rules.any_enabled(&[
|
||||||
.settings
|
Rule::MissingFutureAnnotationsImport,
|
||||||
.rules
|
Rule::NonPEP604Annotation,
|
||||||
.enabled(Rule::MissingFutureAnnotationsImport)
|
]) {
|
||||||
&& (self.settings.target_version < PythonVersion::Py310
|
if let Some(operator) =
|
||||||
&& (self.settings.target_version >= PythonVersion::Py37
|
analyze::typing::to_pep604_operator(value, slice, &self.ctx)
|
||||||
&& !self.ctx.future_annotations()
|
{
|
||||||
&& self.ctx.in_annotation()))
|
if self
|
||||||
&& analyze::typing::is_pep604_builtin(value, &self.ctx)
|
.settings
|
||||||
{
|
.rules
|
||||||
flake8_future_annotations::rules::missing_future_annotations(self, value);
|
.enabled(Rule::MissingFutureAnnotationsImport)
|
||||||
}
|
{
|
||||||
if self.settings.rules.enabled(Rule::NonPEP604Annotation)
|
if self.settings.target_version < PythonVersion::Py310
|
||||||
&& (self.settings.target_version >= PythonVersion::Py310
|
&& self.settings.target_version >= PythonVersion::Py37
|
||||||
|| (self.settings.target_version >= PythonVersion::Py37
|
&& !self.ctx.future_annotations()
|
||||||
&& self.ctx.future_annotations()
|
&& self.ctx.in_annotation()
|
||||||
&& self.ctx.in_annotation()))
|
{
|
||||||
&& analyze::typing::is_pep604_builtin(value, &self.ctx)
|
flake8_future_annotations::rules::missing_future_annotations(
|
||||||
{
|
self, value,
|
||||||
pyupgrade::rules::use_pep604_annotation(self, expr, value, slice);
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if self.settings.rules.enabled(Rule::NonPEP604Annotation) {
|
||||||
|
if self.settings.target_version >= PythonVersion::Py310
|
||||||
|
|| (self.settings.target_version >= PythonVersion::Py37
|
||||||
|
&& self.ctx.future_annotations()
|
||||||
|
&& self.ctx.in_annotation())
|
||||||
|
{
|
||||||
|
pyupgrade::rules::use_pep604_annotation(
|
||||||
|
self, expr, slice, operator,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if self.ctx.match_typing_expr(value, "Literal") {
|
if self.ctx.match_typing_expr(value, "Literal") {
|
||||||
|
@ -2332,28 +2346,42 @@ where
|
||||||
}
|
}
|
||||||
|
|
||||||
// Ex) List[...]
|
// Ex) List[...]
|
||||||
if self
|
if self.settings.rules.any_enabled(&[
|
||||||
.settings
|
Rule::MissingFutureAnnotationsImport,
|
||||||
.rules
|
Rule::NonPEP585Annotation,
|
||||||
.enabled(Rule::MissingFutureAnnotationsImport)
|
]) {
|
||||||
&& (self.settings.target_version < PythonVersion::Py39
|
if let Some(replacement) =
|
||||||
&& (self.settings.target_version >= PythonVersion::Py37
|
analyze::typing::to_pep585_generic(expr, &self.ctx)
|
||||||
&& !self.ctx.future_annotations()
|
{
|
||||||
&& self.ctx.in_annotation()))
|
if self
|
||||||
&& analyze::typing::is_pep585_builtin(expr, &self.ctx)
|
.settings
|
||||||
{
|
.rules
|
||||||
flake8_future_annotations::rules::missing_future_annotations(
|
.enabled(Rule::MissingFutureAnnotationsImport)
|
||||||
self, expr,
|
{
|
||||||
);
|
if self.settings.target_version < PythonVersion::Py39
|
||||||
}
|
&& self.settings.target_version >= PythonVersion::Py37
|
||||||
if self.settings.rules.enabled(Rule::NonPEP585Annotation)
|
&& !self.ctx.future_annotations()
|
||||||
&& (self.settings.target_version >= PythonVersion::Py39
|
&& self.ctx.in_annotation()
|
||||||
|| (self.settings.target_version >= PythonVersion::Py37
|
{
|
||||||
&& self.ctx.future_annotations()
|
flake8_future_annotations::rules::missing_future_annotations(
|
||||||
&& self.ctx.in_annotation()))
|
self, expr,
|
||||||
&& analyze::typing::is_pep585_builtin(expr, &self.ctx)
|
);
|
||||||
{
|
}
|
||||||
pyupgrade::rules::use_pep585_annotation(self, expr);
|
}
|
||||||
|
if self.settings.rules.enabled(Rule::NonPEP585Annotation) {
|
||||||
|
if self.settings.target_version >= PythonVersion::Py39
|
||||||
|
|| (self.settings.target_version >= PythonVersion::Py37
|
||||||
|
&& self.ctx.future_annotations()
|
||||||
|
&& self.ctx.in_annotation())
|
||||||
|
{
|
||||||
|
pyupgrade::rules::use_pep585_annotation(
|
||||||
|
self,
|
||||||
|
expr,
|
||||||
|
&replacement,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
self.handle_node_load(expr);
|
self.handle_node_load(expr);
|
||||||
|
@ -2396,26 +2424,36 @@ where
|
||||||
}
|
}
|
||||||
ExprKind::Attribute(ast::ExprAttribute { attr, value, .. }) => {
|
ExprKind::Attribute(ast::ExprAttribute { attr, value, .. }) => {
|
||||||
// Ex) typing.List[...]
|
// Ex) typing.List[...]
|
||||||
if self
|
if self.settings.rules.any_enabled(&[
|
||||||
.settings
|
Rule::MissingFutureAnnotationsImport,
|
||||||
.rules
|
Rule::NonPEP585Annotation,
|
||||||
.enabled(Rule::MissingFutureAnnotationsImport)
|
]) {
|
||||||
&& (self.settings.target_version < PythonVersion::Py39
|
if let Some(replacement) = analyze::typing::to_pep585_generic(expr, &self.ctx) {
|
||||||
&& (self.settings.target_version >= PythonVersion::Py37
|
if self
|
||||||
&& !self.ctx.future_annotations()
|
.settings
|
||||||
&& self.ctx.in_annotation()))
|
.rules
|
||||||
&& analyze::typing::is_pep585_builtin(expr, &self.ctx)
|
.enabled(Rule::MissingFutureAnnotationsImport)
|
||||||
{
|
{
|
||||||
flake8_future_annotations::rules::missing_future_annotations(self, expr);
|
if self.settings.target_version < PythonVersion::Py39
|
||||||
}
|
&& self.settings.target_version >= PythonVersion::Py37
|
||||||
if self.settings.rules.enabled(Rule::NonPEP585Annotation)
|
&& !self.ctx.future_annotations()
|
||||||
&& (self.settings.target_version >= PythonVersion::Py39
|
&& self.ctx.in_annotation()
|
||||||
|| (self.settings.target_version >= PythonVersion::Py37
|
{
|
||||||
&& self.ctx.future_annotations()
|
flake8_future_annotations::rules::missing_future_annotations(
|
||||||
&& self.ctx.in_annotation()))
|
self, expr,
|
||||||
&& analyze::typing::is_pep585_builtin(expr, &self.ctx)
|
);
|
||||||
{
|
}
|
||||||
pyupgrade::rules::use_pep585_annotation(self, expr);
|
}
|
||||||
|
if self.settings.rules.enabled(Rule::NonPEP585Annotation) {
|
||||||
|
if self.settings.target_version >= PythonVersion::Py39
|
||||||
|
|| (self.settings.target_version >= PythonVersion::Py37
|
||||||
|
&& self.ctx.future_annotations()
|
||||||
|
&& self.ctx.in_annotation())
|
||||||
|
{
|
||||||
|
pyupgrade::rules::use_pep585_annotation(self, expr, &replacement);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
if self.settings.rules.enabled(Rule::DatetimeTimezoneUTC)
|
if self.settings.rules.enabled(Rule::DatetimeTimezoneUTC)
|
||||||
&& self.settings.target_version >= PythonVersion::Py311
|
&& self.settings.target_version >= PythonVersion::Py311
|
||||||
|
|
|
@ -2,13 +2,17 @@ use rustpython_parser::ast::Expr;
|
||||||
|
|
||||||
use ruff_diagnostics::{AutofixKind, Diagnostic, Edit, Fix, Violation};
|
use ruff_diagnostics::{AutofixKind, Diagnostic, Edit, Fix, Violation};
|
||||||
use ruff_macros::{derive_message_formats, violation};
|
use ruff_macros::{derive_message_formats, violation};
|
||||||
|
use ruff_python_ast::call_path::compose_call_path;
|
||||||
|
use ruff_python_semantic::analyze::typing::ModuleMember;
|
||||||
|
|
||||||
|
use crate::autofix::actions::get_or_import_symbol;
|
||||||
use crate::checkers::ast::Checker;
|
use crate::checkers::ast::Checker;
|
||||||
use crate::registry::AsRule;
|
use crate::registry::AsRule;
|
||||||
|
|
||||||
#[violation]
|
#[violation]
|
||||||
pub struct NonPEP585Annotation {
|
pub struct NonPEP585Annotation {
|
||||||
name: String,
|
from: String,
|
||||||
|
to: String,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Violation for NonPEP585Annotation {
|
impl Violation for NonPEP585Annotation {
|
||||||
|
@ -16,44 +20,60 @@ impl Violation for NonPEP585Annotation {
|
||||||
|
|
||||||
#[derive_message_formats]
|
#[derive_message_formats]
|
||||||
fn message(&self) -> String {
|
fn message(&self) -> String {
|
||||||
let NonPEP585Annotation { name } = self;
|
let NonPEP585Annotation { from, to } = self;
|
||||||
format!(
|
format!("Use `{to}` instead of `{from}` for type annotation")
|
||||||
"Use `{}` instead of `{}` for type annotations",
|
|
||||||
name.to_lowercase(),
|
|
||||||
name,
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn autofix_title(&self) -> Option<String> {
|
fn autofix_title(&self) -> Option<String> {
|
||||||
let NonPEP585Annotation { name } = self;
|
let NonPEP585Annotation { to, .. } = self;
|
||||||
Some(format!("Replace `{name}` with `{}`", name.to_lowercase()))
|
Some(format!("Replace with `{to}`"))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// UP006
|
/// UP006
|
||||||
pub(crate) fn use_pep585_annotation(checker: &mut Checker, expr: &Expr) {
|
pub(crate) fn use_pep585_annotation(
|
||||||
if let Some(binding) = checker
|
checker: &mut Checker,
|
||||||
.ctx
|
expr: &Expr,
|
||||||
.resolve_call_path(expr)
|
replacement: &ModuleMember,
|
||||||
.and_then(|call_path| call_path.last().copied())
|
) {
|
||||||
{
|
let Some(from) = compose_call_path(expr) else {
|
||||||
let fixable = !checker.ctx.in_complex_string_type_definition();
|
return;
|
||||||
let mut diagnostic = Diagnostic::new(
|
};
|
||||||
NonPEP585Annotation {
|
let mut diagnostic = Diagnostic::new(
|
||||||
name: binding.to_string(),
|
NonPEP585Annotation {
|
||||||
},
|
from,
|
||||||
expr.range(),
|
to: replacement.to_string(),
|
||||||
);
|
},
|
||||||
if fixable && checker.patch(diagnostic.kind.rule()) {
|
expr.range(),
|
||||||
let binding = binding.to_lowercase();
|
);
|
||||||
if checker.ctx.is_builtin(&binding) {
|
let fixable = !checker.ctx.in_complex_string_type_definition();
|
||||||
#[allow(deprecated)]
|
if fixable && checker.patch(diagnostic.kind.rule()) {
|
||||||
diagnostic.set_fix(Fix::unspecified(Edit::range_replacement(
|
match replacement {
|
||||||
binding,
|
ModuleMember::BuiltIn(name) => {
|
||||||
expr.range(),
|
// Built-in type, like `list`.
|
||||||
)));
|
if checker.ctx.is_builtin(name) {
|
||||||
|
diagnostic.set_fix(Fix::automatic(Edit::range_replacement(
|
||||||
|
(*name).to_string(),
|
||||||
|
expr.range(),
|
||||||
|
)));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ModuleMember::Member(module, member) => {
|
||||||
|
// Imported type, like `collections.deque`.
|
||||||
|
diagnostic.try_set_fix(|| {
|
||||||
|
let (import_edit, binding) = get_or_import_symbol(
|
||||||
|
module,
|
||||||
|
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);
|
||||||
}
|
}
|
||||||
|
|
|
@ -4,6 +4,7 @@ use rustpython_parser::ast::{self, Constant, Expr, ExprKind, Operator};
|
||||||
use ruff_diagnostics::{AutofixKind, Diagnostic, Edit, Fix, Violation};
|
use ruff_diagnostics::{AutofixKind, Diagnostic, Edit, Fix, Violation};
|
||||||
use ruff_macros::{derive_message_formats, violation};
|
use ruff_macros::{derive_message_formats, violation};
|
||||||
use ruff_python_ast::helpers::unparse_expr;
|
use ruff_python_ast::helpers::unparse_expr;
|
||||||
|
use ruff_python_semantic::analyze::typing::Pep604Operator;
|
||||||
|
|
||||||
use crate::checkers::ast::Checker;
|
use crate::checkers::ast::Checker;
|
||||||
use crate::registry::AsRule;
|
use crate::registry::AsRule;
|
||||||
|
@ -56,55 +57,18 @@ fn union(elts: &[Expr]) -> Expr {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns `true` if any argument in the slice is a string.
|
|
||||||
fn any_arg_is_str(slice: &Expr) -> bool {
|
|
||||||
match &slice.node {
|
|
||||||
ExprKind::Constant(ast::ExprConstant {
|
|
||||||
value: Constant::Str(_),
|
|
||||||
..
|
|
||||||
}) => true,
|
|
||||||
ExprKind::Tuple(ast::ExprTuple { elts, .. }) => elts.iter().any(any_arg_is_str),
|
|
||||||
_ => false,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Copy, Clone)]
|
|
||||||
enum TypingMember {
|
|
||||||
Union,
|
|
||||||
Optional,
|
|
||||||
}
|
|
||||||
|
|
||||||
/// UP007
|
/// UP007
|
||||||
pub(crate) fn use_pep604_annotation(
|
pub(crate) fn use_pep604_annotation(
|
||||||
checker: &mut Checker,
|
checker: &mut Checker,
|
||||||
expr: &Expr,
|
expr: &Expr,
|
||||||
value: &Expr,
|
|
||||||
slice: &Expr,
|
slice: &Expr,
|
||||||
|
operator: Pep604Operator,
|
||||||
) {
|
) {
|
||||||
// If any of the _arguments_ are forward references, we can't use PEP 604.
|
|
||||||
// Ex) `Union["str", "int"]` can't be converted to `"str" | "int"`.
|
|
||||||
if any_arg_is_str(slice) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
let Some(typing_member) = checker.ctx.resolve_call_path(value).as_ref().and_then(|call_path| {
|
|
||||||
if checker.ctx.match_typing_call_path(call_path, "Optional") {
|
|
||||||
Some(TypingMember::Optional)
|
|
||||||
} else if checker.ctx.match_typing_call_path(call_path, "Union") {
|
|
||||||
Some(TypingMember::Union)
|
|
||||||
} else {
|
|
||||||
None
|
|
||||||
}
|
|
||||||
}) else {
|
|
||||||
return;
|
|
||||||
};
|
|
||||||
|
|
||||||
// Avoid fixing forward references, or types not in an annotation.
|
// Avoid fixing forward references, or types not in an annotation.
|
||||||
let fixable =
|
let fixable =
|
||||||
checker.ctx.in_type_definition() && !checker.ctx.in_complex_string_type_definition();
|
checker.ctx.in_type_definition() && !checker.ctx.in_complex_string_type_definition();
|
||||||
|
match operator {
|
||||||
match typing_member {
|
Pep604Operator::Optional => {
|
||||||
TypingMember::Optional => {
|
|
||||||
let mut diagnostic = Diagnostic::new(NonPEP604Annotation, expr.range());
|
let mut diagnostic = Diagnostic::new(NonPEP604Annotation, expr.range());
|
||||||
if fixable && checker.patch(diagnostic.kind.rule()) {
|
if fixable && checker.patch(diagnostic.kind.rule()) {
|
||||||
#[allow(deprecated)]
|
#[allow(deprecated)]
|
||||||
|
@ -115,7 +79,7 @@ pub(crate) fn use_pep604_annotation(
|
||||||
}
|
}
|
||||||
checker.diagnostics.push(diagnostic);
|
checker.diagnostics.push(diagnostic);
|
||||||
}
|
}
|
||||||
TypingMember::Union => {
|
Pep604Operator::Union => {
|
||||||
let mut diagnostic = Diagnostic::new(NonPEP604Annotation, expr.range());
|
let mut diagnostic = Diagnostic::new(NonPEP604Annotation, expr.range());
|
||||||
if fixable && checker.patch(diagnostic.kind.rule()) {
|
if fixable && checker.patch(diagnostic.kind.rule()) {
|
||||||
match &slice.node {
|
match &slice.node {
|
||||||
|
|
|
@ -1,15 +1,15 @@
|
||||||
---
|
---
|
||||||
source: crates/ruff/src/rules/pyupgrade/mod.rs
|
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:
|
4 | def f(x: typing.List[str]) -> None:
|
||||||
| ^^^^^^^^^^^ UP006
|
| ^^^^^^^^^^^ UP006
|
||||||
5 | ...
|
5 | ...
|
||||||
|
|
|
|
||||||
= help: Replace `List` with `list`
|
= help: Replace with `list`
|
||||||
|
|
||||||
ℹ Suggested fix
|
ℹ Fix
|
||||||
1 1 | import typing
|
1 1 | import typing
|
||||||
2 2 |
|
2 2 |
|
||||||
3 3 |
|
3 3 |
|
||||||
|
@ -19,15 +19,15 @@ UP006.py:4:10: UP006 [*] Use `list` instead of `List` for type annotations
|
||||||
6 6 |
|
6 6 |
|
||||||
7 7 |
|
7 7 |
|
||||||
|
|
||||||
UP006.py:11:10: UP006 [*] Use `list` instead of `List` for type annotations
|
UP006.py:11:10: UP006 [*] Use `list` instead of `List` for type annotation
|
||||||
|
|
|
|
||||||
11 | def f(x: List[str]) -> None:
|
11 | def f(x: List[str]) -> None:
|
||||||
| ^^^^ UP006
|
| ^^^^ UP006
|
||||||
12 | ...
|
12 | ...
|
||||||
|
|
|
|
||||||
= help: Replace `List` with `list`
|
= help: Replace with `list`
|
||||||
|
|
||||||
ℹ Suggested fix
|
ℹ Fix
|
||||||
8 8 | from typing import List
|
8 8 | from typing import List
|
||||||
9 9 |
|
9 9 |
|
||||||
10 10 |
|
10 10 |
|
||||||
|
@ -37,15 +37,15 @@ UP006.py:11:10: UP006 [*] Use `list` instead of `List` for type annotations
|
||||||
13 13 |
|
13 13 |
|
||||||
14 14 |
|
14 14 |
|
||||||
|
|
||||||
UP006.py:18:10: UP006 [*] Use `list` instead of `List` for type annotations
|
UP006.py:18:10: UP006 [*] Use `list` instead of `t.List` for type annotation
|
||||||
|
|
|
|
||||||
18 | def f(x: t.List[str]) -> None:
|
18 | def f(x: t.List[str]) -> None:
|
||||||
| ^^^^^^ UP006
|
| ^^^^^^ UP006
|
||||||
19 | ...
|
19 | ...
|
||||||
|
|
|
|
||||||
= help: Replace `List` with `list`
|
= help: Replace with `list`
|
||||||
|
|
||||||
ℹ Suggested fix
|
ℹ Fix
|
||||||
15 15 | import typing as t
|
15 15 | import typing as t
|
||||||
16 16 |
|
16 16 |
|
||||||
17 17 |
|
17 17 |
|
||||||
|
@ -55,15 +55,15 @@ UP006.py:18:10: UP006 [*] Use `list` instead of `List` for type annotations
|
||||||
20 20 |
|
20 20 |
|
||||||
21 21 |
|
21 21 |
|
||||||
|
|
||||||
UP006.py:25:10: UP006 [*] Use `list` instead of `List` for type annotations
|
UP006.py:25:10: UP006 [*] Use `list` instead of `IList` for type annotation
|
||||||
|
|
|
|
||||||
25 | def f(x: IList[str]) -> None:
|
25 | def f(x: IList[str]) -> None:
|
||||||
| ^^^^^ UP006
|
| ^^^^^ UP006
|
||||||
26 | ...
|
26 | ...
|
||||||
|
|
|
|
||||||
= help: Replace `List` with `list`
|
= help: Replace with `list`
|
||||||
|
|
||||||
ℹ Suggested fix
|
ℹ Fix
|
||||||
22 22 | from typing import List as IList
|
22 22 | from typing import List as IList
|
||||||
23 23 |
|
23 23 |
|
||||||
24 24 |
|
24 24 |
|
||||||
|
@ -73,15 +73,15 @@ UP006.py:25:10: UP006 [*] Use `list` instead of `List` for type annotations
|
||||||
27 27 |
|
27 27 |
|
||||||
28 28 |
|
28 28 |
|
||||||
|
|
||||||
UP006.py:29:11: UP006 [*] Use `list` instead of `List` for type annotations
|
UP006.py:29:11: UP006 [*] Use `list` instead of `List` for type annotation
|
||||||
|
|
|
|
||||||
29 | def f(x: "List[str]") -> None:
|
29 | def f(x: "List[str]") -> None:
|
||||||
| ^^^^ UP006
|
| ^^^^ UP006
|
||||||
30 | ...
|
30 | ...
|
||||||
|
|
|
|
||||||
= help: Replace `List` with `list`
|
= help: Replace with `list`
|
||||||
|
|
||||||
ℹ Suggested fix
|
ℹ Fix
|
||||||
26 26 | ...
|
26 26 | ...
|
||||||
27 27 |
|
27 27 |
|
||||||
28 28 |
|
28 28 |
|
||||||
|
@ -91,15 +91,15 @@ UP006.py:29:11: UP006 [*] Use `list` instead of `List` for type annotations
|
||||||
31 31 |
|
31 31 |
|
||||||
32 32 |
|
32 32 |
|
||||||
|
|
||||||
UP006.py:33:12: UP006 [*] Use `list` instead of `List` for type annotations
|
UP006.py:33:12: UP006 [*] Use `list` instead of `List` for type annotation
|
||||||
|
|
|
|
||||||
33 | def f(x: r"List[str]") -> None:
|
33 | def f(x: r"List[str]") -> None:
|
||||||
| ^^^^ UP006
|
| ^^^^ UP006
|
||||||
34 | ...
|
34 | ...
|
||||||
|
|
|
|
||||||
= help: Replace `List` with `list`
|
= help: Replace with `list`
|
||||||
|
|
||||||
ℹ Suggested fix
|
ℹ Fix
|
||||||
30 30 | ...
|
30 30 | ...
|
||||||
31 31 |
|
31 31 |
|
||||||
32 32 |
|
32 32 |
|
||||||
|
@ -109,15 +109,15 @@ UP006.py:33:12: UP006 [*] Use `list` instead of `List` for type annotations
|
||||||
35 35 |
|
35 35 |
|
||||||
36 36 |
|
36 36 |
|
||||||
|
|
||||||
UP006.py:37:11: UP006 [*] Use `list` instead of `List` for type annotations
|
UP006.py:37:11: UP006 [*] Use `list` instead of `List` for type annotation
|
||||||
|
|
|
|
||||||
37 | def f(x: "List[str]") -> None:
|
37 | def f(x: "List[str]") -> None:
|
||||||
| ^^^^ UP006
|
| ^^^^ UP006
|
||||||
38 | ...
|
38 | ...
|
||||||
|
|
|
|
||||||
= help: Replace `List` with `list`
|
= help: Replace with `list`
|
||||||
|
|
||||||
ℹ Suggested fix
|
ℹ Fix
|
||||||
34 34 | ...
|
34 34 | ...
|
||||||
35 35 |
|
35 35 |
|
||||||
36 36 |
|
36 36 |
|
||||||
|
@ -127,15 +127,15 @@ UP006.py:37:11: UP006 [*] Use `list` instead of `List` for type annotations
|
||||||
39 39 |
|
39 39 |
|
||||||
40 40 |
|
40 40 |
|
||||||
|
|
||||||
UP006.py:41:13: UP006 [*] Use `list` instead of `List` for type annotations
|
UP006.py:41:13: UP006 [*] Use `list` instead of `List` for type annotation
|
||||||
|
|
|
|
||||||
41 | def f(x: """List[str]""") -> None:
|
41 | def f(x: """List[str]""") -> None:
|
||||||
| ^^^^ UP006
|
| ^^^^ UP006
|
||||||
42 | ...
|
42 | ...
|
||||||
|
|
|
|
||||||
= help: Replace `List` with `list`
|
= help: Replace with `list`
|
||||||
|
|
||||||
ℹ Suggested fix
|
ℹ Fix
|
||||||
38 38 | ...
|
38 38 | ...
|
||||||
39 39 |
|
39 39 |
|
||||||
40 40 |
|
40 40 |
|
||||||
|
@ -145,23 +145,23 @@ UP006.py:41:13: UP006 [*] Use `list` instead of `List` for type annotations
|
||||||
43 43 |
|
43 43 |
|
||||||
44 44 |
|
44 44 |
|
||||||
|
|
||||||
UP006.py:45:10: UP006 Use `list` instead of `List` for type annotations
|
UP006.py:45:10: UP006 Use `list` instead of `List` for type annotation
|
||||||
|
|
|
|
||||||
45 | def f(x: "Li" "st[str]") -> None:
|
45 | def f(x: "Li" "st[str]") -> None:
|
||||||
| ^^^^^^^^^^^^^^ UP006
|
| ^^^^^^^^^^^^^^ UP006
|
||||||
46 | ...
|
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 `List` for type annotation
|
||||||
|
|
|
|
||||||
49 | def f(x: "List['List[str]']") -> None:
|
49 | def f(x: "List['List[str]']") -> None:
|
||||||
| ^^^^ UP006
|
| ^^^^ UP006
|
||||||
50 | ...
|
50 | ...
|
||||||
|
|
|
|
||||||
= help: Replace `List` with `list`
|
= help: Replace with `list`
|
||||||
|
|
||||||
ℹ Suggested fix
|
ℹ Fix
|
||||||
46 46 | ...
|
46 46 | ...
|
||||||
47 47 |
|
47 47 |
|
||||||
48 48 |
|
48 48 |
|
||||||
|
@ -171,15 +171,15 @@ UP006.py:49:11: UP006 [*] Use `list` instead of `List` for type annotations
|
||||||
51 51 |
|
51 51 |
|
||||||
52 52 |
|
52 52 |
|
||||||
|
|
||||||
UP006.py:49:17: UP006 [*] Use `list` instead of `List` for type annotations
|
UP006.py:49:17: UP006 [*] Use `list` instead of `List` for type annotation
|
||||||
|
|
|
|
||||||
49 | def f(x: "List['List[str]']") -> None:
|
49 | def f(x: "List['List[str]']") -> None:
|
||||||
| ^^^^ UP006
|
| ^^^^ UP006
|
||||||
50 | ...
|
50 | ...
|
||||||
|
|
|
|
||||||
= help: Replace `List` with `list`
|
= help: Replace with `list`
|
||||||
|
|
||||||
ℹ Suggested fix
|
ℹ Fix
|
||||||
46 46 | ...
|
46 46 | ...
|
||||||
47 47 |
|
47 47 |
|
||||||
48 48 |
|
48 48 |
|
||||||
|
@ -189,15 +189,15 @@ UP006.py:49:17: UP006 [*] Use `list` instead of `List` for type annotations
|
||||||
51 51 |
|
51 51 |
|
||||||
52 52 |
|
52 52 |
|
||||||
|
|
||||||
UP006.py:53:11: UP006 [*] Use `list` instead of `List` for type annotations
|
UP006.py:53:11: UP006 [*] Use `list` instead of `List` for type annotation
|
||||||
|
|
|
|
||||||
53 | def f(x: "List['Li' 'st[str]']") -> None:
|
53 | def f(x: "List['Li' 'st[str]']") -> None:
|
||||||
| ^^^^ UP006
|
| ^^^^ UP006
|
||||||
54 | ...
|
54 | ...
|
||||||
|
|
|
|
||||||
= help: Replace `List` with `list`
|
= help: Replace with `list`
|
||||||
|
|
||||||
ℹ Suggested fix
|
ℹ Fix
|
||||||
50 50 | ...
|
50 50 | ...
|
||||||
51 51 |
|
51 51 |
|
||||||
52 52 |
|
52 52 |
|
||||||
|
@ -207,28 +207,78 @@ UP006.py:53:11: UP006 [*] Use `list` instead of `List` for type annotations
|
||||||
55 55 |
|
55 55 |
|
||||||
56 56 |
|
56 56 |
|
||||||
|
|
||||||
UP006.py:53:16: UP006 Use `list` instead of `List` for type annotations
|
UP006.py:53:16: UP006 Use `list` instead of `List` for type annotation
|
||||||
|
|
|
|
||||||
53 | def f(x: "List['Li' 'st[str]']") -> None:
|
53 | def f(x: "List['Li' 'st[str]']") -> None:
|
||||||
| ^^^^^^^^^^^^^^ UP006
|
| ^^^^^^^^^^^^^^ UP006
|
||||||
54 | ...
|
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 `List` for type annotation
|
||||||
|
|
|
|
||||||
57 | def f(x: "Li" "st['List[str]']") -> None:
|
57 | def f(x: "Li" "st['List[str]']") -> None:
|
||||||
| ^^^^^^^^^^^^^^^^^^^^^^ UP006
|
| ^^^^^^^^^^^^^^^^^^^^^^ UP006
|
||||||
58 | ...
|
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 `List` for type annotation
|
||||||
|
|
|
|
||||||
57 | def f(x: "Li" "st['List[str]']") -> None:
|
57 | def f(x: "Li" "st['List[str]']") -> None:
|
||||||
| ^^^^^^^^^^^^^^^^^^^^^^ UP006
|
| ^^^^^^^^^^^^^^^^^^^^^^ UP006
|
||||||
58 | ...
|
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 | ...
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -1,16 +1,16 @@
|
||||||
---
|
---
|
||||||
source: crates/ruff/src/rules/pyupgrade/mod.rs
|
source: crates/ruff/src/rules/pyupgrade/mod.rs
|
||||||
---
|
---
|
||||||
future_annotations.py:34:18: UP006 [*] Use `list` instead of `List` for type annotations
|
future_annotations.py:34:18: UP006 [*] Use `list` instead of `List` for type annotation
|
||||||
|
|
|
|
||||||
34 | def f(x: int) -> List[int]:
|
34 | def f(x: int) -> List[int]:
|
||||||
| ^^^^ UP006
|
| ^^^^ UP006
|
||||||
35 | y = List[int]()
|
35 | y = List[int]()
|
||||||
36 | y.append(x)
|
36 | y.append(x)
|
||||||
|
|
|
|
||||||
= help: Replace `List` with `list`
|
= help: Replace with `list`
|
||||||
|
|
||||||
ℹ Suggested fix
|
ℹ Fix
|
||||||
31 31 | return cls(x=0, y=0)
|
31 31 | return cls(x=0, y=0)
|
||||||
32 32 |
|
32 32 |
|
||||||
33 33 |
|
33 33 |
|
||||||
|
|
|
@ -1,16 +1,16 @@
|
||||||
---
|
---
|
||||||
source: crates/ruff/src/rules/pyupgrade/mod.rs
|
source: crates/ruff/src/rules/pyupgrade/mod.rs
|
||||||
---
|
---
|
||||||
future_annotations.py:34:18: UP006 [*] Use `list` instead of `List` for type annotations
|
future_annotations.py:34:18: UP006 [*] Use `list` instead of `List` for type annotation
|
||||||
|
|
|
|
||||||
34 | def f(x: int) -> List[int]:
|
34 | def f(x: int) -> List[int]:
|
||||||
| ^^^^ UP006
|
| ^^^^ UP006
|
||||||
35 | y = List[int]()
|
35 | y = List[int]()
|
||||||
36 | y.append(x)
|
36 | y.append(x)
|
||||||
|
|
|
|
||||||
= help: Replace `List` with `list`
|
= help: Replace with `list`
|
||||||
|
|
||||||
ℹ Suggested fix
|
ℹ Fix
|
||||||
31 31 | return cls(x=0, y=0)
|
31 31 | return cls(x=0, y=0)
|
||||||
32 32 |
|
32 32 |
|
||||||
33 33 |
|
33 33 |
|
||||||
|
@ -20,7 +20,7 @@ future_annotations.py:34:18: UP006 [*] Use `list` instead of `List` for type ann
|
||||||
36 36 | y.append(x)
|
36 36 | y.append(x)
|
||||||
37 37 | return y
|
37 37 | return y
|
||||||
|
|
||||||
future_annotations.py:35:9: UP006 [*] Use `list` instead of `List` for type annotations
|
future_annotations.py:35:9: UP006 [*] Use `list` instead of `List` for type annotation
|
||||||
|
|
|
|
||||||
35 | def f(x: int) -> List[int]:
|
35 | def f(x: int) -> List[int]:
|
||||||
36 | y = List[int]()
|
36 | y = List[int]()
|
||||||
|
@ -28,9 +28,9 @@ future_annotations.py:35:9: UP006 [*] Use `list` instead of `List` for type anno
|
||||||
37 | y.append(x)
|
37 | y.append(x)
|
||||||
38 | return y
|
38 | return y
|
||||||
|
|
|
|
||||||
= help: Replace `List` with `list`
|
= help: Replace with `list`
|
||||||
|
|
||||||
ℹ Suggested fix
|
ℹ Fix
|
||||||
32 32 |
|
32 32 |
|
||||||
33 33 |
|
33 33 |
|
||||||
34 34 | def f(x: int) -> List[int]:
|
34 34 | def f(x: int) -> List[int]:
|
||||||
|
@ -40,32 +40,32 @@ future_annotations.py:35:9: UP006 [*] Use `list` instead of `List` for type anno
|
||||||
37 37 | return y
|
37 37 | return y
|
||||||
38 38 |
|
38 38 |
|
||||||
|
|
||||||
future_annotations.py:42:27: UP006 [*] Use `list` instead of `List` for type annotations
|
future_annotations.py:42:27: UP006 [*] Use `list` instead of `List` for type annotation
|
||||||
|
|
|
|
||||||
42 | x: Optional[int] = None
|
42 | x: Optional[int] = None
|
||||||
43 |
|
43 |
|
||||||
44 | MyList: TypeAlias = Union[List[int], List[str]]
|
44 | MyList: TypeAlias = Union[List[int], List[str]]
|
||||||
| ^^^^ UP006
|
| ^^^^ UP006
|
||||||
|
|
|
|
||||||
= help: Replace `List` with `list`
|
= help: Replace with `list`
|
||||||
|
|
||||||
ℹ Suggested fix
|
ℹ Fix
|
||||||
39 39 |
|
39 39 |
|
||||||
40 40 | x: Optional[int] = None
|
40 40 | x: Optional[int] = None
|
||||||
41 41 |
|
41 41 |
|
||||||
42 |-MyList: TypeAlias = Union[List[int], List[str]]
|
42 |-MyList: TypeAlias = Union[List[int], List[str]]
|
||||||
42 |+MyList: TypeAlias = Union[list[int], List[str]]
|
42 |+MyList: TypeAlias = Union[list[int], List[str]]
|
||||||
|
|
||||||
future_annotations.py:42:38: UP006 [*] Use `list` instead of `List` for type annotations
|
future_annotations.py:42:38: UP006 [*] Use `list` instead of `List` for type annotation
|
||||||
|
|
|
|
||||||
42 | x: Optional[int] = None
|
42 | x: Optional[int] = None
|
||||||
43 |
|
43 |
|
||||||
44 | MyList: TypeAlias = Union[List[int], List[str]]
|
44 | MyList: TypeAlias = Union[List[int], List[str]]
|
||||||
| ^^^^ UP006
|
| ^^^^ UP006
|
||||||
|
|
|
|
||||||
= help: Replace `List` with `list`
|
= help: Replace with `list`
|
||||||
|
|
||||||
ℹ Suggested fix
|
ℹ Fix
|
||||||
39 39 |
|
39 39 |
|
||||||
40 40 | x: Optional[int] = None
|
40 40 | x: Optional[int] = None
|
||||||
41 41 |
|
41 41 |
|
||||||
|
|
|
@ -2,8 +2,7 @@ use rustpython_parser::ast::{self, Constant, Expr, ExprKind, Operator};
|
||||||
|
|
||||||
use ruff_python_ast::call_path::{from_unqualified_name, CallPath};
|
use ruff_python_ast::call_path::{from_unqualified_name, CallPath};
|
||||||
use ruff_python_stdlib::typing::{
|
use ruff_python_stdlib::typing::{
|
||||||
IMMUTABLE_GENERIC_TYPES, IMMUTABLE_TYPES, PEP_585_BUILTINS_ELIGIBLE, PEP_593_SUBSCRIPTS,
|
IMMUTABLE_GENERIC_TYPES, IMMUTABLE_TYPES, PEP_585_GENERICS, PEP_593_SUBSCRIPTS, SUBSCRIPTS,
|
||||||
SUBSCRIPTS,
|
|
||||||
};
|
};
|
||||||
|
|
||||||
use crate::context::Context;
|
use crate::context::Context;
|
||||||
|
@ -62,23 +61,90 @@ pub fn match_annotated_subscript<'a>(
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns `true` if `Expr` represents a reference to a typing object with a
|
#[derive(Debug, Clone, Eq, PartialEq)]
|
||||||
/// PEP 585 built-in.
|
pub enum ModuleMember {
|
||||||
pub fn is_pep585_builtin(expr: &Expr, context: &Context) -> bool {
|
/// A builtin symbol, like `"list"`.
|
||||||
context.resolve_call_path(expr).map_or(false, |call_path| {
|
BuiltIn(&'static str),
|
||||||
PEP_585_BUILTINS_ELIGIBLE.contains(&call_path.as_slice())
|
/// A module member, like `("collections", "deque")`.
|
||||||
|
Member(&'static str, &'static str),
|
||||||
|
}
|
||||||
|
|
||||||
|
impl std::fmt::Display for ModuleMember {
|
||||||
|
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
|
||||||
|
match self {
|
||||||
|
ModuleMember::BuiltIn(name) => std::write!(f, "{name}"),
|
||||||
|
ModuleMember::Member(module, member) => std::write!(f, "{module}.{member}"),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns the PEP 585 standard library generic variant for a `typing` module reference, if such
|
||||||
|
/// a variant exists.
|
||||||
|
pub fn to_pep585_generic(expr: &Expr, context: &Context) -> Option<ModuleMember> {
|
||||||
|
context.resolve_call_path(expr).and_then(|call_path| {
|
||||||
|
let [module, name] = call_path.as_slice() else {
|
||||||
|
return None;
|
||||||
|
};
|
||||||
|
PEP_585_GENERICS
|
||||||
|
.iter()
|
||||||
|
.find_map(|((from_module, from_member), (to_module, to_member))| {
|
||||||
|
if module == from_module && name == from_member {
|
||||||
|
if to_module.is_empty() {
|
||||||
|
Some(ModuleMember::BuiltIn(to_member))
|
||||||
|
} else {
|
||||||
|
Some(ModuleMember::Member(to_module, to_member))
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
})
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns `true` if `Expr` represents a reference to a typing object with a
|
#[derive(Debug, Copy, Clone)]
|
||||||
/// PEP 603 built-in.
|
pub enum Pep604Operator {
|
||||||
pub fn is_pep604_builtin(expr: &Expr, context: &Context) -> bool {
|
/// The union operator, e.g., `Union[str, int]`, expressible as `str | int` after PEP 604.
|
||||||
context.resolve_call_path(expr).map_or(false, |call_path| {
|
Union,
|
||||||
context.match_typing_call_path(&call_path, "Optional")
|
/// The union operator, e.g., `Optional[str]`, expressible as `str | None` after PEP 604.
|
||||||
|| context.match_typing_call_path(&call_path, "Union")
|
Optional,
|
||||||
})
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Return the PEP 604 operator variant to which the given subscript [`Expr`] corresponds, if any.
|
||||||
|
pub fn to_pep604_operator(value: &Expr, slice: &Expr, context: &Context) -> Option<Pep604Operator> {
|
||||||
|
/// Returns `true` if any argument in the slice is a string.
|
||||||
|
fn any_arg_is_str(slice: &Expr) -> bool {
|
||||||
|
match &slice.node {
|
||||||
|
ExprKind::Constant(ast::ExprConstant {
|
||||||
|
value: Constant::Str(_),
|
||||||
|
..
|
||||||
|
}) => true,
|
||||||
|
ExprKind::Tuple(ast::ExprTuple { elts, .. }) => elts.iter().any(any_arg_is_str),
|
||||||
|
_ => false,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// If any of the _arguments_ are forward references, we can't use PEP 604.
|
||||||
|
// Ex) `Union["str", "int"]` can't be converted to `"str" | "int"`.
|
||||||
|
if any_arg_is_str(slice) {
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
|
||||||
|
context
|
||||||
|
.resolve_call_path(value)
|
||||||
|
.as_ref()
|
||||||
|
.and_then(|call_path| {
|
||||||
|
if context.match_typing_call_path(call_path, "Optional") {
|
||||||
|
Some(Pep604Operator::Optional)
|
||||||
|
} else if context.match_typing_call_path(call_path, "Union") {
|
||||||
|
Some(Pep604Operator::Union)
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Return `true` if `Expr` represents a reference to a type annotation that resolves to an
|
||||||
|
/// immutable type.
|
||||||
pub fn is_immutable_annotation(context: &Context, expr: &Expr) -> bool {
|
pub fn is_immutable_annotation(context: &Context, expr: &Expr) -> bool {
|
||||||
match &expr.node {
|
match &expr.node {
|
||||||
ExprKind::Name(_) | ExprKind::Attribute(_) => {
|
ExprKind::Name(_) | ExprKind::Attribute(_) => {
|
||||||
|
@ -144,6 +210,7 @@ const IMMUTABLE_FUNCS: &[&[&str]] = &[
|
||||||
&["re", "compile"],
|
&["re", "compile"],
|
||||||
];
|
];
|
||||||
|
|
||||||
|
/// Return `true` if `func` is a function that returns an immutable object.
|
||||||
pub fn is_immutable_func(
|
pub fn is_immutable_func(
|
||||||
context: &Context,
|
context: &Context,
|
||||||
func: &Expr,
|
func: &Expr,
|
||||||
|
|
|
@ -187,15 +187,26 @@ pub const PEP_593_SUBSCRIPTS: &[&[&str]] = &[
|
||||||
&["typing_extensions", "Annotated"],
|
&["typing_extensions", "Annotated"],
|
||||||
];
|
];
|
||||||
|
|
||||||
|
type ModuleMember = (&'static str, &'static str);
|
||||||
|
|
||||||
|
type SymbolReplacement = (ModuleMember, ModuleMember);
|
||||||
|
|
||||||
// See: https://peps.python.org/pep-0585/
|
// See: https://peps.python.org/pep-0585/
|
||||||
pub const PEP_585_BUILTINS_ELIGIBLE: &[&[&str]] = &[
|
pub const PEP_585_GENERICS: &[SymbolReplacement] = &[
|
||||||
&["typing", "Dict"],
|
(("typing", "Dict"), ("", "dict")),
|
||||||
&["typing", "FrozenSet"],
|
(("typing", "FrozenSet"), ("", "frozenset")),
|
||||||
&["typing", "List"],
|
(("typing", "List"), ("", "list")),
|
||||||
&["typing", "Set"],
|
(("typing", "Set"), ("", "set")),
|
||||||
&["typing", "Tuple"],
|
(("typing", "Tuple"), ("", "tuple")),
|
||||||
&["typing", "Type"],
|
(("typing", "Type"), ("", "type")),
|
||||||
&["typing_extensions", "Type"],
|
(("typing_extensions", "Type"), ("", "type")),
|
||||||
|
(("typing", "Deque"), ("collections", "deque")),
|
||||||
|
(("typing_extensions", "Deque"), ("collections", "deque")),
|
||||||
|
(("typing", "DefaultDict"), ("collections", "defaultdict")),
|
||||||
|
(
|
||||||
|
("typing_extensions", "DefaultDict"),
|
||||||
|
("collections", "defaultdict"),
|
||||||
|
),
|
||||||
];
|
];
|
||||||
|
|
||||||
// See: https://github.com/JelleZijlstra/autotyping/blob/0adba5ba0eee33c1de4ad9d0c79acfd737321dd9/autotyping/autotyping.py#L69-L91
|
// See: https://github.com/JelleZijlstra/autotyping/blob/0adba5ba0eee33c1de4ad9d0c79acfd737321dd9/autotyping/autotyping.py#L69-L91
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue