Flag PEP 585 and PEP 604 violations in quoted annotations (#3593)

This commit is contained in:
Charlie Marsh 2023-03-20 11:15:44 -04:00 committed by GitHub
parent 81d0884974
commit 7c0f17279c
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
17 changed files with 173 additions and 98 deletions

View file

@ -2170,9 +2170,8 @@ where
ExprKind::Subscript { value, slice, .. } => { ExprKind::Subscript { value, slice, .. } => {
// Ex) Optional[...], Union[...] // Ex) Optional[...], Union[...]
if self.ctx.in_type_definition if self.ctx.in_type_definition
&& !self.ctx.in_deferred_string_type_definition
&& !self.settings.pyupgrade.keep_runtime_typing && !self.settings.pyupgrade.keep_runtime_typing
&& self.settings.rules.enabled(Rule::TypingUnion) && self.settings.rules.enabled(Rule::NonPEP604Annotation)
&& (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.annotations_future_enabled && self.ctx.annotations_future_enabled
@ -2225,16 +2224,13 @@ where
} }
// Ex) List[...] // Ex) List[...]
if !self.ctx.in_deferred_string_type_definition if !self.settings.pyupgrade.keep_runtime_typing
&& !self.settings.pyupgrade.keep_runtime_typing && self.settings.rules.enabled(Rule::NonPEP585Annotation)
&& self.settings.rules.enabled(Rule::DeprecatedCollectionType)
&& (self.settings.target_version >= PythonVersion::Py39 && (self.settings.target_version >= PythonVersion::Py39
|| (self.settings.target_version >= PythonVersion::Py37 || (self.settings.target_version >= PythonVersion::Py37
&& self.ctx.annotations_future_enabled && self.ctx.annotations_future_enabled
&& self.ctx.in_annotation)) && self.ctx.in_annotation))
&& typing::is_pep585_builtin(expr, |expr| { && typing::is_pep585_builtin(expr, &self.ctx)
self.ctx.resolve_call_path(expr)
})
{ {
pyupgrade::rules::use_pep585_annotation(self, expr); pyupgrade::rules::use_pep585_annotation(self, expr);
} }
@ -2271,14 +2267,13 @@ where
} }
ExprKind::Attribute { attr, value, .. } => { ExprKind::Attribute { attr, value, .. } => {
// Ex) typing.List[...] // Ex) typing.List[...]
if !self.ctx.in_deferred_string_type_definition if !self.settings.pyupgrade.keep_runtime_typing
&& !self.settings.pyupgrade.keep_runtime_typing && self.settings.rules.enabled(Rule::NonPEP585Annotation)
&& self.settings.rules.enabled(Rule::DeprecatedCollectionType)
&& (self.settings.target_version >= PythonVersion::Py39 && (self.settings.target_version >= PythonVersion::Py39
|| (self.settings.target_version >= PythonVersion::Py37 || (self.settings.target_version >= PythonVersion::Py37
&& self.ctx.annotations_future_enabled && self.ctx.annotations_future_enabled
&& self.ctx.in_annotation)) && self.ctx.in_annotation))
&& typing::is_pep585_builtin(expr, |expr| self.ctx.resolve_call_path(expr)) && typing::is_pep585_builtin(expr, &self.ctx)
{ {
pyupgrade::rules::use_pep585_annotation(self, expr); pyupgrade::rules::use_pep585_annotation(self, expr);
} }
@ -2434,7 +2429,7 @@ where
if self.settings.rules.enabled(Rule::OSErrorAlias) { if self.settings.rules.enabled(Rule::OSErrorAlias) {
pyupgrade::rules::os_error_alias_call(self, func); pyupgrade::rules::os_error_alias_call(self, func);
} }
if self.settings.rules.enabled(Rule::IsinstanceWithTuple) if self.settings.rules.enabled(Rule::NonPEP604Isinstance)
&& self.settings.target_version >= PythonVersion::Py310 && self.settings.target_version >= PythonVersion::Py310
{ {
pyupgrade::rules::use_pep604_isinstance(self, expr, func, args); pyupgrade::rules::use_pep604_isinstance(self, expr, func, args);
@ -3575,7 +3570,7 @@ where
} else { } else {
match match_annotated_subscript( match match_annotated_subscript(
value, value,
|expr| self.ctx.resolve_call_path(expr), &self.ctx,
self.settings.typing_modules.iter().map(String::as_str), self.settings.typing_modules.iter().map(String::as_str),
) { ) {
Some(subscript) => { Some(subscript) => {

View file

@ -355,8 +355,8 @@ pub fn code_to_rule(linter: Linter, code: &str) -> Option<Rule> {
(Pyupgrade, "003") => Rule::TypeOfPrimitive, (Pyupgrade, "003") => Rule::TypeOfPrimitive,
(Pyupgrade, "004") => Rule::UselessObjectInheritance, (Pyupgrade, "004") => Rule::UselessObjectInheritance,
(Pyupgrade, "005") => Rule::DeprecatedUnittestAlias, (Pyupgrade, "005") => Rule::DeprecatedUnittestAlias,
(Pyupgrade, "006") => Rule::DeprecatedCollectionType, (Pyupgrade, "006") => Rule::NonPEP585Annotation,
(Pyupgrade, "007") => Rule::TypingUnion, (Pyupgrade, "007") => Rule::NonPEP604Annotation,
(Pyupgrade, "008") => Rule::SuperCallWithParameters, (Pyupgrade, "008") => Rule::SuperCallWithParameters,
(Pyupgrade, "009") => Rule::UTF8EncodingDeclaration, (Pyupgrade, "009") => Rule::UTF8EncodingDeclaration,
(Pyupgrade, "010") => Rule::UnnecessaryFutureImport, (Pyupgrade, "010") => Rule::UnnecessaryFutureImport,
@ -386,7 +386,7 @@ pub fn code_to_rule(linter: Linter, code: &str) -> Option<Rule> {
(Pyupgrade, "035") => Rule::DeprecatedImport, (Pyupgrade, "035") => Rule::DeprecatedImport,
(Pyupgrade, "036") => Rule::OutdatedVersionBlock, (Pyupgrade, "036") => Rule::OutdatedVersionBlock,
(Pyupgrade, "037") => Rule::QuotedAnnotation, (Pyupgrade, "037") => Rule::QuotedAnnotation,
(Pyupgrade, "038") => Rule::IsinstanceWithTuple, (Pyupgrade, "038") => Rule::NonPEP604Isinstance,
// pydocstyle // pydocstyle
(Pydocstyle, "100") => Rule::UndocumentedPublicModule, (Pydocstyle, "100") => Rule::UndocumentedPublicModule,

View file

@ -325,8 +325,8 @@ ruff_macros::register_rules!(
rules::pyupgrade::rules::TypeOfPrimitive, rules::pyupgrade::rules::TypeOfPrimitive,
rules::pyupgrade::rules::UselessObjectInheritance, rules::pyupgrade::rules::UselessObjectInheritance,
rules::pyupgrade::rules::DeprecatedUnittestAlias, rules::pyupgrade::rules::DeprecatedUnittestAlias,
rules::pyupgrade::rules::DeprecatedCollectionType, rules::pyupgrade::rules::NonPEP585Annotation,
rules::pyupgrade::rules::TypingUnion, rules::pyupgrade::rules::NonPEP604Annotation,
rules::pyupgrade::rules::SuperCallWithParameters, rules::pyupgrade::rules::SuperCallWithParameters,
rules::pyupgrade::rules::UTF8EncodingDeclaration, rules::pyupgrade::rules::UTF8EncodingDeclaration,
rules::pyupgrade::rules::UnnecessaryFutureImport, rules::pyupgrade::rules::UnnecessaryFutureImport,
@ -356,7 +356,7 @@ ruff_macros::register_rules!(
rules::pyupgrade::rules::DeprecatedImport, rules::pyupgrade::rules::DeprecatedImport,
rules::pyupgrade::rules::OutdatedVersionBlock, rules::pyupgrade::rules::OutdatedVersionBlock,
rules::pyupgrade::rules::QuotedAnnotation, rules::pyupgrade::rules::QuotedAnnotation,
rules::pyupgrade::rules::IsinstanceWithTuple, rules::pyupgrade::rules::NonPEP604Isinstance,
// pydocstyle // pydocstyle
rules::pydocstyle::rules::UndocumentedPublicModule, rules::pydocstyle::rules::UndocumentedPublicModule,
rules::pydocstyle::rules::UndocumentedPublicClass, rules::pydocstyle::rules::UndocumentedPublicClass,

View file

@ -22,8 +22,8 @@ mod tests {
#[test_case(Rule::TypeOfPrimitive, Path::new("UP003.py"); "UP003")] #[test_case(Rule::TypeOfPrimitive, Path::new("UP003.py"); "UP003")]
#[test_case(Rule::UselessObjectInheritance, Path::new("UP004.py"); "UP004")] #[test_case(Rule::UselessObjectInheritance, Path::new("UP004.py"); "UP004")]
#[test_case(Rule::DeprecatedUnittestAlias, Path::new("UP005.py"); "UP005")] #[test_case(Rule::DeprecatedUnittestAlias, Path::new("UP005.py"); "UP005")]
#[test_case(Rule::DeprecatedCollectionType, Path::new("UP006.py"); "UP006")] #[test_case(Rule::NonPEP585Annotation, Path::new("UP006.py"); "UP006")]
#[test_case(Rule::TypingUnion, Path::new("UP007.py"); "UP007")] #[test_case(Rule::NonPEP604Annotation, Path::new("UP007.py"); "UP007")]
#[test_case(Rule::SuperCallWithParameters, Path::new("UP008.py"); "UP008")] #[test_case(Rule::SuperCallWithParameters, Path::new("UP008.py"); "UP008")]
#[test_case(Rule::UTF8EncodingDeclaration, Path::new("UP009_0.py"); "UP009_0")] #[test_case(Rule::UTF8EncodingDeclaration, Path::new("UP009_0.py"); "UP009_0")]
#[test_case(Rule::UTF8EncodingDeclaration, Path::new("UP009_1.py"); "UP009_1")] #[test_case(Rule::UTF8EncodingDeclaration, Path::new("UP009_1.py"); "UP009_1")]
@ -67,7 +67,7 @@ mod tests {
#[test_case(Rule::OutdatedVersionBlock, Path::new("UP036_3.py"); "UP036_3")] #[test_case(Rule::OutdatedVersionBlock, Path::new("UP036_3.py"); "UP036_3")]
#[test_case(Rule::OutdatedVersionBlock, Path::new("UP036_4.py"); "UP036_4")] #[test_case(Rule::OutdatedVersionBlock, Path::new("UP036_4.py"); "UP036_4")]
#[test_case(Rule::QuotedAnnotation, Path::new("UP037.py"); "UP037")] #[test_case(Rule::QuotedAnnotation, Path::new("UP037.py"); "UP037")]
#[test_case(Rule::IsinstanceWithTuple, Path::new("UP038.py"); "UP038")] #[test_case(Rule::NonPEP604Isinstance, Path::new("UP038.py"); "UP038")]
fn rules(rule_code: Rule, path: &Path) -> Result<()> { fn rules(rule_code: Rule, path: &Path) -> Result<()> {
let snapshot = path.to_string_lossy().to_string(); let snapshot = path.to_string_lossy().to_string();
let diagnostics = test_path( let diagnostics = test_path(
@ -84,7 +84,7 @@ mod tests {
Path::new("pyupgrade/future_annotations.py"), Path::new("pyupgrade/future_annotations.py"),
&settings::Settings { &settings::Settings {
target_version: PythonVersion::Py37, target_version: PythonVersion::Py37,
..settings::Settings::for_rule(Rule::DeprecatedCollectionType) ..settings::Settings::for_rule(Rule::NonPEP585Annotation)
}, },
)?; )?;
assert_yaml_snapshot!(diagnostics); assert_yaml_snapshot!(diagnostics);
@ -97,7 +97,7 @@ mod tests {
Path::new("pyupgrade/future_annotations.py"), Path::new("pyupgrade/future_annotations.py"),
&settings::Settings { &settings::Settings {
target_version: PythonVersion::Py310, target_version: PythonVersion::Py310,
..settings::Settings::for_rule(Rule::DeprecatedCollectionType) ..settings::Settings::for_rule(Rule::NonPEP585Annotation)
}, },
)?; )?;
assert_yaml_snapshot!(diagnostics); assert_yaml_snapshot!(diagnostics);
@ -110,7 +110,7 @@ mod tests {
Path::new("pyupgrade/future_annotations.py"), Path::new("pyupgrade/future_annotations.py"),
&settings::Settings { &settings::Settings {
target_version: PythonVersion::Py37, target_version: PythonVersion::Py37,
..settings::Settings::for_rule(Rule::TypingUnion) ..settings::Settings::for_rule(Rule::NonPEP604Annotation)
}, },
)?; )?;
assert_yaml_snapshot!(diagnostics); assert_yaml_snapshot!(diagnostics);
@ -123,7 +123,7 @@ mod tests {
Path::new("pyupgrade/future_annotations.py"), Path::new("pyupgrade/future_annotations.py"),
&settings::Settings { &settings::Settings {
target_version: PythonVersion::Py310, target_version: PythonVersion::Py310,
..settings::Settings::for_rule(Rule::TypingUnion) ..settings::Settings::for_rule(Rule::NonPEP604Annotation)
}, },
)?; )?;
assert_yaml_snapshot!(diagnostics); assert_yaml_snapshot!(diagnostics);

View file

@ -42,9 +42,9 @@ pub(crate) use unnecessary_future_import::{unnecessary_future_import, Unnecessar
pub(crate) use unpacked_list_comprehension::{ pub(crate) use unpacked_list_comprehension::{
unpacked_list_comprehension, UnpackedListComprehension, unpacked_list_comprehension, UnpackedListComprehension,
}; };
pub(crate) use use_pep585_annotation::{use_pep585_annotation, DeprecatedCollectionType}; pub(crate) use use_pep585_annotation::{use_pep585_annotation, NonPEP585Annotation};
pub(crate) use use_pep604_annotation::{use_pep604_annotation, TypingUnion}; pub(crate) use use_pep604_annotation::{use_pep604_annotation, NonPEP604Annotation};
pub(crate) use use_pep604_isinstance::{use_pep604_isinstance, IsinstanceWithTuple}; pub(crate) use use_pep604_isinstance::{use_pep604_isinstance, NonPEP604Isinstance};
pub(crate) use useless_metaclass_type::{useless_metaclass_type, UselessMetaclassType}; pub(crate) use useless_metaclass_type::{useless_metaclass_type, UselessMetaclassType};
pub(crate) use useless_object_inheritance::{useless_object_inheritance, UselessObjectInheritance}; pub(crate) use useless_object_inheritance::{useless_object_inheritance, UselessObjectInheritance};
pub(crate) use yield_in_for_loop::{yield_in_for_loop, YieldInForLoop}; pub(crate) use yield_in_for_loop::{yield_in_for_loop, YieldInForLoop};

View file

@ -1,22 +1,24 @@
use rustpython_parser::ast::Expr; use rustpython_parser::ast::Expr;
use ruff_diagnostics::{AlwaysAutofixableViolation, Diagnostic, Fix}; use ruff_diagnostics::{AutofixKind, Availability, Diagnostic, Fix, Violation};
use ruff_macros::{derive_message_formats, violation}; use ruff_macros::{derive_message_formats, violation};
use ruff_python_ast::types::Range; use ruff_python_ast::types::Range;
use crate::checkers::ast::Checker; use crate::checkers::ast::Checker;
use crate::registry::AsRule; use crate::registry::AsRule;
// TODO: document referencing [PEP 585]: https://peps.python.org/pep-0585/
#[violation] #[violation]
pub struct DeprecatedCollectionType { pub struct NonPEP585Annotation {
pub name: String, pub name: String,
pub fixable: bool,
} }
impl AlwaysAutofixableViolation for DeprecatedCollectionType { impl Violation for NonPEP585Annotation {
const AUTOFIX: Option<AutofixKind> = Some(AutofixKind::new(Availability::Sometimes));
#[derive_message_formats] #[derive_message_formats]
fn message(&self) -> String { fn message(&self) -> String {
let DeprecatedCollectionType { name } = self; let NonPEP585Annotation { name, .. } = self;
format!( format!(
"Use `{}` instead of `{}` for type annotations", "Use `{}` instead of `{}` for type annotations",
name.to_lowercase(), name.to_lowercase(),
@ -24,9 +26,10 @@ impl AlwaysAutofixableViolation for DeprecatedCollectionType {
) )
} }
fn autofix_title(&self) -> String { fn autofix_title_formatter(&self) -> Option<fn(&Self) -> String> {
let DeprecatedCollectionType { name } = self; self.fixable.then_some(|NonPEP585Annotation { name, .. }| {
format!("Replace `{name}` with `{}`", name.to_lowercase()) format!("Replace `{name}` with `{}`", name.to_lowercase())
})
} }
} }
@ -37,13 +40,15 @@ pub fn use_pep585_annotation(checker: &mut Checker, expr: &Expr) {
.resolve_call_path(expr) .resolve_call_path(expr)
.and_then(|call_path| call_path.last().copied()) .and_then(|call_path| call_path.last().copied())
{ {
let fixable = !checker.ctx.in_deferred_string_type_definition;
let mut diagnostic = Diagnostic::new( let mut diagnostic = Diagnostic::new(
DeprecatedCollectionType { NonPEP585Annotation {
name: binding.to_string(), name: binding.to_string(),
fixable,
}, },
Range::from(expr), Range::from(expr),
); );
if checker.patch(diagnostic.kind.rule()) { if fixable && checker.patch(diagnostic.kind.rule()) {
let binding = binding.to_lowercase(); let binding = binding.to_lowercase();
if checker.ctx.is_builtin(&binding) { if checker.ctx.is_builtin(&binding) {
diagnostic.amend(Fix::replacement( diagnostic.amend(Fix::replacement(

View file

@ -1,6 +1,6 @@
use rustpython_parser::ast::{Constant, Expr, ExprKind, Location, Operator}; use rustpython_parser::ast::{Constant, Expr, ExprKind, Location, Operator};
use ruff_diagnostics::{AlwaysAutofixableViolation, Diagnostic, Fix}; use ruff_diagnostics::{AutofixKind, Availability, Diagnostic, 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_ast::types::Range; use ruff_python_ast::types::Range;
@ -8,18 +8,21 @@ use ruff_python_ast::types::Range;
use crate::checkers::ast::Checker; use crate::checkers::ast::Checker;
use crate::registry::AsRule; use crate::registry::AsRule;
// TODO: document referencing [PEP 604]: https://peps.python.org/pep-0604/
#[violation] #[violation]
pub struct TypingUnion; pub struct NonPEP604Annotation {
pub fixable: bool,
}
impl Violation for NonPEP604Annotation {
const AUTOFIX: Option<AutofixKind> = Some(AutofixKind::new(Availability::Sometimes));
impl AlwaysAutofixableViolation for TypingUnion {
#[derive_message_formats] #[derive_message_formats]
fn message(&self) -> String { fn message(&self) -> String {
format!("Use `X | Y` for type annotations") format!("Use `X | Y` for type annotations")
} }
fn autofix_title(&self) -> String { fn autofix_title_formatter(&self) -> Option<fn(&Self) -> String> {
"Convert to `X | Y`".to_string() self.fixable.then_some(|_| format!("Convert to `X | Y`"))
} }
} }
@ -77,11 +80,6 @@ enum TypingMember {
/// UP007 /// UP007
pub fn use_pep604_annotation(checker: &mut Checker, expr: &Expr, value: &Expr, slice: &Expr) { pub fn use_pep604_annotation(checker: &mut Checker, expr: &Expr, value: &Expr, slice: &Expr) {
// Avoid rewriting forward annotations.
if any_arg_is_str(slice) {
return;
}
let Some(typing_member) = checker.ctx.resolve_call_path(value).as_ref().and_then(|call_path| { 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") { if checker.ctx.match_typing_call_path(call_path, "Optional") {
Some(TypingMember::Optional) Some(TypingMember::Optional)
@ -94,10 +92,14 @@ pub fn use_pep604_annotation(checker: &mut Checker, expr: &Expr, value: &Expr, s
return; return;
}; };
// Avoid fixing forward annotations.
let fixable = !checker.ctx.in_deferred_string_type_definition && !any_arg_is_str(slice);
match typing_member { match typing_member {
TypingMember::Optional => { TypingMember::Optional => {
let mut diagnostic = Diagnostic::new(TypingUnion, Range::from(expr)); let mut diagnostic =
if checker.patch(diagnostic.kind.rule()) { Diagnostic::new(NonPEP604Annotation { fixable }, Range::from(expr));
if fixable && checker.patch(diagnostic.kind.rule()) {
diagnostic.amend(Fix::replacement( diagnostic.amend(Fix::replacement(
unparse_expr(&optional(slice), checker.stylist), unparse_expr(&optional(slice), checker.stylist),
expr.location, expr.location,
@ -107,8 +109,9 @@ pub fn use_pep604_annotation(checker: &mut Checker, expr: &Expr, value: &Expr, s
checker.diagnostics.push(diagnostic); checker.diagnostics.push(diagnostic);
} }
TypingMember::Union => { TypingMember::Union => {
let mut diagnostic = Diagnostic::new(TypingUnion, Range::from(expr)); let mut diagnostic =
if checker.patch(diagnostic.kind.rule()) { Diagnostic::new(NonPEP604Annotation { fixable }, Range::from(expr));
if fixable && checker.patch(diagnostic.kind.rule()) {
match &slice.node { match &slice.node {
ExprKind::Slice { .. } => { ExprKind::Slice { .. } => {
// Invalid type annotation. // Invalid type annotation.

View file

@ -35,13 +35,12 @@ impl CallKind {
} }
} }
// TODO: document referencing [PEP 604]: https://peps.python.org/pep-0604/
#[violation] #[violation]
pub struct IsinstanceWithTuple { pub struct NonPEP604Isinstance {
pub kind: CallKind, pub kind: CallKind,
} }
impl AlwaysAutofixableViolation for IsinstanceWithTuple { impl AlwaysAutofixableViolation for NonPEP604Isinstance {
#[derive_message_formats] #[derive_message_formats]
fn message(&self) -> String { fn message(&self) -> String {
format!("Use `X | Y` in `{}` call instead of `(X, Y)`", self.kind) format!("Use `X | Y` in `{}` call instead of `(X, Y)`", self.kind)
@ -93,7 +92,7 @@ pub fn use_pep604_isinstance(checker: &mut Checker, expr: &Expr, func: &Expr, ar
} }
let mut diagnostic = let mut diagnostic =
Diagnostic::new(IsinstanceWithTuple { kind }, Range::from(expr)); Diagnostic::new(NonPEP604Isinstance { kind }, Range::from(expr));
if checker.patch(diagnostic.kind.rule()) { if checker.patch(diagnostic.kind.rule()) {
diagnostic.amend(Fix::replacement( diagnostic.amend(Fix::replacement(
unparse_expr(&union(elts), checker.stylist), unparse_expr(&union(elts), checker.stylist),

View file

@ -3,7 +3,7 @@ source: crates/ruff/src/rules/pyupgrade/mod.rs
expression: diagnostics expression: diagnostics
--- ---
- kind: - kind:
name: DeprecatedCollectionType name: NonPEP585Annotation
body: "Use `list` instead of `List` for type annotations" body: "Use `list` instead of `List` for type annotations"
suggestion: "Replace `List` with `list`" suggestion: "Replace `List` with `list`"
fixable: true fixable: true
@ -23,7 +23,7 @@ expression: diagnostics
column: 20 column: 20
parent: ~ parent: ~
- kind: - kind:
name: DeprecatedCollectionType name: NonPEP585Annotation
body: "Use `list` instead of `List` for type annotations" body: "Use `list` instead of `List` for type annotations"
suggestion: "Replace `List` with `list`" suggestion: "Replace `List` with `list`"
fixable: true fixable: true
@ -43,7 +43,7 @@ expression: diagnostics
column: 13 column: 13
parent: ~ parent: ~
- kind: - kind:
name: DeprecatedCollectionType name: NonPEP585Annotation
body: "Use `list` instead of `List` for type annotations" body: "Use `list` instead of `List` for type annotations"
suggestion: "Replace `List` with `list`" suggestion: "Replace `List` with `list`"
fixable: true fixable: true
@ -63,7 +63,7 @@ expression: diagnostics
column: 15 column: 15
parent: ~ parent: ~
- kind: - kind:
name: DeprecatedCollectionType name: NonPEP585Annotation
body: "Use `list` instead of `List` for type annotations" body: "Use `list` instead of `List` for type annotations"
suggestion: "Replace `List` with `list`" suggestion: "Replace `List` with `list`"
fixable: true fixable: true
@ -83,7 +83,20 @@ expression: diagnostics
column: 14 column: 14
parent: ~ parent: ~
- kind: - kind:
name: DeprecatedCollectionType name: NonPEP585Annotation
body: "Use `list` instead of `List` for type annotations"
suggestion: ~
fixable: false
location:
row: 29
column: 9
end_location:
row: 29
column: 20
fix: ~
parent: ~
- kind:
name: NonPEP585Annotation
body: "Use `list` instead of `List` for type annotations" body: "Use `list` instead of `List` for type annotations"
suggestion: "Replace `List` with `list`" suggestion: "Replace `List` with `list`"
fixable: true fixable: true

View file

@ -3,7 +3,7 @@ source: crates/ruff/src/rules/pyupgrade/mod.rs
expression: diagnostics expression: diagnostics
--- ---
- kind: - kind:
name: TypingUnion name: NonPEP604Annotation
body: "Use `X | Y` for type annotations" body: "Use `X | Y` for type annotations"
suggestion: "Convert to `X | Y`" suggestion: "Convert to `X | Y`"
fixable: true fixable: true
@ -23,7 +23,7 @@ expression: diagnostics
column: 22 column: 22
parent: ~ parent: ~
- kind: - kind:
name: TypingUnion name: NonPEP604Annotation
body: "Use `X | Y` for type annotations" body: "Use `X | Y` for type annotations"
suggestion: "Convert to `X | Y`" suggestion: "Convert to `X | Y`"
fixable: true fixable: true
@ -43,7 +43,7 @@ expression: diagnostics
column: 29 column: 29
parent: ~ parent: ~
- kind: - kind:
name: TypingUnion name: NonPEP604Annotation
body: "Use `X | Y` for type annotations" body: "Use `X | Y` for type annotations"
suggestion: "Convert to `X | Y`" suggestion: "Convert to `X | Y`"
fixable: true fixable: true
@ -63,7 +63,7 @@ expression: diagnostics
column: 45 column: 45
parent: ~ parent: ~
- kind: - kind:
name: TypingUnion name: NonPEP604Annotation
body: "Use `X | Y` for type annotations" body: "Use `X | Y` for type annotations"
suggestion: "Convert to `X | Y`" suggestion: "Convert to `X | Y`"
fixable: true fixable: true
@ -83,7 +83,7 @@ expression: diagnostics
column: 44 column: 44
parent: ~ parent: ~
- kind: - kind:
name: TypingUnion name: NonPEP604Annotation
body: "Use `X | Y` for type annotations" body: "Use `X | Y` for type annotations"
suggestion: "Convert to `X | Y`" suggestion: "Convert to `X | Y`"
fixable: true fixable: true
@ -103,7 +103,7 @@ expression: diagnostics
column: 31 column: 31
parent: ~ parent: ~
- kind: - kind:
name: TypingUnion name: NonPEP604Annotation
body: "Use `X | Y` for type annotations" body: "Use `X | Y` for type annotations"
suggestion: "Convert to `X | Y`" suggestion: "Convert to `X | Y`"
fixable: true fixable: true
@ -123,7 +123,7 @@ expression: diagnostics
column: 33 column: 33
parent: ~ parent: ~
- kind: - kind:
name: TypingUnion name: NonPEP604Annotation
body: "Use `X | Y` for type annotations" body: "Use `X | Y` for type annotations"
suggestion: "Convert to `X | Y`" suggestion: "Convert to `X | Y`"
fixable: true fixable: true
@ -143,7 +143,72 @@ expression: diagnostics
column: 40 column: 40
parent: ~ parent: ~
- kind: - kind:
name: TypingUnion name: NonPEP604Annotation
body: "Use `X | Y` for type annotations"
suggestion: ~
fixable: false
location:
row: 30
column: 9
end_location:
row: 30
column: 47
fix: ~
parent: ~
- kind:
name: NonPEP604Annotation
body: "Use `X | Y` for type annotations"
suggestion: ~
fixable: false
location:
row: 30
column: 9
end_location:
row: 30
column: 47
fix: ~
parent: ~
- kind:
name: NonPEP604Annotation
body: "Use `X | Y` for type annotations"
suggestion: ~
fixable: false
location:
row: 34
column: 9
end_location:
row: 34
column: 33
fix: ~
parent: ~
- kind:
name: NonPEP604Annotation
body: "Use `X | Y` for type annotations"
suggestion: ~
fixable: false
location:
row: 38
column: 9
end_location:
row: 38
column: 26
fix: ~
parent: ~
- kind:
name: NonPEP604Annotation
body: "Use `X | Y` for type annotations"
suggestion: ~
fixable: false
location:
row: 42
column: 9
end_location:
row: 42
column: 37
fix: ~
parent: ~
- kind:
name: NonPEP604Annotation
body: "Use `X | Y` for type annotations" body: "Use `X | Y` for type annotations"
suggestion: "Convert to `X | Y`" suggestion: "Convert to `X | Y`"
fixable: true fixable: true

View file

@ -3,7 +3,7 @@ source: crates/ruff/src/rules/pyupgrade/mod.rs
expression: diagnostics expression: diagnostics
--- ---
- kind: - kind:
name: IsinstanceWithTuple name: NonPEP604Isinstance
body: "Use `X | Y` in `isinstance` call instead of `(X, Y)`" body: "Use `X | Y` in `isinstance` call instead of `(X, Y)`"
suggestion: "Convert to `X | Y`" suggestion: "Convert to `X | Y`"
fixable: true fixable: true
@ -23,7 +23,7 @@ expression: diagnostics
column: 26 column: 26
parent: ~ parent: ~
- kind: - kind:
name: IsinstanceWithTuple name: NonPEP604Isinstance
body: "Use `X | Y` in `issubclass` call instead of `(X, Y)`" body: "Use `X | Y` in `issubclass` call instead of `(X, Y)`"
suggestion: "Convert to `X | Y`" suggestion: "Convert to `X | Y`"
fixable: true fixable: true

View file

@ -3,7 +3,7 @@ source: crates/ruff/src/rules/pyupgrade/mod.rs
expression: diagnostics expression: diagnostics
--- ---
- kind: - kind:
name: DeprecatedCollectionType name: NonPEP585Annotation
body: "Use `list` instead of `List` for type annotations" body: "Use `list` instead of `List` for type annotations"
suggestion: "Replace `List` with `list`" suggestion: "Replace `List` with `list`"
fixable: true fixable: true

View file

@ -3,7 +3,7 @@ source: crates/ruff/src/rules/pyupgrade/mod.rs
expression: diagnostics expression: diagnostics
--- ---
- kind: - kind:
name: DeprecatedCollectionType name: NonPEP585Annotation
body: "Use `list` instead of `List` for type annotations" body: "Use `list` instead of `List` for type annotations"
suggestion: "Replace `List` with `list`" suggestion: "Replace `List` with `list`"
fixable: true fixable: true
@ -23,7 +23,7 @@ expression: diagnostics
column: 21 column: 21
parent: ~ parent: ~
- kind: - kind:
name: DeprecatedCollectionType name: NonPEP585Annotation
body: "Use `list` instead of `List` for type annotations" body: "Use `list` instead of `List` for type annotations"
suggestion: "Replace `List` with `list`" suggestion: "Replace `List` with `list`"
fixable: true fixable: true
@ -43,7 +43,7 @@ expression: diagnostics
column: 12 column: 12
parent: ~ parent: ~
- kind: - kind:
name: DeprecatedCollectionType name: NonPEP585Annotation
body: "Use `list` instead of `List` for type annotations" body: "Use `list` instead of `List` for type annotations"
suggestion: "Replace `List` with `list`" suggestion: "Replace `List` with `list`"
fixable: true fixable: true
@ -63,7 +63,7 @@ expression: diagnostics
column: 30 column: 30
parent: ~ parent: ~
- kind: - kind:
name: DeprecatedCollectionType name: NonPEP585Annotation
body: "Use `list` instead of `List` for type annotations" body: "Use `list` instead of `List` for type annotations"
suggestion: "Replace `List` with `list`" suggestion: "Replace `List` with `list`"
fixable: true fixable: true

View file

@ -3,7 +3,7 @@ source: crates/ruff/src/rules/pyupgrade/mod.rs
expression: diagnostics expression: diagnostics
--- ---
- kind: - kind:
name: TypingUnion name: NonPEP604Annotation
body: "Use `X | Y` for type annotations" body: "Use `X | Y` for type annotations"
suggestion: "Convert to `X | Y`" suggestion: "Convert to `X | Y`"
fixable: true fixable: true

View file

@ -3,7 +3,7 @@ source: crates/ruff/src/rules/pyupgrade/mod.rs
expression: diagnostics expression: diagnostics
--- ---
- kind: - kind:
name: TypingUnion name: NonPEP604Annotation
body: "Use `X | Y` for type annotations" body: "Use `X | Y` for type annotations"
suggestion: "Convert to `X | Y`" suggestion: "Convert to `X | Y`"
fixable: true fixable: true
@ -23,7 +23,7 @@ expression: diagnostics
column: 16 column: 16
parent: ~ parent: ~
- kind: - kind:
name: TypingUnion name: NonPEP604Annotation
body: "Use `X | Y` for type annotations" body: "Use `X | Y` for type annotations"
suggestion: "Convert to `X | Y`" suggestion: "Convert to `X | Y`"
fixable: true fixable: true

View file

@ -137,7 +137,7 @@ mod tests {
fn redirects() -> Result<()> { fn redirects() -> Result<()> {
let diagnostics = test_path( let diagnostics = test_path(
Path::new("ruff/redirects.py"), Path::new("ruff/redirects.py"),
&settings::Settings::for_rules(vec![Rule::TypingUnion]), &settings::Settings::for_rules(vec![Rule::NonPEP604Annotation]),
)?; )?;
assert_yaml_snapshot!(diagnostics); assert_yaml_snapshot!(diagnostics);
Ok(()) Ok(())

View file

@ -1,7 +1,8 @@
use ruff_python_stdlib::typing::{PEP_585_BUILTINS_ELIGIBLE, PEP_593_SUBSCRIPTS, SUBSCRIPTS};
use rustpython_parser::ast::{Expr, ExprKind}; use rustpython_parser::ast::{Expr, ExprKind};
use crate::types::CallPath; use ruff_python_stdlib::typing::{PEP_585_BUILTINS_ELIGIBLE, PEP_593_SUBSCRIPTS, SUBSCRIPTS};
use crate::context::Context;
pub enum Callable { pub enum Callable {
ForwardRef, ForwardRef,
@ -18,14 +19,11 @@ pub enum SubscriptKind {
PEP593AnnotatedSubscript, PEP593AnnotatedSubscript,
} }
pub fn match_annotated_subscript<'a, F>( pub fn match_annotated_subscript<'a>(
expr: &'a Expr, expr: &Expr,
resolve_call_path: F, context: &Context,
typing_modules: impl Iterator<Item = &'a str>, typing_modules: impl Iterator<Item = &'a str>,
) -> Option<SubscriptKind> ) -> Option<SubscriptKind> {
where
F: FnOnce(&'a Expr) -> Option<CallPath<'a>>,
{
if !matches!( if !matches!(
expr.node, expr.node,
ExprKind::Name { .. } | ExprKind::Attribute { .. } ExprKind::Name { .. } | ExprKind::Attribute { .. }
@ -33,7 +31,7 @@ where
return None; return None;
} }
resolve_call_path(expr).and_then(|call_path| { context.resolve_call_path(expr).and_then(|call_path| {
if SUBSCRIPTS.contains(&call_path.as_slice()) { if SUBSCRIPTS.contains(&call_path.as_slice()) {
return Some(SubscriptKind::AnnotatedSubscript); return Some(SubscriptKind::AnnotatedSubscript);
} }
@ -63,11 +61,8 @@ where
/// Returns `true` if `Expr` represents a reference to a typing object with a /// Returns `true` if `Expr` represents a reference to a typing object with a
/// PEP 585 built-in. /// PEP 585 built-in.
pub fn is_pep585_builtin<'a, F>(expr: &'a Expr, resolve_call_path: F) -> bool pub fn is_pep585_builtin(expr: &Expr, context: &Context) -> bool {
where context.resolve_call_path(expr).map_or(false, |call_path| {
F: FnOnce(&'a Expr) -> Option<CallPath<'a>>,
{
resolve_call_path(expr).map_or(false, |call_path| {
PEP_585_BUILTINS_ELIGIBLE.contains(&call_path.as_slice()) PEP_585_BUILTINS_ELIGIBLE.contains(&call_path.as_slice())
}) })
} }