diff --git a/crates/ruff/resources/test/fixtures/flake8_pyi/PYI052.py b/crates/ruff/resources/test/fixtures/flake8_pyi/PYI052.py new file mode 100644 index 0000000000..37a4f4d867 --- /dev/null +++ b/crates/ruff/resources/test/fixtures/flake8_pyi/PYI052.py @@ -0,0 +1,93 @@ +import builtins +import typing +from typing import TypeAlias, Final + +field1: int +field2: int = ... +field3 = ... # type: int # Y033 Do not use type comments in stubs (e.g. use "x: int" instead of "x = ... # type: int") +field4: int = 0 +field41: int = 0xFFFFFFFF +field42: int = 1234567890 +field43: int = -0xFFFFFFFF +field44: int = -1234567890 +field5 = 0 # type: int # Y033 Do not use type comments in stubs (e.g. use "x: int" instead of "x = ... # type: int") # Y052 Need type annotation for "field5" +field6 = 0 # Y052 Need type annotation for "field6" +field7 = b"" # Y052 Need type annotation for "field7" +field71 = "foo" # Y052 Need type annotation for "field71" +field72: str = "foo" +field8 = False # Y052 Need type annotation for "field8" +field81 = -1 # Y052 Need type annotation for "field81" +field82: float = -98.43 +field83 = -42j # Y052 Need type annotation for "field83" +field84 = 5 + 42j # Y052 Need type annotation for "field84" +field85 = -5 - 42j # Y052 Need type annotation for "field85" +field9 = None # Y026 Use typing_extensions.TypeAlias for type aliases, e.g. "field9: TypeAlias = None" +Field95: TypeAlias = None +Field96: TypeAlias = int | None +Field97: TypeAlias = None | typing.SupportsInt | builtins.str | float | bool +field19 = [1, 2, 3] # Y052 Need type annotation for "field19" +field191: list[int] = [1, 2, 3] +field20 = (1, 2, 3) # Y052 Need type annotation for "field20" +field201: tuple[int, ...] = (1, 2, 3) +field21 = {1, 2, 3} # Y052 Need type annotation for "field21" +field211: set[int] = {1, 2, 3} +field212 = {"foo": "bar"} # Y052 Need type annotation for "field212" +field213: dict[str, str] = {"foo": "bar"} +field22: Final = {"foo": 5} +field221: list[int] = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11] # Y015 Only simple default values are allowed for assignments +field223: list[int] = [*range(10)] # Y015 Only simple default values are allowed for assignments +field224: list[int] = list(range(10)) # Y015 Only simple default values are allowed for assignments +field225: list[object] = [{}, 1, 2] # Y015 Only simple default values are allowed for assignments +field226: tuple[str | tuple[str, ...], ...] = ("foo", ("foo", "bar")) # Y015 Only simple default values are allowed for assignments +field227: dict[str, object] = {"foo": {"foo": "bar"}} # Y015 Only simple default values are allowed for assignments +field228: dict[str, list[object]] = {"foo": []} # Y015 Only simple default values are allowed for assignments +# When parsed, this case results in `None` being placed in the `.keys` list for the `ast.Dict` node +field229: dict[int, int] = {1: 2, **{3: 4}} # Y015 Only simple default values are allowed for assignments +field23 = "foo" + "bar" # Y015 Only simple default values are allowed for assignments +field24 = b"foo" + b"bar" # Y015 Only simple default values are allowed for assignments +field25 = 5 * 5 # Y015 Only simple default values are allowed for assignments + +# We shouldn't emit Y015 within functions +def f(): + field26: list[int] = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11] + + +# We shouldn't emit Y015 for __slots__ or __match_args__ +class Class1: + __slots__ = ( + '_one', + '_two', + '_three', + '_four', + '_five', + '_six', + '_seven', + '_eight', + '_nine', + '_ten', + '_eleven', + ) + + __match_args__ = ( + 'one', + 'two', + 'three', + 'four', + 'five', + 'six', + 'seven', + 'eight', + 'nine', + 'ten', + 'eleven', + ) + +# We shouldn't emit Y015 for __all__ +__all__ = ["Class1"] + +# Ignore the following for PYI015 +field26 = typing.Sequence[int] +field27 = list[str] +field28 = builtins.str +field29 = str +field30 = str | bytes | None diff --git a/crates/ruff/resources/test/fixtures/flake8_pyi/PYI052.pyi b/crates/ruff/resources/test/fixtures/flake8_pyi/PYI052.pyi new file mode 100644 index 0000000000..860ee255fb --- /dev/null +++ b/crates/ruff/resources/test/fixtures/flake8_pyi/PYI052.pyi @@ -0,0 +1,100 @@ +import builtins +import typing +from typing import TypeAlias, Final, NewType, TypeVar, TypeVarTuple, ParamSpec + +# We shouldn't emit Y015 for simple default values +field1: int +field2: int = ... +field3 = ... # type: int # Y033 Do not use type comments in stubs (e.g. use "x: int" instead of "x = ... # type: int") +field4: int = 0 +field41: int = 0xFFFFFFFF +field42: int = 1234567890 +field43: int = -0xFFFFFFFF +field44: int = -1234567890 +field5 = 0 # type: int # Y033 Do not use type comments in stubs (e.g. use "x: int" instead of "x = ... # type: int") # Y052 Need type annotation for "field5" +field6 = 0 # Y052 Need type annotation for "field6" +field7 = b"" # Y052 Need type annotation for "field7" +field71 = "foo" # Y052 Need type annotation for "field71" +field72: str = "foo" +field8 = False # Y052 Need type annotation for "field8" +field81 = -1 # Y052 Need type annotation for "field81" +field82: float = -98.43 +field83 = -42j # Y052 Need type annotation for "field83" +field84 = 5 + 42j # Y052 Need type annotation for "field84" +field85 = -5 - 42j # Y052 Need type annotation for "field85" +field9 = None # Y026 Use typing_extensions.TypeAlias for type aliases, e.g. "field9: TypeAlias = None" +Field95: TypeAlias = None +Field96: TypeAlias = int | None +Field97: TypeAlias = None | typing.SupportsInt | builtins.str | float | bool +Field98 = NewType('MyInt', int) +Field99 = TypeVar('Field99') +Field100 = TypeVarTuple('Field100') +Field101 = ParamSpec('Field101') +field19 = [1, 2, 3] # Y052 Need type annotation for "field19" +field191: list[int] = [1, 2, 3] +field20 = (1, 2, 3) # Y052 Need type annotation for "field20" +field201: tuple[int, ...] = (1, 2, 3) +field21 = {1, 2, 3} # Y052 Need type annotation for "field21" +field211: set[int] = {1, 2, 3} +field212 = {"foo": "bar"} # Y052 Need type annotation for "field212" +field213: dict[str, str] = {"foo": "bar"} +field22: Final = {"foo": 5} + +# We *should* emit Y015 for more complex default values +field221: list[int] = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11] # Y015 Only simple default values are allowed for assignments +field223: list[int] = [*range(10)] # Y015 Only simple default values are allowed for assignments +field224: list[int] = list(range(10)) # Y015 Only simple default values are allowed for assignments +field225: list[object] = [{}, 1, 2] # Y015 Only simple default values are allowed for assignments +field226: tuple[str | tuple[str, ...], ...] = ("foo", ("foo", "bar")) # Y015 Only simple default values are allowed for assignments +field227: dict[str, object] = {"foo": {"foo": "bar"}} # Y015 Only simple default values are allowed for assignments +field228: dict[str, list[object]] = {"foo": []} # Y015 Only simple default values are allowed for assignments +# When parsed, this case results in `None` being placed in the `.keys` list for the `ast.Dict` node +field229: dict[int, int] = {1: 2, **{3: 4}} # Y015 Only simple default values are allowed for assignments +field23 = "foo" + "bar" # Y015 Only simple default values are allowed for assignments +field24 = b"foo" + b"bar" # Y015 Only simple default values are allowed for assignments +field25 = 5 * 5 # Y015 Only simple default values are allowed for assignments + +# We shouldn't emit Y015 within functions +def f(): + field26: list[int] = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11] + + +# We shouldn't emit Y015 for __slots__ or __match_args__ +class Class1: + __slots__ = ( + '_one', + '_two', + '_three', + '_four', + '_five', + '_six', + '_seven', + '_eight', + '_nine', + '_ten', + '_eleven', + ) + + __match_args__ = ( + 'one', + 'two', + 'three', + 'four', + 'five', + 'six', + 'seven', + 'eight', + 'nine', + 'ten', + 'eleven', + ) + +# We shouldn't emit Y015 for __all__ +__all__ = ["Class1"] + +# Ignore the following for PYI015 +field26 = typing.Sequence[int] +field27 = list[str] +field28 = builtins.str +field29 = str +field30 = str | bytes | None diff --git a/crates/ruff/src/checkers/ast/mod.rs b/crates/ruff/src/checkers/ast/mod.rs index 8ab1e987ac..9424f0ff93 100644 --- a/crates/ruff/src/checkers/ast/mod.rs +++ b/crates/ruff/src/checkers/ast/mod.rs @@ -1752,11 +1752,11 @@ where } if self.is_stub { - if self - .settings - .rules - .any_enabled(&[Rule::UnprefixedTypeParam, Rule::AssignmentDefaultInStub]) - { + if self.settings.rules.any_enabled(&[ + Rule::UnprefixedTypeParam, + Rule::AssignmentDefaultInStub, + Rule::UnannotatedAssignmentInStub, + ]) { // Ignore assignments in function bodies; those are covered by other rules. if !self.ctx.scopes().any(|scope| scope.kind.is_function()) { if self.settings.rules.enabled(Rule::UnprefixedTypeParam) { @@ -1765,6 +1765,15 @@ where if self.settings.rules.enabled(Rule::AssignmentDefaultInStub) { flake8_pyi::rules::assignment_default_in_stub(self, targets, value); } + if self + .settings + .rules + .enabled(Rule::UnannotatedAssignmentInStub) + { + flake8_pyi::rules::unannotated_assignment_in_stub( + self, targets, value, + ); + } } } } diff --git a/crates/ruff/src/codes.rs b/crates/ruff/src/codes.rs index 4443fb4bec..7fdb3fb5e1 100644 --- a/crates/ruff/src/codes.rs +++ b/crates/ruff/src/codes.rs @@ -610,6 +610,7 @@ pub fn code_to_rule(linter: Linter, code: &str) -> Option { (Flake8Pyi, "033") => Rule::TypeCommentInStub, (Flake8Pyi, "042") => Rule::SnakeCaseTypeAlias, (Flake8Pyi, "043") => Rule::TSuffixedTypeAlias, + (Flake8Pyi, "052") => Rule::UnannotatedAssignmentInStub, // flake8-pytest-style (Flake8PytestStyle, "001") => Rule::PytestFixtureIncorrectParenthesesStyle, diff --git a/crates/ruff/src/registry.rs b/crates/ruff/src/registry.rs index e331c2d0a9..01fd3e21ac 100644 --- a/crates/ruff/src/registry.rs +++ b/crates/ruff/src/registry.rs @@ -540,18 +540,19 @@ ruff_macros::register_rules!( rules::flake8_pyi::rules::AssignmentDefaultInStub, rules::flake8_pyi::rules::BadVersionInfoComparison, rules::flake8_pyi::rules::DocstringInStub, - rules::flake8_pyi::rules::NonEmptyStubBody, - rules::flake8_pyi::rules::PassStatementStubBody, - rules::flake8_pyi::rules::TypeCommentInStub, - rules::flake8_pyi::rules::TypedArgumentDefaultInStub, - rules::flake8_pyi::rules::UnprefixedTypeParam, - rules::flake8_pyi::rules::UnrecognizedPlatformCheck, - rules::flake8_pyi::rules::UnrecognizedPlatformName, - rules::flake8_pyi::rules::PassInClassBody, rules::flake8_pyi::rules::DuplicateUnionMember, + rules::flake8_pyi::rules::NonEmptyStubBody, + rules::flake8_pyi::rules::PassInClassBody, + rules::flake8_pyi::rules::PassStatementStubBody, rules::flake8_pyi::rules::QuotedAnnotationInStub, rules::flake8_pyi::rules::SnakeCaseTypeAlias, rules::flake8_pyi::rules::TSuffixedTypeAlias, + rules::flake8_pyi::rules::TypeCommentInStub, + rules::flake8_pyi::rules::TypedArgumentDefaultInStub, + rules::flake8_pyi::rules::UnannotatedAssignmentInStub, + rules::flake8_pyi::rules::UnprefixedTypeParam, + rules::flake8_pyi::rules::UnrecognizedPlatformCheck, + rules::flake8_pyi::rules::UnrecognizedPlatformName, // flake8-pytest-style rules::flake8_pytest_style::rules::PytestFixtureIncorrectParenthesesStyle, rules::flake8_pytest_style::rules::PytestFixturePositionalArgs, diff --git a/crates/ruff/src/rules/flake8_pyi/mod.rs b/crates/ruff/src/rules/flake8_pyi/mod.rs index fd800a2db9..e923989379 100644 --- a/crates/ruff/src/rules/flake8_pyi/mod.rs +++ b/crates/ruff/src/rules/flake8_pyi/mod.rs @@ -13,38 +13,40 @@ mod tests { use crate::test::test_path; use crate::{assert_messages, settings}; - #[test_case(Rule::UnprefixedTypeParam, Path::new("PYI001.py"))] - #[test_case(Rule::UnprefixedTypeParam, Path::new("PYI001.pyi"))] - #[test_case(Rule::BadVersionInfoComparison, Path::new("PYI006.py"))] - #[test_case(Rule::BadVersionInfoComparison, Path::new("PYI006.pyi"))] - #[test_case(Rule::UnrecognizedPlatformCheck, Path::new("PYI007.py"))] - #[test_case(Rule::UnrecognizedPlatformCheck, Path::new("PYI007.pyi"))] - #[test_case(Rule::UnrecognizedPlatformName, Path::new("PYI008.py"))] - #[test_case(Rule::UnrecognizedPlatformName, Path::new("PYI008.pyi"))] - #[test_case(Rule::PassStatementStubBody, Path::new("PYI009.py"))] - #[test_case(Rule::PassStatementStubBody, Path::new("PYI009.pyi"))] - #[test_case(Rule::NonEmptyStubBody, Path::new("PYI010.py"))] - #[test_case(Rule::NonEmptyStubBody, Path::new("PYI010.pyi"))] - #[test_case(Rule::TypedArgumentDefaultInStub, Path::new("PYI011.py"))] - #[test_case(Rule::TypedArgumentDefaultInStub, Path::new("PYI011.pyi"))] - #[test_case(Rule::PassInClassBody, Path::new("PYI012.py"))] - #[test_case(Rule::PassInClassBody, Path::new("PYI012.pyi"))] #[test_case(Rule::ArgumentDefaultInStub, Path::new("PYI014.py"))] #[test_case(Rule::ArgumentDefaultInStub, Path::new("PYI014.pyi"))] #[test_case(Rule::AssignmentDefaultInStub, Path::new("PYI015.py"))] #[test_case(Rule::AssignmentDefaultInStub, Path::new("PYI015.pyi"))] - #[test_case(Rule::DuplicateUnionMember, Path::new("PYI016.py"))] - #[test_case(Rule::DuplicateUnionMember, Path::new("PYI016.pyi"))] - #[test_case(Rule::QuotedAnnotationInStub, Path::new("PYI020.py"))] - #[test_case(Rule::QuotedAnnotationInStub, Path::new("PYI020.pyi"))] + #[test_case(Rule::BadVersionInfoComparison, Path::new("PYI006.py"))] + #[test_case(Rule::BadVersionInfoComparison, Path::new("PYI006.pyi"))] #[test_case(Rule::DocstringInStub, Path::new("PYI021.py"))] #[test_case(Rule::DocstringInStub, Path::new("PYI021.pyi"))] - #[test_case(Rule::TypeCommentInStub, Path::new("PYI033.py"))] - #[test_case(Rule::TypeCommentInStub, Path::new("PYI033.pyi"))] + #[test_case(Rule::DuplicateUnionMember, Path::new("PYI016.py"))] + #[test_case(Rule::DuplicateUnionMember, Path::new("PYI016.pyi"))] + #[test_case(Rule::NonEmptyStubBody, Path::new("PYI010.py"))] + #[test_case(Rule::NonEmptyStubBody, Path::new("PYI010.pyi"))] + #[test_case(Rule::PassInClassBody, Path::new("PYI012.py"))] + #[test_case(Rule::PassInClassBody, Path::new("PYI012.pyi"))] + #[test_case(Rule::PassStatementStubBody, Path::new("PYI009.py"))] + #[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::SnakeCaseTypeAlias, Path::new("PYI042.py"))] #[test_case(Rule::SnakeCaseTypeAlias, Path::new("PYI042.pyi"))] #[test_case(Rule::TSuffixedTypeAlias, Path::new("PYI043.py"))] #[test_case(Rule::TSuffixedTypeAlias, Path::new("PYI043.pyi"))] + #[test_case(Rule::TypeCommentInStub, Path::new("PYI033.py"))] + #[test_case(Rule::TypeCommentInStub, Path::new("PYI033.pyi"))] + #[test_case(Rule::TypedArgumentDefaultInStub, Path::new("PYI011.py"))] + #[test_case(Rule::TypedArgumentDefaultInStub, Path::new("PYI011.pyi"))] + #[test_case(Rule::UnannotatedAssignmentInStub, Path::new("PYI052.py"))] + #[test_case(Rule::UnannotatedAssignmentInStub, Path::new("PYI052.pyi"))] + #[test_case(Rule::UnprefixedTypeParam, Path::new("PYI001.py"))] + #[test_case(Rule::UnprefixedTypeParam, Path::new("PYI001.pyi"))] + #[test_case(Rule::UnrecognizedPlatformCheck, Path::new("PYI007.py"))] + #[test_case(Rule::UnrecognizedPlatformCheck, Path::new("PYI007.pyi"))] + #[test_case(Rule::UnrecognizedPlatformName, Path::new("PYI008.py"))] + #[test_case(Rule::UnrecognizedPlatformName, Path::new("PYI008.pyi"))] fn rules(rule_code: Rule, path: &Path) -> Result<()> { let snapshot = format!("{}_{}", rule_code.noqa_code(), path.to_string_lossy()); let diagnostics = test_path( diff --git a/crates/ruff/src/rules/flake8_pyi/rules/mod.rs b/crates/ruff/src/rules/flake8_pyi/rules/mod.rs index 833286ace5..c26ec99be5 100644 --- a/crates/ruff/src/rules/flake8_pyi/rules/mod.rs +++ b/crates/ruff/src/rules/flake8_pyi/rules/mod.rs @@ -10,8 +10,8 @@ pub(crate) use prefix_type_params::{prefix_type_params, UnprefixedTypeParam}; pub(crate) use quoted_annotation_in_stub::{quoted_annotation_in_stub, QuotedAnnotationInStub}; pub(crate) use simple_defaults::{ annotated_assignment_default_in_stub, argument_simple_defaults, assignment_default_in_stub, - typed_argument_simple_defaults, ArgumentDefaultInStub, AssignmentDefaultInStub, - TypedArgumentDefaultInStub, + typed_argument_simple_defaults, unannotated_assignment_in_stub, ArgumentDefaultInStub, + AssignmentDefaultInStub, TypedArgumentDefaultInStub, UnannotatedAssignmentInStub, }; pub(crate) use type_alias_naming::{ snake_case_type_alias, t_suffixed_type_alias, SnakeCaseTypeAlias, TSuffixedTypeAlias, diff --git a/crates/ruff/src/rules/flake8_pyi/rules/simple_defaults.rs b/crates/ruff/src/rules/flake8_pyi/rules/simple_defaults.rs index 07e0c59615..b0446603f9 100644 --- a/crates/ruff/src/rules/flake8_pyi/rules/simple_defaults.rs +++ b/crates/ruff/src/rules/flake8_pyi/rules/simple_defaults.rs @@ -1,8 +1,9 @@ use rustpython_parser::ast::{self, Arguments, Constant, Expr, ExprKind, Operator, Unaryop}; -use ruff_diagnostics::{AlwaysAutofixableViolation, Diagnostic, Edit, Fix}; +use ruff_diagnostics::{AlwaysAutofixableViolation, Diagnostic, Edit, Fix, Violation}; use ruff_macros::{derive_message_formats, violation}; use ruff_python_semantic::context::Context; +use ruff_python_semantic::scope::{ClassDef, ScopeKind}; use crate::checkers::ast::Checker; use crate::registry::AsRule; @@ -49,6 +50,19 @@ impl AlwaysAutofixableViolation for AssignmentDefaultInStub { } } +#[violation] +pub struct UnannotatedAssignmentInStub { + name: String, +} + +impl Violation for UnannotatedAssignmentInStub { + #[derive_message_formats] + fn message(&self) -> String { + let UnannotatedAssignmentInStub { name } = self; + format!("Need type annotation for `{name}`") + } +} + const ALLOWED_MATH_ATTRIBUTES_IN_DEFAULTS: &[&[&str]] = &[ &["math", "inf"], &["math", "nan"], @@ -285,6 +299,21 @@ fn is_special_assignment(context: &Context, target: &Expr) -> bool { } } +/// Returns `true` if the a class is an enum, based on its base classes. +fn is_enum(context: &Context, bases: &[Expr]) -> bool { + return bases.iter().any(|expr| { + context.resolve_call_path(expr).map_or(false, |call_path| { + matches!( + call_path.as_slice(), + [ + "enum", + "Enum" | "Flag" | "IntEnum" | "IntFlag" | "StrEnum" | "ReprEnum" + ] + ) + }) + }); +} + /// PYI011 pub(crate) fn typed_argument_simple_defaults(checker: &mut Checker, args: &Arguments) { if !args.defaults.is_empty() { @@ -401,7 +430,14 @@ pub(crate) fn argument_simple_defaults(checker: &mut Checker, args: &Arguments) /// PYI015 pub(crate) fn assignment_default_in_stub(checker: &mut Checker, targets: &[Expr], value: &Expr) { - if targets.len() == 1 && is_special_assignment(&checker.ctx, &targets[0]) { + if targets.len() != 1 { + return; + } + let target = &targets[0]; + if !matches!(target.node, ExprKind::Name(..)) { + return; + } + if is_special_assignment(&checker.ctx, target) { return; } if is_type_var_like_call(&checker.ctx, value) { @@ -455,3 +491,42 @@ pub(crate) fn annotated_assignment_default_in_stub( } checker.diagnostics.push(diagnostic); } + +/// PYI052 +pub(crate) fn unannotated_assignment_in_stub( + checker: &mut Checker, + targets: &[Expr], + value: &Expr, +) { + if targets.len() != 1 { + return; + } + let target = &targets[0]; + let ExprKind::Name(ast::ExprName { id, .. }) = &target.node else { + return; + }; + if is_special_assignment(&checker.ctx, target) { + return; + } + if is_type_var_like_call(&checker.ctx, value) { + return; + } + if is_valid_default_value_without_annotation(value) { + return; + } + if !is_valid_default_value_with_annotation(value, checker, true) { + return; + } + + if let ScopeKind::Class(ClassDef { bases, .. }) = &checker.ctx.scope().kind { + if is_enum(&checker.ctx, bases) { + return; + } + } + checker.diagnostics.push(Diagnostic::new( + UnannotatedAssignmentInStub { + name: id.to_string(), + }, + value.range(), + )); +} diff --git a/crates/ruff/src/rules/flake8_pyi/snapshots/ruff__rules__flake8_pyi__tests__PYI052_PYI052.py.snap b/crates/ruff/src/rules/flake8_pyi/snapshots/ruff__rules__flake8_pyi__tests__PYI052_PYI052.py.snap new file mode 100644 index 0000000000..d1aa2e9116 --- /dev/null +++ b/crates/ruff/src/rules/flake8_pyi/snapshots/ruff__rules__flake8_pyi__tests__PYI052_PYI052.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__PYI052_PYI052.pyi.snap b/crates/ruff/src/rules/flake8_pyi/snapshots/ruff__rules__flake8_pyi__tests__PYI052_PYI052.pyi.snap new file mode 100644 index 0000000000..cda8ebb101 --- /dev/null +++ b/crates/ruff/src/rules/flake8_pyi/snapshots/ruff__rules__flake8_pyi__tests__PYI052_PYI052.pyi.snap @@ -0,0 +1,134 @@ +--- +source: crates/ruff/src/rules/flake8_pyi/mod.rs +--- +PYI052.pyi:14:10: PYI052 Need type annotation for `field5` + | +14 | field43: int = -0xFFFFFFFF +15 | field44: int = -1234567890 +16 | field5 = 0 # type: int # Y033 Do not use type comments in stubs (e.g. use "x: int" instead of "x = ... # type: int") # Y052 Need type annotation for "field5" + | ^ PYI052 +17 | field6 = 0 # Y052 Need type annotation for "field6" +18 | field7 = b"" # Y052 Need type annotation for "field7" + | + +PYI052.pyi:15:10: PYI052 Need type annotation for `field6` + | +15 | field44: int = -1234567890 +16 | field5 = 0 # type: int # Y033 Do not use type comments in stubs (e.g. use "x: int" instead of "x = ... # type: int") # Y052 Need type annotation for "field5" +17 | field6 = 0 # Y052 Need type annotation for "field6" + | ^ PYI052 +18 | field7 = b"" # Y052 Need type annotation for "field7" +19 | field71 = "foo" # Y052 Need type annotation for "field71" + | + +PYI052.pyi:16:10: PYI052 Need type annotation for `field7` + | +16 | field5 = 0 # type: int # Y033 Do not use type comments in stubs (e.g. use "x: int" instead of "x = ... # type: int") # Y052 Need type annotation for "field5" +17 | field6 = 0 # Y052 Need type annotation for "field6" +18 | field7 = b"" # Y052 Need type annotation for "field7" + | ^^^ PYI052 +19 | field71 = "foo" # Y052 Need type annotation for "field71" +20 | field72: str = "foo" + | + +PYI052.pyi:17:11: PYI052 Need type annotation for `field71` + | +17 | field6 = 0 # Y052 Need type annotation for "field6" +18 | field7 = b"" # Y052 Need type annotation for "field7" +19 | field71 = "foo" # Y052 Need type annotation for "field71" + | ^^^^^ PYI052 +20 | field72: str = "foo" +21 | field8 = False # Y052 Need type annotation for "field8" + | + +PYI052.pyi:19:10: PYI052 Need type annotation for `field8` + | +19 | field71 = "foo" # Y052 Need type annotation for "field71" +20 | field72: str = "foo" +21 | field8 = False # Y052 Need type annotation for "field8" + | ^^^^^ PYI052 +22 | field81 = -1 # Y052 Need type annotation for "field81" +23 | field82: float = -98.43 + | + +PYI052.pyi:20:11: PYI052 Need type annotation for `field81` + | +20 | field72: str = "foo" +21 | field8 = False # Y052 Need type annotation for "field8" +22 | field81 = -1 # Y052 Need type annotation for "field81" + | ^^ PYI052 +23 | field82: float = -98.43 +24 | field83 = -42j # Y052 Need type annotation for "field83" + | + +PYI052.pyi:22:11: PYI052 Need type annotation for `field83` + | +22 | field81 = -1 # Y052 Need type annotation for "field81" +23 | field82: float = -98.43 +24 | field83 = -42j # Y052 Need type annotation for "field83" + | ^^^^ PYI052 +25 | field84 = 5 + 42j # Y052 Need type annotation for "field84" +26 | field85 = -5 - 42j # Y052 Need type annotation for "field85" + | + +PYI052.pyi:23:11: PYI052 Need type annotation for `field84` + | +23 | field82: float = -98.43 +24 | field83 = -42j # Y052 Need type annotation for "field83" +25 | field84 = 5 + 42j # Y052 Need type annotation for "field84" + | ^^^^^^^ PYI052 +26 | field85 = -5 - 42j # Y052 Need type annotation for "field85" +27 | field9 = None # Y026 Use typing_extensions.TypeAlias for type aliases, e.g. "field9: TypeAlias = None" + | + +PYI052.pyi:24:11: PYI052 Need type annotation for `field85` + | +24 | field83 = -42j # Y052 Need type annotation for "field83" +25 | field84 = 5 + 42j # Y052 Need type annotation for "field84" +26 | field85 = -5 - 42j # Y052 Need type annotation for "field85" + | ^^^^^^^^ PYI052 +27 | field9 = None # Y026 Use typing_extensions.TypeAlias for type aliases, e.g. "field9: TypeAlias = None" +28 | Field95: TypeAlias = None + | + +PYI052.pyi:33:11: PYI052 Need type annotation for `field19` + | +33 | Field100 = TypeVarTuple('Field100') +34 | Field101 = ParamSpec('Field101') +35 | field19 = [1, 2, 3] # Y052 Need type annotation for "field19" + | ^^^^^^^^^ PYI052 +36 | field191: list[int] = [1, 2, 3] +37 | field20 = (1, 2, 3) # Y052 Need type annotation for "field20" + | + +PYI052.pyi:35:11: PYI052 Need type annotation for `field20` + | +35 | field19 = [1, 2, 3] # Y052 Need type annotation for "field19" +36 | field191: list[int] = [1, 2, 3] +37 | field20 = (1, 2, 3) # Y052 Need type annotation for "field20" + | ^^^^^^^^^ PYI052 +38 | field201: tuple[int, ...] = (1, 2, 3) +39 | field21 = {1, 2, 3} # Y052 Need type annotation for "field21" + | + +PYI052.pyi:37:11: PYI052 Need type annotation for `field21` + | +37 | field20 = (1, 2, 3) # Y052 Need type annotation for "field20" +38 | field201: tuple[int, ...] = (1, 2, 3) +39 | field21 = {1, 2, 3} # Y052 Need type annotation for "field21" + | ^^^^^^^^^ PYI052 +40 | field211: set[int] = {1, 2, 3} +41 | field212 = {"foo": "bar"} # Y052 Need type annotation for "field212" + | + +PYI052.pyi:39:12: PYI052 Need type annotation for `field212` + | +39 | field21 = {1, 2, 3} # Y052 Need type annotation for "field21" +40 | field211: set[int] = {1, 2, 3} +41 | field212 = {"foo": "bar"} # Y052 Need type annotation for "field212" + | ^^^^^^^^^^^^^^ PYI052 +42 | field213: dict[str, str] = {"foo": "bar"} +43 | field22: Final = {"foo": 5} + | + + diff --git a/ruff.schema.json b/ruff.schema.json index 8588e62880..30ca80dbd5 100644 --- a/ruff.schema.json +++ b/ruff.schema.json @@ -2127,6 +2127,8 @@ "PYI04", "PYI042", "PYI043", + "PYI05", + "PYI052", "Q", "Q0", "Q00",