mirror of
https://github.com/astral-sh/ruff.git
synced 2025-08-03 18:28:56 +00:00
[red-knot] Reduce false positives on super()
and enum-class attribute accesses (#17004)
## Summary This PR adds some branches so that we infer `Todo` types for attribute access on instances of `super()` and subtypes of `type[Enum]`. It reduces false positives in the short term until we implement full support for these features. ## Test Plan New mdtests added + mypy_primer report
This commit is contained in:
parent
c963b185eb
commit
992a1af4c2
5 changed files with 63 additions and 1 deletions
|
@ -36,7 +36,7 @@ def f():
|
|||
reveal_type(a7) # revealed: None
|
||||
reveal_type(a8) # revealed: Literal[1]
|
||||
# TODO: This should be Color.RED
|
||||
reveal_type(b1) # revealed: Unknown | Literal[0]
|
||||
reveal_type(b1) # revealed: @Todo(Attribute access on enum classes)
|
||||
|
||||
# error: [invalid-type-form]
|
||||
invalid1: Literal[3 + 4]
|
||||
|
|
|
@ -1709,6 +1709,37 @@ reveal_type(C.a_type) # revealed: type
|
|||
reveal_type(C.a_none) # revealed: None
|
||||
```
|
||||
|
||||
## Enum classes
|
||||
|
||||
Enums are not supported yet; attribute access on an enum class is inferred as `Todo`.
|
||||
|
||||
```py
|
||||
import enum
|
||||
|
||||
reveal_type(enum.Enum.__members__) # revealed: @Todo(Attribute access on enum classes)
|
||||
|
||||
class Foo(enum.Enum):
|
||||
BAR = 1
|
||||
|
||||
reveal_type(Foo.BAR) # revealed: @Todo(Attribute access on enum classes)
|
||||
reveal_type(Foo.BAR.value) # revealed: @Todo(Attribute access on enum classes)
|
||||
reveal_type(Foo.__members__) # revealed: @Todo(Attribute access on enum classes)
|
||||
```
|
||||
|
||||
## `super()`
|
||||
|
||||
`super()` is not supported yet, but we do not emit false positives on `super()` calls.
|
||||
|
||||
```py
|
||||
class Foo:
|
||||
def bar(self) -> int:
|
||||
return 42
|
||||
|
||||
class Bar(Foo):
|
||||
def bar(self) -> int:
|
||||
return super().bar()
|
||||
```
|
||||
|
||||
## References
|
||||
|
||||
Some of the tests in the *Class and instance variables* section draw inspiration from
|
||||
|
|
|
@ -104,6 +104,7 @@ impl ModuleKind {
|
|||
#[strum(serialize_all = "snake_case")]
|
||||
pub enum KnownModule {
|
||||
Builtins,
|
||||
Enum,
|
||||
Types,
|
||||
#[strum(serialize = "_typeshed")]
|
||||
Typeshed,
|
||||
|
@ -121,6 +122,7 @@ impl KnownModule {
|
|||
pub const fn as_str(self) -> &'static str {
|
||||
match self {
|
||||
Self::Builtins => "builtins",
|
||||
Self::Enum => "enum",
|
||||
Self::Types => "types",
|
||||
Self::Typing => "typing",
|
||||
Self::Typeshed => "_typeshed",
|
||||
|
@ -164,6 +166,10 @@ impl KnownModule {
|
|||
pub const fn is_inspect(self) -> bool {
|
||||
matches!(self, Self::Inspect)
|
||||
}
|
||||
|
||||
pub const fn is_enum(self) -> bool {
|
||||
matches!(self, Self::Enum)
|
||||
}
|
||||
}
|
||||
|
||||
impl std::fmt::Display for KnownModule {
|
||||
|
|
|
@ -2018,6 +2018,10 @@ impl<'db> Type<'db> {
|
|||
Symbol::bound(Type::IntLiteral(segment.into())).into()
|
||||
}
|
||||
|
||||
Type::Instance(InstanceType { class }) if class.is_known(db, KnownClass::Super) => {
|
||||
SymbolAndQualifiers::todo("super() support")
|
||||
}
|
||||
|
||||
Type::IntLiteral(_) if matches!(name_str, "real" | "numerator") => {
|
||||
Symbol::bound(self).into()
|
||||
}
|
||||
|
@ -2105,6 +2109,10 @@ impl<'db> Type<'db> {
|
|||
return class_attr_plain;
|
||||
}
|
||||
|
||||
if self.is_subtype_of(db, KnownClass::Enum.to_subclass_of(db)) {
|
||||
return SymbolAndQualifiers::todo("Attribute access on enum classes");
|
||||
}
|
||||
|
||||
let class_attr_fallback = Self::try_call_dunder_get_on_attribute(
|
||||
db,
|
||||
class_attr_plain,
|
||||
|
|
|
@ -827,6 +827,9 @@ pub enum KnownClass {
|
|||
BaseException,
|
||||
BaseExceptionGroup,
|
||||
Classmethod,
|
||||
Super,
|
||||
// enum
|
||||
Enum,
|
||||
// Types
|
||||
GenericAlias,
|
||||
ModuleType,
|
||||
|
@ -924,6 +927,8 @@ impl<'db> KnownClass {
|
|||
| Self::Deque
|
||||
| Self::Float
|
||||
| Self::Sized
|
||||
| Self::Enum
|
||||
| Self::Super
|
||||
| Self::Classmethod => Truthiness::Ambiguous,
|
||||
}
|
||||
}
|
||||
|
@ -970,6 +975,8 @@ impl<'db> KnownClass {
|
|||
Self::Deque => "deque",
|
||||
Self::Sized => "Sized",
|
||||
Self::OrderedDict => "OrderedDict",
|
||||
Self::Enum => "Enum",
|
||||
Self::Super => "super",
|
||||
// For example, `typing.List` is defined as `List = _Alias()` in typeshed
|
||||
Self::StdlibAlias => "_Alias",
|
||||
// This is the name the type of `sys.version_info` has in typeshed,
|
||||
|
@ -1120,8 +1127,10 @@ impl<'db> KnownClass {
|
|||
| Self::Classmethod
|
||||
| Self::Slice
|
||||
| Self::Range
|
||||
| Self::Super
|
||||
| Self::Property => KnownModule::Builtins,
|
||||
Self::VersionInfo => KnownModule::Sys,
|
||||
Self::Enum => KnownModule::Enum,
|
||||
Self::GenericAlias
|
||||
| Self::ModuleType
|
||||
| Self::FunctionType
|
||||
|
@ -1212,6 +1221,8 @@ impl<'db> KnownClass {
|
|||
| Self::ParamSpec
|
||||
| Self::TypeVarTuple
|
||||
| Self::Sized
|
||||
| Self::Enum
|
||||
| Self::Super
|
||||
| Self::NewType => false,
|
||||
}
|
||||
}
|
||||
|
@ -1265,6 +1276,8 @@ impl<'db> KnownClass {
|
|||
| Self::ParamSpec
|
||||
| Self::TypeVarTuple
|
||||
| Self::Sized
|
||||
| Self::Enum
|
||||
| Self::Super
|
||||
| Self::NewType => false,
|
||||
}
|
||||
}
|
||||
|
@ -1318,6 +1331,8 @@ impl<'db> KnownClass {
|
|||
"_NoDefaultType" => Self::NoDefaultType,
|
||||
"SupportsIndex" => Self::SupportsIndex,
|
||||
"Sized" => Self::Sized,
|
||||
"Enum" => Self::Enum,
|
||||
"super" => Self::Super,
|
||||
"_version_info" => Self::VersionInfo,
|
||||
"ellipsis" if Program::get(db).python_version(db) <= PythonVersion::PY39 => {
|
||||
Self::EllipsisType
|
||||
|
@ -1368,6 +1383,8 @@ impl<'db> KnownClass {
|
|||
| Self::FunctionType
|
||||
| Self::MethodType
|
||||
| Self::MethodWrapperType
|
||||
| Self::Enum
|
||||
| Self::Super
|
||||
| Self::WrapperDescriptorType => module == self.canonical_module(db),
|
||||
Self::NoneType => matches!(module, KnownModule::Typeshed | KnownModule::Types),
|
||||
Self::SpecialForm
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue