mirror of
https://github.com/astral-sh/ruff.git
synced 2025-09-27 12:29:28 +00:00
Understand typing.Optional
in annotations (#14397)
This commit is contained in:
parent
cd80c9d907
commit
ff19629b11
4 changed files with 61 additions and 4 deletions
|
@ -0,0 +1,47 @@
|
||||||
|
# Optional
|
||||||
|
|
||||||
|
## Annotation
|
||||||
|
|
||||||
|
`typing.Optional` is equivalent to using the type with a None in a Union.
|
||||||
|
|
||||||
|
```py
|
||||||
|
from typing import Optional
|
||||||
|
|
||||||
|
a: Optional[int]
|
||||||
|
a1: Optional[bool]
|
||||||
|
a2: Optional[Optional[bool]]
|
||||||
|
a3: Optional[None]
|
||||||
|
|
||||||
|
def f():
|
||||||
|
# revealed: int | None
|
||||||
|
reveal_type(a)
|
||||||
|
# revealed: bool | None
|
||||||
|
reveal_type(a1)
|
||||||
|
# revealed: bool | None
|
||||||
|
reveal_type(a2)
|
||||||
|
# revealed: None
|
||||||
|
reveal_type(a3)
|
||||||
|
```
|
||||||
|
|
||||||
|
## Assignment
|
||||||
|
|
||||||
|
```py
|
||||||
|
from typing import Optional
|
||||||
|
|
||||||
|
a: Optional[int] = 1
|
||||||
|
a = None
|
||||||
|
# error: [invalid-assignment] "Object of type `Literal[""]` is not assignable to `int | None`"
|
||||||
|
a = ""
|
||||||
|
```
|
||||||
|
|
||||||
|
## Typing Extensions
|
||||||
|
|
||||||
|
```py
|
||||||
|
from typing_extensions import Optional
|
||||||
|
|
||||||
|
a: Optional[int]
|
||||||
|
|
||||||
|
def f():
|
||||||
|
# revealed: int | None
|
||||||
|
reveal_type(a)
|
||||||
|
```
|
|
@ -1807,6 +1807,8 @@ impl<'db> KnownClass {
|
||||||
pub enum KnownInstanceType<'db> {
|
pub enum KnownInstanceType<'db> {
|
||||||
/// The symbol `typing.Literal` (which can also be found as `typing_extensions.Literal`)
|
/// The symbol `typing.Literal` (which can also be found as `typing_extensions.Literal`)
|
||||||
Literal,
|
Literal,
|
||||||
|
/// The symbol `typing.Optional` (which can also be found as `typing_extensions.Literal`)
|
||||||
|
Optional,
|
||||||
/// A single instance of `typing.TypeVar`
|
/// A single instance of `typing.TypeVar`
|
||||||
TypeVar(TypeVarInstance<'db>),
|
TypeVar(TypeVarInstance<'db>),
|
||||||
// TODO: fill this enum out with more special forms, etc.
|
// TODO: fill this enum out with more special forms, etc.
|
||||||
|
@ -1816,6 +1818,7 @@ impl<'db> KnownInstanceType<'db> {
|
||||||
pub const fn as_str(self) -> &'static str {
|
pub const fn as_str(self) -> &'static str {
|
||||||
match self {
|
match self {
|
||||||
KnownInstanceType::Literal => "Literal",
|
KnownInstanceType::Literal => "Literal",
|
||||||
|
KnownInstanceType::Optional => "Optional",
|
||||||
KnownInstanceType::TypeVar(_) => "TypeVar",
|
KnownInstanceType::TypeVar(_) => "TypeVar",
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1823,8 +1826,7 @@ impl<'db> KnownInstanceType<'db> {
|
||||||
/// Evaluate the known instance in boolean context
|
/// Evaluate the known instance in boolean context
|
||||||
pub const fn bool(self) -> Truthiness {
|
pub const fn bool(self) -> Truthiness {
|
||||||
match self {
|
match self {
|
||||||
Self::Literal => Truthiness::AlwaysTrue,
|
Self::Literal | Self::Optional | Self::TypeVar(_) => Truthiness::AlwaysTrue,
|
||||||
Self::TypeVar(_) => Truthiness::AlwaysTrue,
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1832,6 +1834,7 @@ impl<'db> KnownInstanceType<'db> {
|
||||||
pub fn repr(self, db: &'db dyn Db) -> &'db str {
|
pub fn repr(self, db: &'db dyn Db) -> &'db str {
|
||||||
match self {
|
match self {
|
||||||
Self::Literal => "typing.Literal",
|
Self::Literal => "typing.Literal",
|
||||||
|
Self::Optional => "typing.Optional",
|
||||||
Self::TypeVar(typevar) => typevar.name(db),
|
Self::TypeVar(typevar) => typevar.name(db),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1840,6 +1843,7 @@ impl<'db> KnownInstanceType<'db> {
|
||||||
pub const fn class(self) -> KnownClass {
|
pub const fn class(self) -> KnownClass {
|
||||||
match self {
|
match self {
|
||||||
Self::Literal => KnownClass::SpecialForm,
|
Self::Literal => KnownClass::SpecialForm,
|
||||||
|
Self::Optional => KnownClass::SpecialForm,
|
||||||
Self::TypeVar(_) => KnownClass::TypeVar,
|
Self::TypeVar(_) => KnownClass::TypeVar,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1859,6 +1863,7 @@ impl<'db> KnownInstanceType<'db> {
|
||||||
}
|
}
|
||||||
match (module.name().as_str(), instance_name) {
|
match (module.name().as_str(), instance_name) {
|
||||||
("typing" | "typing_extensions", "Literal") => Some(Self::Literal),
|
("typing" | "typing_extensions", "Literal") => Some(Self::Literal),
|
||||||
|
("typing" | "typing_extensions", "Optional") => Some(Self::Optional),
|
||||||
_ => None,
|
_ => None,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -4524,6 +4524,10 @@ impl<'db> TypeInferenceBuilder<'db> {
|
||||||
Type::Unknown
|
Type::Unknown
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
KnownInstanceType::Optional => {
|
||||||
|
let param_type = self.infer_type_expression(parameters);
|
||||||
|
UnionType::from_elements(self.db, [param_type, Type::none(self.db)])
|
||||||
|
}
|
||||||
KnownInstanceType::TypeVar(_) => Type::Todo,
|
KnownInstanceType::TypeVar(_) => Type::Todo,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -371,8 +371,9 @@ impl<'db> ClassBase<'db> {
|
||||||
| Type::ModuleLiteral(_)
|
| Type::ModuleLiteral(_)
|
||||||
| Type::SubclassOf(_) => None,
|
| Type::SubclassOf(_) => None,
|
||||||
Type::KnownInstance(known_instance) => match known_instance {
|
Type::KnownInstance(known_instance) => match known_instance {
|
||||||
KnownInstanceType::Literal => None,
|
KnownInstanceType::TypeVar(_)
|
||||||
KnownInstanceType::TypeVar(_) => None,
|
| KnownInstanceType::Literal
|
||||||
|
| KnownInstanceType::Optional => None,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue