From fb6c125a72568b395bbc95da7af617cad6816fda Mon Sep 17 00:00:00 2001 From: Justin Prieto Date: Wed, 12 Jul 2023 23:09:06 -0400 Subject: [PATCH 1/5] [`flake8-pyi`] Implement PYI041 --- .../test/fixtures/flake8_pyi/PYI041.py | 39 ++++++ .../test/fixtures/flake8_pyi/PYI041.pyi | 39 ++++++ crates/ruff/src/checkers/ast/mod.rs | 3 + crates/ruff/src/codes.rs | 1 + crates/ruff/src/rules/flake8_pyi/mod.rs | 2 + crates/ruff/src/rules/flake8_pyi/rules/mod.rs | 2 + .../rules/redundant_numeric_union.rs | 125 ++++++++++++++++++ ...__flake8_pyi__tests__PYI041_PYI041.py.snap | 4 + ..._flake8_pyi__tests__PYI041_PYI041.pyi.snap | 44 ++++++ ruff.schema.json | 3 +- 10 files changed, 261 insertions(+), 1 deletion(-) create mode 100644 crates/ruff/resources/test/fixtures/flake8_pyi/PYI041.py create mode 100644 crates/ruff/resources/test/fixtures/flake8_pyi/PYI041.pyi create mode 100644 crates/ruff/src/rules/flake8_pyi/rules/redundant_numeric_union.rs create mode 100644 crates/ruff/src/rules/flake8_pyi/snapshots/ruff__rules__flake8_pyi__tests__PYI041_PYI041.py.snap create mode 100644 crates/ruff/src/rules/flake8_pyi/snapshots/ruff__rules__flake8_pyi__tests__PYI041_PYI041.pyi.snap diff --git a/crates/ruff/resources/test/fixtures/flake8_pyi/PYI041.py b/crates/ruff/resources/test/fixtures/flake8_pyi/PYI041.py new file mode 100644 index 0000000000000..2909126c6c7f9 --- /dev/null +++ b/crates/ruff/resources/test/fixtures/flake8_pyi/PYI041.py @@ -0,0 +1,39 @@ +import builtins +from typing import ( + Union, +) + +from typing_extensions import ( + TypeAlias, +) + +TA0: TypeAlias = int +TA1: TypeAlias = int | float | bool +TA2: TypeAlias = Union[int, float, bool] + + +def good1(arg: int) -> int | bool: ... + + +def good2(arg: int, arg2: int | bool) -> None: ... + + +def f0(arg1: float | int) -> None: ... # PYI041 + + +def f1(arg1: float, *, arg2: float | list[str] | type[bool] | complex) -> None: ... # PYI041 + + +def f2(arg1: int, /, arg2: int | int | float) -> None: ... # PYI041 + + +def f3(arg1: int, *args: Union[int | int | float]) -> None: ... # PYI041 + + +async def f4(**kwargs: int | int | float) -> None: ... # PYI041 + + +class Foo: + def good(self, arg: int) -> None: ... + + def bad(self, arg: int | builtins.float | complex) -> None: ... # PYI041 diff --git a/crates/ruff/resources/test/fixtures/flake8_pyi/PYI041.pyi b/crates/ruff/resources/test/fixtures/flake8_pyi/PYI041.pyi new file mode 100644 index 0000000000000..2909126c6c7f9 --- /dev/null +++ b/crates/ruff/resources/test/fixtures/flake8_pyi/PYI041.pyi @@ -0,0 +1,39 @@ +import builtins +from typing import ( + Union, +) + +from typing_extensions import ( + TypeAlias, +) + +TA0: TypeAlias = int +TA1: TypeAlias = int | float | bool +TA2: TypeAlias = Union[int, float, bool] + + +def good1(arg: int) -> int | bool: ... + + +def good2(arg: int, arg2: int | bool) -> None: ... + + +def f0(arg1: float | int) -> None: ... # PYI041 + + +def f1(arg1: float, *, arg2: float | list[str] | type[bool] | complex) -> None: ... # PYI041 + + +def f2(arg1: int, /, arg2: int | int | float) -> None: ... # PYI041 + + +def f3(arg1: int, *args: Union[int | int | float]) -> None: ... # PYI041 + + +async def f4(**kwargs: int | int | float) -> None: ... # PYI041 + + +class Foo: + def good(self, arg: int) -> None: ... + + def bad(self, arg: int | builtins.float | complex) -> None: ... # PYI041 diff --git a/crates/ruff/src/checkers/ast/mod.rs b/crates/ruff/src/checkers/ast/mod.rs index 930bcc35c7b8f..3d3921f025eaa 100644 --- a/crates/ruff/src/checkers/ast/mod.rs +++ b/crates/ruff/src/checkers/ast/mod.rs @@ -443,6 +443,9 @@ where args, ); } + if self.enabled(Rule::RedundantNumericUnion) { + flake8_pyi::rules::redundant_numeric_union(self, args); + } } if self.enabled(Rule::DunderFunctionName) { if let Some(diagnostic) = pep8_naming::rules::dunder_function_name( diff --git a/crates/ruff/src/codes.rs b/crates/ruff/src/codes.rs index b8b95250ad618..3a2d13a41e1d6 100644 --- a/crates/ruff/src/codes.rs +++ b/crates/ruff/src/codes.rs @@ -639,6 +639,7 @@ pub fn code_to_rule(linter: Linter, code: &str) -> Option<(RuleGroup, Rule)> { (Flake8Pyi, "034") => (RuleGroup::Unspecified, rules::flake8_pyi::rules::NonSelfReturnType), (Flake8Pyi, "035") => (RuleGroup::Unspecified, rules::flake8_pyi::rules::UnassignedSpecialVariableInStub), (Flake8Pyi, "036") => (RuleGroup::Unspecified, rules::flake8_pyi::rules::BadExitAnnotation), + (Flake8Pyi, "041") => (RuleGroup::Unspecified, rules::flake8_pyi::rules::RedundantNumericUnion), (Flake8Pyi, "042") => (RuleGroup::Unspecified, rules::flake8_pyi::rules::SnakeCaseTypeAlias), (Flake8Pyi, "043") => (RuleGroup::Unspecified, rules::flake8_pyi::rules::TSuffixedTypeAlias), (Flake8Pyi, "044") => (RuleGroup::Unspecified, rules::flake8_pyi::rules::FutureAnnotationsInStub), diff --git a/crates/ruff/src/rules/flake8_pyi/mod.rs b/crates/ruff/src/rules/flake8_pyi/mod.rs index 6b8e6e4264fed..b931f17c0b9b2 100644 --- a/crates/ruff/src/rules/flake8_pyi/mod.rs +++ b/crates/ruff/src/rules/flake8_pyi/mod.rs @@ -49,6 +49,8 @@ mod tests { #[test_case(Rule::PassStatementStubBody, Path::new("PYI009.pyi"))] #[test_case(Rule::QuotedAnnotationInStub, Path::new("PYI020.py"))] #[test_case(Rule::QuotedAnnotationInStub, Path::new("PYI020.pyi"))] + #[test_case(Rule::RedundantNumericUnion, Path::new("PYI041.py"))] + #[test_case(Rule::RedundantNumericUnion, Path::new("PYI041.pyi"))] #[test_case(Rule::SnakeCaseTypeAlias, Path::new("PYI042.py"))] #[test_case(Rule::SnakeCaseTypeAlias, Path::new("PYI042.pyi"))] #[test_case(Rule::UnassignedSpecialVariableInStub, Path::new("PYI035.py"))] diff --git a/crates/ruff/src/rules/flake8_pyi/rules/mod.rs b/crates/ruff/src/rules/flake8_pyi/rules/mod.rs index 5fda64cb32913..f16fde0d9e855 100644 --- a/crates/ruff/src/rules/flake8_pyi/rules/mod.rs +++ b/crates/ruff/src/rules/flake8_pyi/rules/mod.rs @@ -16,6 +16,7 @@ pub(crate) use pass_in_class_body::*; pub(crate) use pass_statement_stub_body::*; pub(crate) use prefix_type_params::*; pub(crate) use quoted_annotation_in_stub::*; +pub(crate) use redundant_numeric_union::*; pub(crate) use simple_defaults::*; pub(crate) use str_or_repr_defined_in_stub::*; pub(crate) use string_or_bytes_too_long::*; @@ -45,6 +46,7 @@ mod pass_in_class_body; mod pass_statement_stub_body; mod prefix_type_params; mod quoted_annotation_in_stub; +mod redundant_numeric_union; mod simple_defaults; mod str_or_repr_defined_in_stub; mod string_or_bytes_too_long; diff --git a/crates/ruff/src/rules/flake8_pyi/rules/redundant_numeric_union.rs b/crates/ruff/src/rules/flake8_pyi/rules/redundant_numeric_union.rs new file mode 100644 index 0000000000000..a86f716a41602 --- /dev/null +++ b/crates/ruff/src/rules/flake8_pyi/rules/redundant_numeric_union.rs @@ -0,0 +1,125 @@ +use rustpython_parser::ast::{Arguments, Expr, Ranged}; + +use ruff_diagnostics::{Diagnostic, Violation}; +use ruff_macros::{derive_message_formats, violation}; + +use crate::checkers::ast::Checker; +use crate::rules::flake8_pyi::helpers::traverse_union; + +#[derive(Debug, Clone, Copy, Eq, PartialEq)] +enum Redundancy { + FloatComplex, + IntComplex, + IntFloat, +} + +/// ## What it does +/// Checks for unions in function parameter annotations that contain redundant numeric types. +/// See PEP 3141 for details on the "numeric tower". +/// +/// ## Why is this bad? +/// Unions with redundant elements are less readable than unions without them. +/// +/// ## Example +/// ```python +/// def foo(arg: float | int) -> None: +/// ... +/// ``` +/// +/// Use instead: +/// ```python +/// def foo(arg: float) -> None: +/// ... +/// ``` +#[violation] +pub struct RedundantNumericUnion { + redundancy: Redundancy, +} + +impl Violation for RedundantNumericUnion { + #[derive_message_formats] + fn message(&self) -> String { + let (subtype, supertype) = match self.redundancy { + Redundancy::FloatComplex => ("float", "complex"), + Redundancy::IntComplex => ("int", "complex"), + Redundancy::IntFloat => ("int", "float"), + }; + + format!("`{subtype}` is redundant in a union with `{supertype}`.") + } +} + +fn check_annotation(annotation: &Expr, checker: &mut Checker) { + let mut has_float = false; + let mut has_complex = false; + let mut has_int = false; + + let mut eval_element = |expr: &Expr, _parent: Option<&Expr>| { + let Some(call_path) = checker.semantic().resolve_call_path(expr) else { + return; + }; + + match call_path.as_slice() { + ["" | "builtins", "int"] => has_int = true, + ["" | "builtins", "float"] => has_float = true, + ["" | "builtins", "complex"] => has_complex = true, + _ => (), + } + }; + + traverse_union(&mut eval_element, checker.semantic(), annotation, None); + + if has_complex { + if has_float { + checker.diagnostics.push(Diagnostic::new( + RedundantNumericUnion { + redundancy: Redundancy::FloatComplex, + }, + annotation.range(), + )); + } + + if has_int { + checker.diagnostics.push(Diagnostic::new( + RedundantNumericUnion { + redundancy: Redundancy::IntComplex, + }, + annotation.range(), + )); + } + } else if has_float && has_int { + checker.diagnostics.push(Diagnostic::new( + RedundantNumericUnion { + redundancy: Redundancy::IntFloat, + }, + annotation.range(), + )); + } +} + +/// PYI041 +pub(crate) fn redundant_numeric_union(checker: &mut Checker, args: &Arguments) { + let annotations = args + .args + .iter() + .chain(args.posonlyargs.iter()) + .chain(args.kwonlyargs.iter()) + .filter_map(|arg| arg.def.annotation.as_ref()); + + for annotation in annotations { + check_annotation(annotation, checker); + } + + // If annotations on `args` or `kwargs` are flagged by this rule, the annotations themselves + // are not accurate, but check them anyway. It's possible that flagging them will help the user + // realize they're incorrect. + let args_kwargs_annotations = args + .vararg + .iter() + .chain(args.kwarg.iter()) + .filter_map(|arg| arg.annotation.as_ref()); + + for annotation in args_kwargs_annotations { + check_annotation(annotation, checker); + } +} diff --git a/crates/ruff/src/rules/flake8_pyi/snapshots/ruff__rules__flake8_pyi__tests__PYI041_PYI041.py.snap b/crates/ruff/src/rules/flake8_pyi/snapshots/ruff__rules__flake8_pyi__tests__PYI041_PYI041.py.snap new file mode 100644 index 0000000000000..d1aa2e9116558 --- /dev/null +++ b/crates/ruff/src/rules/flake8_pyi/snapshots/ruff__rules__flake8_pyi__tests__PYI041_PYI041.py.snap @@ -0,0 +1,4 @@ +--- +source: crates/ruff/src/rules/flake8_pyi/mod.rs +--- + diff --git a/crates/ruff/src/rules/flake8_pyi/snapshots/ruff__rules__flake8_pyi__tests__PYI041_PYI041.pyi.snap b/crates/ruff/src/rules/flake8_pyi/snapshots/ruff__rules__flake8_pyi__tests__PYI041_PYI041.pyi.snap new file mode 100644 index 0000000000000..5f5d2d507c6e2 --- /dev/null +++ b/crates/ruff/src/rules/flake8_pyi/snapshots/ruff__rules__flake8_pyi__tests__PYI041_PYI041.pyi.snap @@ -0,0 +1,44 @@ +--- +source: crates/ruff/src/rules/flake8_pyi/mod.rs +--- +PYI041.pyi:21:14: PYI041 `int` is redundant in a union with `float`. + | +21 | def f0(arg1: float | int) -> None: ... # PYI041 + | ^^^^^^^^^^^ PYI041 + | + +PYI041.pyi:24:30: PYI041 `float` is redundant in a union with `complex`. + | +24 | def f1(arg1: float, *, arg2: float | list[str] | type[bool] | complex) -> None: ... # PYI041 + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ PYI041 + | + +PYI041.pyi:27:28: PYI041 `int` is redundant in a union with `float`. + | +27 | def f2(arg1: int, /, arg2: int | int | float) -> None: ... # PYI041 + | ^^^^^^^^^^^^^^^^^ PYI041 + | + +PYI041.pyi:33:24: PYI041 `int` is redundant in a union with `float`. + | +33 | async def f4(**kwargs: int | int | float) -> None: ... # PYI041 + | ^^^^^^^^^^^^^^^^^ PYI041 + | + +PYI041.pyi:39:24: PYI041 `float` is redundant in a union with `complex`. + | +37 | def good(self, arg: int) -> None: ... +38 | +39 | def bad(self, arg: int | builtins.float | complex) -> None: ... # PYI041 + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ PYI041 + | + +PYI041.pyi:39:24: PYI041 `int` is redundant in a union with `complex`. + | +37 | def good(self, arg: int) -> None: ... +38 | +39 | def bad(self, arg: int | builtins.float | complex) -> None: ... # PYI041 + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ PYI041 + | + + diff --git a/ruff.schema.json b/ruff.schema.json index ac8dfa68b5696..6f01e166ab955 100644 --- a/ruff.schema.json +++ b/ruff.schema.json @@ -2357,6 +2357,7 @@ "PYI035", "PYI036", "PYI04", + "PYI041", "PYI042", "PYI043", "PYI044", @@ -2697,4 +2698,4 @@ "type": "string" } } -} \ No newline at end of file +} From 744eb6b993d028bd591e64bb1a6f9924d6066732 Mon Sep 17 00:00:00 2001 From: Charlie Marsh Date: Thu, 13 Jul 2023 00:28:16 -0400 Subject: [PATCH 2/5] Tweak message --- .../test/fixtures/flake8_pyi/PYI041.py | 28 ++++-- .../test/fixtures/flake8_pyi/PYI041.pyi | 3 +- .../rules/redundant_numeric_union.rs | 92 ++++++++++--------- ..._flake8_pyi__tests__PYI041_PYI041.pyi.snap | 36 ++++---- 4 files changed, 87 insertions(+), 72 deletions(-) diff --git a/crates/ruff/resources/test/fixtures/flake8_pyi/PYI041.py b/crates/ruff/resources/test/fixtures/flake8_pyi/PYI041.py index 2909126c6c7f9..f93f5e43f726f 100644 --- a/crates/ruff/resources/test/fixtures/flake8_pyi/PYI041.py +++ b/crates/ruff/resources/test/fixtures/flake8_pyi/PYI041.py @@ -1,4 +1,3 @@ -import builtins from typing import ( Union, ) @@ -12,28 +11,37 @@ TA2: TypeAlias = Union[int, float, bool] -def good1(arg: int) -> int | bool: ... +def good1(arg: int) -> int | bool: + ... -def good2(arg: int, arg2: int | bool) -> None: ... +def good2(arg: int, arg2: int | bool) -> None: + ... -def f0(arg1: float | int) -> None: ... # PYI041 +def f0(arg1: float | int) -> None: + ... # PYI041 -def f1(arg1: float, *, arg2: float | list[str] | type[bool] | complex) -> None: ... # PYI041 +def f1(arg1: float, *, arg2: float | list[str] | type[bool] | complex) -> None: + ... # PYI041 -def f2(arg1: int, /, arg2: int | int | float) -> None: ... # PYI041 +def f2(arg1: int, /, arg2: int | int | float) -> None: + ... # PYI041 -def f3(arg1: int, *args: Union[int | int | float]) -> None: ... # PYI041 +def f3(arg1: int, *args: Union[int | int | float]) -> None: + ... # PYI041 -async def f4(**kwargs: int | int | float) -> None: ... # PYI041 +async def f4(**kwargs: int | int | float) -> None: + ... # PYI041 class Foo: - def good(self, arg: int) -> None: ... + def good(self, arg: int) -> None: + ... - def bad(self, arg: int | builtins.float | complex) -> None: ... # PYI041 + def bad(self, arg: int | float | complex) -> None: + ... # PYI041 diff --git a/crates/ruff/resources/test/fixtures/flake8_pyi/PYI041.pyi b/crates/ruff/resources/test/fixtures/flake8_pyi/PYI041.pyi index 2909126c6c7f9..c3e1c7f4e637e 100644 --- a/crates/ruff/resources/test/fixtures/flake8_pyi/PYI041.pyi +++ b/crates/ruff/resources/test/fixtures/flake8_pyi/PYI041.pyi @@ -1,4 +1,3 @@ -import builtins from typing import ( Union, ) @@ -36,4 +35,4 @@ async def f4(**kwargs: int | int | float) -> None: ... # PYI041 class Foo: def good(self, arg: int) -> None: ... - def bad(self, arg: int | builtins.float | complex) -> None: ... # PYI041 + def bad(self, arg: int | float | complex) -> None: ... # PYI041 diff --git a/crates/ruff/src/rules/flake8_pyi/rules/redundant_numeric_union.rs b/crates/ruff/src/rules/flake8_pyi/rules/redundant_numeric_union.rs index a86f716a41602..740c9e342a593 100644 --- a/crates/ruff/src/rules/flake8_pyi/rules/redundant_numeric_union.rs +++ b/crates/ruff/src/rules/flake8_pyi/rules/redundant_numeric_union.rs @@ -6,31 +6,35 @@ use ruff_macros::{derive_message_formats, violation}; use crate::checkers::ast::Checker; use crate::rules::flake8_pyi::helpers::traverse_union; -#[derive(Debug, Clone, Copy, Eq, PartialEq)] -enum Redundancy { - FloatComplex, - IntComplex, - IntFloat, -} - /// ## What it does -/// Checks for unions in function parameter annotations that contain redundant numeric types. -/// See PEP 3141 for details on the "numeric tower". +/// Checks for union annotations that contain redundant numeric types (e.g., +/// `int | float`). /// /// ## Why is this bad? +/// In Python, `int` is a subtype of `float`, and `float` is a subtype of +/// `complex`. As such, a union that includes both `int` and `float` is +/// redundant, as it is equivalent to a union that only includes `float`. +/// +/// For more, see [PEP 3141], which defines Python's "numeric tower". +/// /// Unions with redundant elements are less readable than unions without them. /// /// ## Example /// ```python -/// def foo(arg: float | int) -> None: +/// def foo(x: float | int) -> None: /// ... /// ``` /// /// Use instead: /// ```python -/// def foo(arg: float) -> None: +/// def foo(x: float) -> None: /// ... /// ``` +/// +/// ## References +/// - [Python documentation: The numeric tower](https://docs.python.org/3/library/numbers.html#the-numeric-tower) +/// +/// [PEP 3141]: https://peps.python.org/pep-3141/ #[violation] pub struct RedundantNumericUnion { redundancy: Redundancy, @@ -44,17 +48,48 @@ impl Violation for RedundantNumericUnion { Redundancy::IntComplex => ("int", "complex"), Redundancy::IntFloat => ("int", "float"), }; + format!("Use `{supertype}` instead of `{subtype} | {supertype}`") + } +} - format!("`{subtype}` is redundant in a union with `{supertype}`.") +/// PYI041 +pub(crate) fn redundant_numeric_union(checker: &mut Checker, args: &Arguments) { + for annotation in args + .args + .iter() + .chain(args.posonlyargs.iter()) + .chain(args.kwonlyargs.iter()) + .filter_map(|arg| arg.def.annotation.as_ref()) + { + check_annotation(checker, annotation); + } + + // If annotations on `args` or `kwargs` are flagged by this rule, the annotations themselves + // are not accurate, but check them anyway. It's possible that flagging them will help the user + // realize they're incorrect. + for annotation in args + .vararg + .iter() + .chain(args.kwarg.iter()) + .filter_map(|arg| arg.annotation.as_ref()) + { + check_annotation(checker, annotation); } } -fn check_annotation(annotation: &Expr, checker: &mut Checker) { +#[derive(Debug, Clone, Copy, Eq, PartialEq)] +enum Redundancy { + FloatComplex, + IntComplex, + IntFloat, +} + +fn check_annotation(checker: &mut Checker, annotation: &Expr) { let mut has_float = false; let mut has_complex = false; let mut has_int = false; - let mut eval_element = |expr: &Expr, _parent: Option<&Expr>| { + let mut func = |expr: &Expr, _parent: Option<&Expr>| { let Some(call_path) = checker.semantic().resolve_call_path(expr) else { return; }; @@ -67,7 +102,7 @@ fn check_annotation(annotation: &Expr, checker: &mut Checker) { } }; - traverse_union(&mut eval_element, checker.semantic(), annotation, None); + traverse_union(&mut func, checker.semantic(), annotation, None); if has_complex { if has_float { @@ -96,30 +131,3 @@ fn check_annotation(annotation: &Expr, checker: &mut Checker) { )); } } - -/// PYI041 -pub(crate) fn redundant_numeric_union(checker: &mut Checker, args: &Arguments) { - let annotations = args - .args - .iter() - .chain(args.posonlyargs.iter()) - .chain(args.kwonlyargs.iter()) - .filter_map(|arg| arg.def.annotation.as_ref()); - - for annotation in annotations { - check_annotation(annotation, checker); - } - - // If annotations on `args` or `kwargs` are flagged by this rule, the annotations themselves - // are not accurate, but check them anyway. It's possible that flagging them will help the user - // realize they're incorrect. - let args_kwargs_annotations = args - .vararg - .iter() - .chain(args.kwarg.iter()) - .filter_map(|arg| arg.annotation.as_ref()); - - for annotation in args_kwargs_annotations { - check_annotation(annotation, checker); - } -} diff --git a/crates/ruff/src/rules/flake8_pyi/snapshots/ruff__rules__flake8_pyi__tests__PYI041_PYI041.pyi.snap b/crates/ruff/src/rules/flake8_pyi/snapshots/ruff__rules__flake8_pyi__tests__PYI041_PYI041.pyi.snap index 5f5d2d507c6e2..7ffb18bf79c21 100644 --- a/crates/ruff/src/rules/flake8_pyi/snapshots/ruff__rules__flake8_pyi__tests__PYI041_PYI041.pyi.snap +++ b/crates/ruff/src/rules/flake8_pyi/snapshots/ruff__rules__flake8_pyi__tests__PYI041_PYI041.pyi.snap @@ -1,44 +1,44 @@ --- source: crates/ruff/src/rules/flake8_pyi/mod.rs --- -PYI041.pyi:21:14: PYI041 `int` is redundant in a union with `float`. +PYI041.pyi:20:14: PYI041 Use `float` instead of `int | float` | -21 | def f0(arg1: float | int) -> None: ... # PYI041 +20 | def f0(arg1: float | int) -> None: ... # PYI041 | ^^^^^^^^^^^ PYI041 | -PYI041.pyi:24:30: PYI041 `float` is redundant in a union with `complex`. +PYI041.pyi:23:30: PYI041 Use `complex` instead of `float | complex` | -24 | def f1(arg1: float, *, arg2: float | list[str] | type[bool] | complex) -> None: ... # PYI041 +23 | def f1(arg1: float, *, arg2: float | list[str] | type[bool] | complex) -> None: ... # PYI041 | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ PYI041 | -PYI041.pyi:27:28: PYI041 `int` is redundant in a union with `float`. +PYI041.pyi:26:28: PYI041 Use `float` instead of `int | float` | -27 | def f2(arg1: int, /, arg2: int | int | float) -> None: ... # PYI041 +26 | def f2(arg1: int, /, arg2: int | int | float) -> None: ... # PYI041 | ^^^^^^^^^^^^^^^^^ PYI041 | -PYI041.pyi:33:24: PYI041 `int` is redundant in a union with `float`. +PYI041.pyi:32:24: PYI041 Use `float` instead of `int | float` | -33 | async def f4(**kwargs: int | int | float) -> None: ... # PYI041 +32 | async def f4(**kwargs: int | int | float) -> None: ... # PYI041 | ^^^^^^^^^^^^^^^^^ PYI041 | -PYI041.pyi:39:24: PYI041 `float` is redundant in a union with `complex`. +PYI041.pyi:38:24: PYI041 Use `complex` instead of `float | complex` | -37 | def good(self, arg: int) -> None: ... -38 | -39 | def bad(self, arg: int | builtins.float | complex) -> None: ... # PYI041 - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ PYI041 +36 | def good(self, arg: int) -> None: ... +37 | +38 | def bad(self, arg: int | float | complex) -> None: ... # PYI041 + | ^^^^^^^^^^^^^^^^^^^^^ PYI041 | -PYI041.pyi:39:24: PYI041 `int` is redundant in a union with `complex`. +PYI041.pyi:38:24: PYI041 Use `complex` instead of `int | complex` | -37 | def good(self, arg: int) -> None: ... -38 | -39 | def bad(self, arg: int | builtins.float | complex) -> None: ... # PYI041 - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ PYI041 +36 | def good(self, arg: int) -> None: ... +37 | +38 | def bad(self, arg: int | float | complex) -> None: ... # PYI041 + | ^^^^^^^^^^^^^^^^^^^^^ PYI041 | From e0cfe4154a37caba3b77d4a7d6a532843804cab1 Mon Sep 17 00:00:00 2001 From: Justin Prieto Date: Thu, 13 Jul 2023 08:19:08 -0400 Subject: [PATCH 3/5] feedback --- .../resources/test/fixtures/flake8_pyi/PYI041.py | 12 ++++++------ .../resources/test/fixtures/flake8_pyi/PYI041.pyi | 1 + 2 files changed, 7 insertions(+), 6 deletions(-) diff --git a/crates/ruff/resources/test/fixtures/flake8_pyi/PYI041.py b/crates/ruff/resources/test/fixtures/flake8_pyi/PYI041.py index f93f5e43f726f..d1349b3ee3a0f 100644 --- a/crates/ruff/resources/test/fixtures/flake8_pyi/PYI041.py +++ b/crates/ruff/resources/test/fixtures/flake8_pyi/PYI041.py @@ -20,23 +20,23 @@ def good2(arg: int, arg2: int | bool) -> None: def f0(arg1: float | int) -> None: - ... # PYI041 + ... def f1(arg1: float, *, arg2: float | list[str] | type[bool] | complex) -> None: - ... # PYI041 + ... def f2(arg1: int, /, arg2: int | int | float) -> None: - ... # PYI041 + ... def f3(arg1: int, *args: Union[int | int | float]) -> None: - ... # PYI041 + ... async def f4(**kwargs: int | int | float) -> None: - ... # PYI041 + ... class Foo: @@ -44,4 +44,4 @@ def good(self, arg: int) -> None: ... def bad(self, arg: int | float | complex) -> None: - ... # PYI041 + ... diff --git a/crates/ruff/resources/test/fixtures/flake8_pyi/PYI041.pyi b/crates/ruff/resources/test/fixtures/flake8_pyi/PYI041.pyi index c3e1c7f4e637e..07c2bd3fd67a3 100644 --- a/crates/ruff/resources/test/fixtures/flake8_pyi/PYI041.pyi +++ b/crates/ruff/resources/test/fixtures/flake8_pyi/PYI041.pyi @@ -6,6 +6,7 @@ from typing_extensions import ( TypeAlias, ) +# Type aliases not flagged TA0: TypeAlias = int TA1: TypeAlias = int | float | bool TA2: TypeAlias = Union[int, float, bool] From 4a60d1a3ff51e4c08ad7dbf7820f3e3bf15fe202 Mon Sep 17 00:00:00 2001 From: Charlie Marsh Date: Thu, 13 Jul 2023 11:04:09 -0400 Subject: [PATCH 4/5] Update snapshot --- ..._flake8_pyi__tests__PYI041_PYI041.pyi.snap | 32 +++++++++---------- 1 file changed, 16 insertions(+), 16 deletions(-) diff --git a/crates/ruff/src/rules/flake8_pyi/snapshots/ruff__rules__flake8_pyi__tests__PYI041_PYI041.pyi.snap b/crates/ruff/src/rules/flake8_pyi/snapshots/ruff__rules__flake8_pyi__tests__PYI041_PYI041.pyi.snap index 7ffb18bf79c21..f89a27a632992 100644 --- a/crates/ruff/src/rules/flake8_pyi/snapshots/ruff__rules__flake8_pyi__tests__PYI041_PYI041.pyi.snap +++ b/crates/ruff/src/rules/flake8_pyi/snapshots/ruff__rules__flake8_pyi__tests__PYI041_PYI041.pyi.snap @@ -1,43 +1,43 @@ --- source: crates/ruff/src/rules/flake8_pyi/mod.rs --- -PYI041.pyi:20:14: PYI041 Use `float` instead of `int | float` +PYI041.pyi:21:14: PYI041 Use `float` instead of `int | float` | -20 | def f0(arg1: float | int) -> None: ... # PYI041 +21 | def f0(arg1: float | int) -> None: ... # PYI041 | ^^^^^^^^^^^ PYI041 | -PYI041.pyi:23:30: PYI041 Use `complex` instead of `float | complex` +PYI041.pyi:24:30: PYI041 Use `complex` instead of `float | complex` | -23 | def f1(arg1: float, *, arg2: float | list[str] | type[bool] | complex) -> None: ... # PYI041 +24 | def f1(arg1: float, *, arg2: float | list[str] | type[bool] | complex) -> None: ... # PYI041 | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ PYI041 | -PYI041.pyi:26:28: PYI041 Use `float` instead of `int | float` +PYI041.pyi:27:28: PYI041 Use `float` instead of `int | float` | -26 | def f2(arg1: int, /, arg2: int | int | float) -> None: ... # PYI041 +27 | def f2(arg1: int, /, arg2: int | int | float) -> None: ... # PYI041 | ^^^^^^^^^^^^^^^^^ PYI041 | -PYI041.pyi:32:24: PYI041 Use `float` instead of `int | float` +PYI041.pyi:33:24: PYI041 Use `float` instead of `int | float` | -32 | async def f4(**kwargs: int | int | float) -> None: ... # PYI041 +33 | async def f4(**kwargs: int | int | float) -> None: ... # PYI041 | ^^^^^^^^^^^^^^^^^ PYI041 | -PYI041.pyi:38:24: PYI041 Use `complex` instead of `float | complex` +PYI041.pyi:39:24: PYI041 Use `complex` instead of `float | complex` | -36 | def good(self, arg: int) -> None: ... -37 | -38 | def bad(self, arg: int | float | complex) -> None: ... # PYI041 +37 | def good(self, arg: int) -> None: ... +38 | +39 | def bad(self, arg: int | float | complex) -> None: ... # PYI041 | ^^^^^^^^^^^^^^^^^^^^^ PYI041 | -PYI041.pyi:38:24: PYI041 Use `complex` instead of `int | complex` +PYI041.pyi:39:24: PYI041 Use `complex` instead of `int | complex` | -36 | def good(self, arg: int) -> None: ... -37 | -38 | def bad(self, arg: int | float | complex) -> None: ... # PYI041 +37 | def good(self, arg: int) -> None: ... +38 | +39 | def bad(self, arg: int | float | complex) -> None: ... # PYI041 | ^^^^^^^^^^^^^^^^^^^^^ PYI041 | From 53bc303f033d5adb4082b160cd27d79b3980a538 Mon Sep 17 00:00:00 2001 From: Charlie Marsh Date: Thu, 13 Jul 2023 12:41:52 -0400 Subject: [PATCH 5/5] Update schema --- ruff.schema.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ruff.schema.json b/ruff.schema.json index 6f01e166ab955..353136f6944ff 100644 --- a/ruff.schema.json +++ b/ruff.schema.json @@ -2698,4 +2698,4 @@ "type": "string" } } -} +} \ No newline at end of file