mirror of
https://github.com/astral-sh/ruff.git
synced 2025-11-03 05:03:33 +00:00
[flake8-pyi] Implement unannotated-assignment-in-stub (PY052) (#4293)
This commit is contained in:
parent
1fe6954150
commit
c711db11ce
11 changed files with 460 additions and 39 deletions
93
crates/ruff/resources/test/fixtures/flake8_pyi/PYI052.py
vendored
Normal file
93
crates/ruff/resources/test/fixtures/flake8_pyi/PYI052.py
vendored
Normal file
|
|
@ -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
|
||||
100
crates/ruff/resources/test/fixtures/flake8_pyi/PYI052.pyi
vendored
Normal file
100
crates/ruff/resources/test/fixtures/flake8_pyi/PYI052.pyi
vendored
Normal file
|
|
@ -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
|
||||
|
|
@ -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,
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -610,6 +610,7 @@ pub fn code_to_rule(linter: Linter, code: &str) -> Option<Rule> {
|
|||
(Flake8Pyi, "033") => Rule::TypeCommentInStub,
|
||||
(Flake8Pyi, "042") => Rule::SnakeCaseTypeAlias,
|
||||
(Flake8Pyi, "043") => Rule::TSuffixedTypeAlias,
|
||||
(Flake8Pyi, "052") => Rule::UnannotatedAssignmentInStub,
|
||||
|
||||
// flake8-pytest-style
|
||||
(Flake8PytestStyle, "001") => Rule::PytestFixtureIncorrectParenthesesStyle,
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
|
|
|
|||
|
|
@ -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(
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
|
|
|
|||
|
|
@ -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(),
|
||||
));
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,4 @@
|
|||
---
|
||||
source: crates/ruff/src/rules/flake8_pyi/mod.rs
|
||||
---
|
||||
|
||||
|
|
@ -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}
|
||||
|
|
||||
|
||||
|
||||
Loading…
Add table
Add a link
Reference in a new issue