mirror of
https://github.com/astral-sh/ruff.git
synced 2025-11-17 11:22:55 +00:00
[ty] support imported PEP 613 type aliases
This commit is contained in:
parent
fb5b8c3653
commit
df0c8e202d
20 changed files with 297 additions and 252 deletions
|
|
@ -143,7 +143,7 @@ static FREQTRADE: Benchmark = Benchmark::new(
|
|||
max_dep_date: "2025-06-17",
|
||||
python_version: PythonVersion::PY312,
|
||||
},
|
||||
525,
|
||||
600,
|
||||
);
|
||||
|
||||
static PANDAS: Benchmark = Benchmark::new(
|
||||
|
|
@ -163,7 +163,7 @@ static PANDAS: Benchmark = Benchmark::new(
|
|||
max_dep_date: "2025-06-17",
|
||||
python_version: PythonVersion::PY312,
|
||||
},
|
||||
3000,
|
||||
4000,
|
||||
);
|
||||
|
||||
static PYDANTIC: Benchmark = Benchmark::new(
|
||||
|
|
@ -181,7 +181,7 @@ static PYDANTIC: Benchmark = Benchmark::new(
|
|||
max_dep_date: "2025-06-17",
|
||||
python_version: PythonVersion::PY39,
|
||||
},
|
||||
5000,
|
||||
7000,
|
||||
);
|
||||
|
||||
static SYMPY: Benchmark = Benchmark::new(
|
||||
|
|
|
|||
|
|
@ -12,11 +12,8 @@ P = ParamSpec("P")
|
|||
Ts = TypeVarTuple("Ts")
|
||||
R_co = TypeVar("R_co", covariant=True)
|
||||
|
||||
Alias: TypeAlias = int
|
||||
|
||||
def f(*args: Unpack[Ts]) -> tuple[Unpack[Ts]]:
|
||||
reveal_type(args) # revealed: tuple[@Todo(`Unpack[]` special form), ...]
|
||||
reveal_type(Alias) # revealed: @Todo(Support for `typing.TypeAlias`)
|
||||
return args
|
||||
|
||||
def g() -> TypeGuard[int]: ...
|
||||
|
|
|
|||
|
|
@ -2208,9 +2208,9 @@ reveal_type(False.real) # revealed: Literal[0]
|
|||
All attribute access on literal `bytes` types is currently delegated to `builtins.bytes`:
|
||||
|
||||
```py
|
||||
# revealed: bound method Literal[b"foo"].join(iterable_of_bytes: Iterable[@Todo(Support for `typing.TypeAlias`)], /) -> bytes
|
||||
# revealed: bound method Literal[b"foo"].join(iterable_of_bytes: Iterable[Buffer], /) -> bytes
|
||||
reveal_type(b"foo".join)
|
||||
# revealed: bound method Literal[b"foo"].endswith(suffix: @Todo(Support for `typing.TypeAlias`) | tuple[@Todo(Support for `typing.TypeAlias`), ...], start: SupportsIndex | None = None, end: SupportsIndex | None = None, /) -> bool
|
||||
# revealed: bound method Literal[b"foo"].endswith(suffix: Buffer | tuple[Buffer, ...], start: SupportsIndex | None = None, end: SupportsIndex | None = None, /) -> bool
|
||||
reveal_type(b"foo".endswith)
|
||||
```
|
||||
|
||||
|
|
|
|||
|
|
@ -313,8 +313,7 @@ reveal_type(A() + "foo") # revealed: A
|
|||
reveal_type("foo" + A()) # revealed: A
|
||||
|
||||
reveal_type(A() + b"foo") # revealed: A
|
||||
# TODO should be `A` since `bytes.__add__` doesn't support `A` instances
|
||||
reveal_type(b"foo" + A()) # revealed: bytes
|
||||
reveal_type(b"foo" + A()) # revealed: A
|
||||
|
||||
reveal_type(A() + ()) # revealed: A
|
||||
reveal_type(() + A()) # revealed: A
|
||||
|
|
|
|||
|
|
@ -54,10 +54,8 @@ reveal_type(2**largest_u32) # revealed: int
|
|||
|
||||
def variable(x: int):
|
||||
reveal_type(x**2) # revealed: int
|
||||
# TODO: should be `Any` (overload 5 on `__pow__`), requires correct overload matching
|
||||
reveal_type(2**x) # revealed: int
|
||||
# TODO: should be `Any` (overload 5 on `__pow__`), requires correct overload matching
|
||||
reveal_type(x**x) # revealed: int
|
||||
reveal_type(2**x) # revealed: Any
|
||||
reveal_type(x**x) # revealed: Any
|
||||
```
|
||||
|
||||
If the second argument is \<0, a `float` is returned at runtime. If the first argument is \<0 but
|
||||
|
|
|
|||
|
|
@ -598,9 +598,9 @@ from typing_extensions import Self
|
|||
|
||||
reveal_type(object.__new__) # revealed: def __new__(cls) -> Self@__new__
|
||||
reveal_type(object().__new__) # revealed: def __new__(cls) -> Self@__new__
|
||||
# revealed: Overload[(cls, x: @Todo(Support for `typing.TypeAlias`) = Literal[0], /) -> Self@__new__, (cls, x: str | bytes | bytearray, /, base: SupportsIndex) -> Self@__new__]
|
||||
# revealed: Overload[(cls, x: str | Buffer | SupportsInt | SupportsIndex | SupportsTrunc = Literal[0], /) -> Self@__new__, (cls, x: str | bytes | bytearray, /, base: SupportsIndex) -> Self@__new__]
|
||||
reveal_type(int.__new__)
|
||||
# revealed: Overload[(cls, x: @Todo(Support for `typing.TypeAlias`) = Literal[0], /) -> Self@__new__, (cls, x: str | bytes | bytearray, /, base: SupportsIndex) -> Self@__new__]
|
||||
# revealed: Overload[(cls, x: str | Buffer | SupportsInt | SupportsIndex | SupportsTrunc = Literal[0], /) -> Self@__new__, (cls, x: str | bytes | bytearray, /, base: SupportsIndex) -> Self@__new__]
|
||||
reveal_type((42).__new__)
|
||||
|
||||
class X:
|
||||
|
|
|
|||
|
|
@ -10,13 +10,13 @@ import pickle
|
|||
|
||||
reveal_type(open("")) # revealed: TextIOWrapper[_WrappedBuffer]
|
||||
reveal_type(open("", "r")) # revealed: TextIOWrapper[_WrappedBuffer]
|
||||
reveal_type(open("", "rb")) # revealed: @Todo(`builtins.open` return type)
|
||||
reveal_type(open("", "rb")) # revealed: BufferedReader[_BufferedReaderStream]
|
||||
|
||||
with open("foo.pickle", "rb") as f:
|
||||
x = pickle.load(f) # fine
|
||||
|
||||
def _(mode: str):
|
||||
reveal_type(open("", mode)) # revealed: @Todo(`builtins.open` return type)
|
||||
reveal_type(open("", mode)) # revealed: IO[Any]
|
||||
```
|
||||
|
||||
## `os.fdopen`
|
||||
|
|
@ -29,7 +29,7 @@ import os
|
|||
|
||||
reveal_type(os.fdopen(0)) # revealed: TextIOWrapper[_WrappedBuffer]
|
||||
reveal_type(os.fdopen(0, "r")) # revealed: TextIOWrapper[_WrappedBuffer]
|
||||
reveal_type(os.fdopen(0, "rb")) # revealed: @Todo(`os.fdopen` return type)
|
||||
reveal_type(os.fdopen(0, "rb")) # revealed: BufferedReader[_BufferedReaderStream]
|
||||
|
||||
with os.fdopen(0, "rb") as f:
|
||||
x = pickle.load(f) # fine
|
||||
|
|
@ -43,9 +43,9 @@ And similarly for `Path.open()`:
|
|||
from pathlib import Path
|
||||
import pickle
|
||||
|
||||
reveal_type(Path("").open()) # revealed: @Todo(`Path.open` return type)
|
||||
reveal_type(Path("").open("r")) # revealed: @Todo(`Path.open` return type)
|
||||
reveal_type(Path("").open("rb")) # revealed: @Todo(`Path.open` return type)
|
||||
reveal_type(Path("").open()) # revealed: TextIOWrapper[_WrappedBuffer]
|
||||
reveal_type(Path("").open("r")) # revealed: TextIOWrapper[_WrappedBuffer]
|
||||
reveal_type(Path("").open("rb")) # revealed: BufferedReader[_BufferedReaderStream]
|
||||
|
||||
with Path("foo.pickle").open("rb") as f:
|
||||
x = pickle.load(f) # fine
|
||||
|
|
@ -61,7 +61,7 @@ import pickle
|
|||
|
||||
reveal_type(NamedTemporaryFile()) # revealed: _TemporaryFileWrapper[bytes]
|
||||
reveal_type(NamedTemporaryFile("r")) # revealed: _TemporaryFileWrapper[str]
|
||||
reveal_type(NamedTemporaryFile("rb")) # revealed: @Todo(`tempfile.NamedTemporaryFile` return type)
|
||||
reveal_type(NamedTemporaryFile("rb")) # revealed: _TemporaryFileWrapper[bytes]
|
||||
|
||||
with NamedTemporaryFile("rb") as f:
|
||||
x = pickle.load(f) # fine
|
||||
|
|
|
|||
|
|
@ -127,7 +127,7 @@ x = lambda y: y
|
|||
reveal_type(x.__code__) # revealed: CodeType
|
||||
reveal_type(x.__name__) # revealed: str
|
||||
reveal_type(x.__defaults__) # revealed: tuple[Any, ...] | None
|
||||
reveal_type(x.__annotations__) # revealed: dict[str, @Todo(Support for `typing.TypeAlias`)]
|
||||
reveal_type(x.__annotations__) # revealed: dict[str, Any]
|
||||
reveal_type(x.__dict__) # revealed: dict[str, Any]
|
||||
reveal_type(x.__doc__) # revealed: str | None
|
||||
reveal_type(x.__kwdefaults__) # revealed: dict[str, Any] | None
|
||||
|
|
|
|||
|
|
@ -1,8 +1,82 @@
|
|||
# PEP 613 type aliases
|
||||
|
||||
## No panics
|
||||
PEP 613 type aliases are simple assignment statements, annotated with `typing.TypeAlias` to mark
|
||||
them as a type alias. At runtime, they behave the same as implicit type aliases. Our support for
|
||||
them is currently the same as for implicit type aliases, but we don't reproduce the full
|
||||
implicit-type-alias test suite here, just some particularly interesting cases.
|
||||
|
||||
We do not fully support PEP 613 type aliases yet. For now, just make sure that we don't panic:
|
||||
## Basic
|
||||
|
||||
### as `TypeAlias`
|
||||
|
||||
```py
|
||||
from typing import TypeAlias
|
||||
|
||||
IntOrStr: TypeAlias = int | str
|
||||
|
||||
def _(x: IntOrStr):
|
||||
reveal_type(x) # revealed: int | str
|
||||
```
|
||||
|
||||
### as `typing.TypeAlias`
|
||||
|
||||
```py
|
||||
import typing
|
||||
|
||||
IntOrStr: typing.TypeAlias = int | str
|
||||
|
||||
def _(x: IntOrStr):
|
||||
reveal_type(x) # revealed: int | str
|
||||
```
|
||||
|
||||
## Can be used as value
|
||||
|
||||
Because PEP 613 type aliases are just annotated assignments, they can be used as values, like a
|
||||
legacy type expression (and unlike a PEP 695 type alias). We might prefer this wasn't allowed, but
|
||||
people do use it.
|
||||
|
||||
```py
|
||||
from typing import TypeAlias
|
||||
|
||||
MyExc: TypeAlias = Exception
|
||||
|
||||
try:
|
||||
raise MyExc("error")
|
||||
except MyExc as e:
|
||||
reveal_type(e) # revealed: Exception
|
||||
```
|
||||
|
||||
## Imported
|
||||
|
||||
`alias.py`:
|
||||
|
||||
```py
|
||||
from typing import TypeAlias
|
||||
|
||||
MyAlias: TypeAlias = int | str
|
||||
```
|
||||
|
||||
`main.py`:
|
||||
|
||||
```py
|
||||
from alias import MyAlias
|
||||
|
||||
def _(x: MyAlias):
|
||||
reveal_type(x) # revealed: int | str
|
||||
```
|
||||
|
||||
## String literal in RHS
|
||||
|
||||
```py
|
||||
from typing import TypeAlias
|
||||
|
||||
IntOrStr: TypeAlias = "int | str"
|
||||
|
||||
def _(x: IntOrStr):
|
||||
reveal_type(x) # revealed: int | str
|
||||
```
|
||||
|
||||
## Cyclic
|
||||
|
||||
```py
|
||||
from typing import TypeAlias
|
||||
|
|
@ -18,6 +92,26 @@ def _(rec: RecursiveHomogeneousTuple):
|
|||
reveal_type(rec) # revealed: tuple[Divergent, ...]
|
||||
```
|
||||
|
||||
## Conditionally imported on Python < 3.10
|
||||
|
||||
```toml
|
||||
[environment]
|
||||
python-version = "3.9"
|
||||
```
|
||||
|
||||
```py
|
||||
try:
|
||||
# error: [unresolved-import]
|
||||
from typing import TypeAlias
|
||||
except ImportError:
|
||||
from typing_extensions import TypeAlias
|
||||
|
||||
MyAlias: TypeAlias = int
|
||||
|
||||
def _(x: MyAlias):
|
||||
reveal_type(x) # revealed: int
|
||||
```
|
||||
|
||||
## PEP-613 aliases in stubs are deferred
|
||||
|
||||
Although the right-hand side of a PEP-613 alias is a value expression, inference of this value is
|
||||
|
|
@ -46,7 +140,31 @@ f(stub.B())
|
|||
|
||||
class Unrelated: ...
|
||||
|
||||
# TODO: we should emit `[invalid-argument-type]` here
|
||||
# (the alias is a `@Todo` because it's imported from another file)
|
||||
# error: [invalid-argument-type]
|
||||
f(Unrelated())
|
||||
```
|
||||
|
||||
## Invalid position
|
||||
|
||||
`typing.TypeAlias` must be used as the sole annotation in an annotated assignment. Use in any other
|
||||
context is an error.
|
||||
|
||||
```py
|
||||
from typing import TypeAlias
|
||||
|
||||
# error: [invalid-type-form]
|
||||
def _(x: TypeAlias):
|
||||
reveal_type(x) # revealed: Unknown
|
||||
|
||||
# error: [invalid-type-form]
|
||||
y: list[TypeAlias] = []
|
||||
```
|
||||
|
||||
## RHS is required
|
||||
|
||||
```py
|
||||
from typing import TypeAlias
|
||||
|
||||
# error: [invalid-type-form]
|
||||
Empty: TypeAlias
|
||||
```
|
||||
|
|
|
|||
|
|
@ -28,7 +28,7 @@ def f() -> None:
|
|||
```py
|
||||
type IntOrStr = int | str
|
||||
|
||||
reveal_type(IntOrStr.__value__) # revealed: @Todo(Support for `typing.TypeAlias`)
|
||||
reveal_type(IntOrStr.__value__) # revealed: Any
|
||||
```
|
||||
|
||||
## Invalid assignment
|
||||
|
|
|
|||
|
|
@ -122,7 +122,7 @@ properties on instance types:
|
|||
|
||||
```py
|
||||
reveal_type(sys.version_info.micro) # revealed: int
|
||||
reveal_type(sys.version_info.releaselevel) # revealed: @Todo(Support for `typing.TypeAlias`)
|
||||
reveal_type(sys.version_info.releaselevel) # revealed: Literal["alpha", "beta", "candidate", "final"]
|
||||
reveal_type(sys.version_info.serial) # revealed: int
|
||||
```
|
||||
|
||||
|
|
|
|||
|
|
@ -4369,11 +4369,6 @@ impl<'db> Type<'db> {
|
|||
Type::KnownBoundMethod(KnownBoundMethodType::StrStartswith(literal)),
|
||||
)
|
||||
.into(),
|
||||
Type::NominalInstance(instance)
|
||||
if instance.has_known_class(db, KnownClass::Path) && name == "open" =>
|
||||
{
|
||||
Place::bound(Type::KnownBoundMethod(KnownBoundMethodType::PathOpen)).into()
|
||||
}
|
||||
|
||||
Type::ClassLiteral(class)
|
||||
if name == "range" && class.is_known(db, KnownClass::ConstraintSet) =>
|
||||
|
|
@ -6737,6 +6732,7 @@ impl<'db> Type<'db> {
|
|||
|
||||
Ok(ty.inner(db).to_meta_type(db))
|
||||
}
|
||||
KnownInstanceType::LiteralStringAlias(ty) => Ok(ty.inner(db)),
|
||||
},
|
||||
|
||||
Type::SpecialForm(special_form) => match special_form {
|
||||
|
|
@ -6791,7 +6787,15 @@ impl<'db> Type<'db> {
|
|||
|
||||
Ok(typing_self(db, scope_id, typevar_binding_context, class).unwrap_or(*self))
|
||||
}
|
||||
SpecialFormType::TypeAlias => Ok(Type::Dynamic(DynamicType::TodoTypeAlias)),
|
||||
// We ensure that `typing.TypeAlias` used in the expected position (annotating an
|
||||
// annotated assignment statement) doesn't reach here. Using it in any other type
|
||||
// expression is an error.
|
||||
SpecialFormType::TypeAlias => Err(InvalidTypeExpressionError {
|
||||
invalid_expressions: smallvec::smallvec_inline![
|
||||
InvalidTypeExpression::TypeAlias
|
||||
],
|
||||
fallback_type: Type::unknown(),
|
||||
}),
|
||||
SpecialFormType::TypedDict => Err(InvalidTypeExpressionError {
|
||||
invalid_expressions: smallvec::smallvec_inline![
|
||||
InvalidTypeExpression::TypedDict
|
||||
|
|
@ -7269,7 +7273,6 @@ impl<'db> Type<'db> {
|
|||
| Type::WrapperDescriptor(_)
|
||||
| Type::KnownBoundMethod(
|
||||
KnownBoundMethodType::StrStartswith(_)
|
||||
| KnownBoundMethodType::PathOpen
|
||||
| KnownBoundMethodType::ConstraintSetRange
|
||||
| KnownBoundMethodType::ConstraintSetAlways
|
||||
| KnownBoundMethodType::ConstraintSetNever
|
||||
|
|
@ -7429,7 +7432,6 @@ impl<'db> Type<'db> {
|
|||
| Type::WrapperDescriptor(_)
|
||||
| Type::KnownBoundMethod(
|
||||
KnownBoundMethodType::StrStartswith(_)
|
||||
| KnownBoundMethodType::PathOpen
|
||||
| KnownBoundMethodType::ConstraintSetRange
|
||||
| KnownBoundMethodType::ConstraintSetAlways
|
||||
| KnownBoundMethodType::ConstraintSetNever
|
||||
|
|
@ -7980,6 +7982,9 @@ pub enum KnownInstanceType<'db> {
|
|||
/// An instance of `typing.GenericAlias` representing a `type[...]` expression.
|
||||
TypeGenericAlias(InternedType<'db>),
|
||||
|
||||
/// A literal string which is the right-hand side of a PEP 613 `TypeAlias`.
|
||||
LiteralStringAlias(InternedType<'db>),
|
||||
|
||||
/// An identity callable created with `typing.NewType(name, base)`, which behaves like a
|
||||
/// subtype of `base` in type expressions. See the `struct NewType` payload for an example.
|
||||
NewType(NewType<'db>),
|
||||
|
|
@ -8016,7 +8021,8 @@ fn walk_known_instance_type<'db, V: visitor::TypeVisitor<'db> + ?Sized>(
|
|||
}
|
||||
KnownInstanceType::Literal(ty)
|
||||
| KnownInstanceType::Annotated(ty)
|
||||
| KnownInstanceType::TypeGenericAlias(ty) => {
|
||||
| KnownInstanceType::TypeGenericAlias(ty)
|
||||
| KnownInstanceType::LiteralStringAlias(ty) => {
|
||||
visitor.visit_type(db, ty.inner(db));
|
||||
}
|
||||
KnownInstanceType::NewType(newtype) => {
|
||||
|
|
@ -8064,6 +8070,9 @@ impl<'db> KnownInstanceType<'db> {
|
|||
Self::Literal(ty) => Self::Literal(ty.normalized_impl(db, visitor)),
|
||||
Self::Annotated(ty) => Self::Annotated(ty.normalized_impl(db, visitor)),
|
||||
Self::TypeGenericAlias(ty) => Self::TypeGenericAlias(ty.normalized_impl(db, visitor)),
|
||||
Self::LiteralStringAlias(ty) => {
|
||||
Self::LiteralStringAlias(ty.normalized_impl(db, visitor))
|
||||
}
|
||||
Self::NewType(newtype) => Self::NewType(
|
||||
newtype
|
||||
.map_base_class_type(db, |class_type| class_type.normalized_impl(db, visitor)),
|
||||
|
|
@ -8089,6 +8098,7 @@ impl<'db> KnownInstanceType<'db> {
|
|||
Self::Literal(_) | Self::Annotated(_) | Self::TypeGenericAlias(_) => {
|
||||
KnownClass::GenericAlias
|
||||
}
|
||||
Self::LiteralStringAlias(_) => KnownClass::Str,
|
||||
Self::NewType(_) => KnownClass::NewType,
|
||||
}
|
||||
}
|
||||
|
|
@ -8175,6 +8185,7 @@ impl<'db> KnownInstanceType<'db> {
|
|||
f.write_str("<typing.Annotated special form>")
|
||||
}
|
||||
KnownInstanceType::TypeGenericAlias(_) => f.write_str("GenericAlias"),
|
||||
KnownInstanceType::LiteralStringAlias(_) => f.write_str("str"),
|
||||
KnownInstanceType::NewType(declaration) => {
|
||||
write!(f, "<NewType pseudo-class '{}'>", declaration.name(self.db))
|
||||
}
|
||||
|
|
@ -8216,9 +8227,6 @@ pub enum DynamicType<'db> {
|
|||
///
|
||||
/// This variant should be created with the `todo_type!` macro.
|
||||
Todo(TodoType),
|
||||
/// A special Todo-variant for type aliases declared using `typing.TypeAlias`.
|
||||
/// A temporary variant to detect and special-case the handling of these aliases in autocomplete suggestions.
|
||||
TodoTypeAlias,
|
||||
/// A special Todo-variant for `Unpack[Ts]`, so that we can treat it specially in `Generic[Unpack[Ts]]`
|
||||
TodoUnpack,
|
||||
/// A type that is determined to be divergent during type inference for a recursive function.
|
||||
|
|
@ -8250,13 +8258,6 @@ impl std::fmt::Display for DynamicType<'_> {
|
|||
f.write_str("@Todo")
|
||||
}
|
||||
}
|
||||
DynamicType::TodoTypeAlias => {
|
||||
if cfg!(debug_assertions) {
|
||||
f.write_str("@Todo(Support for `typing.TypeAlias`)")
|
||||
} else {
|
||||
f.write_str("@Todo")
|
||||
}
|
||||
}
|
||||
DynamicType::Divergent(_) => f.write_str("Divergent"),
|
||||
}
|
||||
}
|
||||
|
|
@ -8415,6 +8416,9 @@ enum InvalidTypeExpression<'db> {
|
|||
ConstraintSet,
|
||||
/// Same for `typing.TypedDict`
|
||||
TypedDict,
|
||||
/// Same for `typing.TypeAlias`, anywhere except for as the sole annotation on an annotated
|
||||
/// assignment
|
||||
TypeAlias,
|
||||
/// Type qualifiers are always invalid in *type expressions*,
|
||||
/// but these ones are okay with 0 arguments in *annotation expressions*
|
||||
TypeQualifier(SpecialFormType),
|
||||
|
|
@ -8470,6 +8474,11 @@ impl<'db> InvalidTypeExpression<'db> {
|
|||
"The special form `typing.TypedDict` is not allowed in type expressions. \
|
||||
Did you mean to use a concrete TypedDict or `collections.abc.Mapping[str, object]` instead?")
|
||||
}
|
||||
InvalidTypeExpression::TypeAlias => {
|
||||
f.write_str(
|
||||
"`typing.TypeAlias` is only allowed as the sole annotation on an annotated assignment",
|
||||
)
|
||||
}
|
||||
InvalidTypeExpression::TypeQualifier(qualifier) => write!(
|
||||
f,
|
||||
"Type qualifier `{qualifier}` is not allowed in type expressions \
|
||||
|
|
@ -10910,8 +10919,6 @@ pub enum KnownBoundMethodType<'db> {
|
|||
/// this allows us to understand statically known branches for common tests such as
|
||||
/// `if sys.platform.startswith("freebsd")`.
|
||||
StrStartswith(StringLiteralType<'db>),
|
||||
/// Method wrapper for `Path.open`,
|
||||
PathOpen,
|
||||
|
||||
// ConstraintSet methods
|
||||
ConstraintSetRange,
|
||||
|
|
@ -10943,8 +10950,7 @@ pub(super) fn walk_method_wrapper_type<'db, V: visitor::TypeVisitor<'db> + ?Size
|
|||
KnownBoundMethodType::StrStartswith(string_literal) => {
|
||||
visitor.visit_type(db, Type::StringLiteral(string_literal));
|
||||
}
|
||||
KnownBoundMethodType::PathOpen
|
||||
| KnownBoundMethodType::ConstraintSetRange
|
||||
KnownBoundMethodType::ConstraintSetRange
|
||||
| KnownBoundMethodType::ConstraintSetAlways
|
||||
| KnownBoundMethodType::ConstraintSetNever
|
||||
| KnownBoundMethodType::ConstraintSetImpliesSubtypeOf(_)
|
||||
|
|
@ -11001,8 +11007,7 @@ impl<'db> KnownBoundMethodType<'db> {
|
|||
ConstraintSet::from(self == other)
|
||||
}
|
||||
|
||||
(KnownBoundMethodType::PathOpen, KnownBoundMethodType::PathOpen)
|
||||
| (
|
||||
(
|
||||
KnownBoundMethodType::ConstraintSetRange,
|
||||
KnownBoundMethodType::ConstraintSetRange,
|
||||
)
|
||||
|
|
@ -11033,7 +11038,6 @@ impl<'db> KnownBoundMethodType<'db> {
|
|||
| KnownBoundMethodType::PropertyDunderGet(_)
|
||||
| KnownBoundMethodType::PropertyDunderSet(_)
|
||||
| KnownBoundMethodType::StrStartswith(_)
|
||||
| KnownBoundMethodType::PathOpen
|
||||
| KnownBoundMethodType::ConstraintSetRange
|
||||
| KnownBoundMethodType::ConstraintSetAlways
|
||||
| KnownBoundMethodType::ConstraintSetNever
|
||||
|
|
@ -11045,7 +11049,6 @@ impl<'db> KnownBoundMethodType<'db> {
|
|||
| KnownBoundMethodType::PropertyDunderGet(_)
|
||||
| KnownBoundMethodType::PropertyDunderSet(_)
|
||||
| KnownBoundMethodType::StrStartswith(_)
|
||||
| KnownBoundMethodType::PathOpen
|
||||
| KnownBoundMethodType::ConstraintSetRange
|
||||
| KnownBoundMethodType::ConstraintSetAlways
|
||||
| KnownBoundMethodType::ConstraintSetNever
|
||||
|
|
@ -11087,8 +11090,7 @@ impl<'db> KnownBoundMethodType<'db> {
|
|||
ConstraintSet::from(self == other)
|
||||
}
|
||||
|
||||
(KnownBoundMethodType::PathOpen, KnownBoundMethodType::PathOpen)
|
||||
| (
|
||||
(
|
||||
KnownBoundMethodType::ConstraintSetRange,
|
||||
KnownBoundMethodType::ConstraintSetRange,
|
||||
)
|
||||
|
|
@ -11122,7 +11124,6 @@ impl<'db> KnownBoundMethodType<'db> {
|
|||
| KnownBoundMethodType::PropertyDunderGet(_)
|
||||
| KnownBoundMethodType::PropertyDunderSet(_)
|
||||
| KnownBoundMethodType::StrStartswith(_)
|
||||
| KnownBoundMethodType::PathOpen
|
||||
| KnownBoundMethodType::ConstraintSetRange
|
||||
| KnownBoundMethodType::ConstraintSetAlways
|
||||
| KnownBoundMethodType::ConstraintSetNever
|
||||
|
|
@ -11134,7 +11135,6 @@ impl<'db> KnownBoundMethodType<'db> {
|
|||
| KnownBoundMethodType::PropertyDunderGet(_)
|
||||
| KnownBoundMethodType::PropertyDunderSet(_)
|
||||
| KnownBoundMethodType::StrStartswith(_)
|
||||
| KnownBoundMethodType::PathOpen
|
||||
| KnownBoundMethodType::ConstraintSetRange
|
||||
| KnownBoundMethodType::ConstraintSetAlways
|
||||
| KnownBoundMethodType::ConstraintSetNever
|
||||
|
|
@ -11160,7 +11160,6 @@ impl<'db> KnownBoundMethodType<'db> {
|
|||
KnownBoundMethodType::PropertyDunderSet(property.normalized_impl(db, visitor))
|
||||
}
|
||||
KnownBoundMethodType::StrStartswith(_)
|
||||
| KnownBoundMethodType::PathOpen
|
||||
| KnownBoundMethodType::ConstraintSetRange
|
||||
| KnownBoundMethodType::ConstraintSetAlways
|
||||
| KnownBoundMethodType::ConstraintSetNever
|
||||
|
|
@ -11178,7 +11177,6 @@ impl<'db> KnownBoundMethodType<'db> {
|
|||
| KnownBoundMethodType::PropertyDunderGet(_)
|
||||
| KnownBoundMethodType::PropertyDunderSet(_) => KnownClass::MethodWrapperType,
|
||||
KnownBoundMethodType::StrStartswith(_) => KnownClass::BuiltinFunctionType,
|
||||
KnownBoundMethodType::PathOpen => KnownClass::MethodType,
|
||||
KnownBoundMethodType::ConstraintSetRange
|
||||
| KnownBoundMethodType::ConstraintSetAlways
|
||||
| KnownBoundMethodType::ConstraintSetNever
|
||||
|
|
@ -11283,9 +11281,6 @@ impl<'db> KnownBoundMethodType<'db> {
|
|||
Some(KnownClass::Bool.to_instance(db)),
|
||||
)))
|
||||
}
|
||||
KnownBoundMethodType::PathOpen => {
|
||||
Either::Right(std::iter::once(Signature::todo("`Path.open` return type")))
|
||||
}
|
||||
|
||||
KnownBoundMethodType::ConstraintSetRange => {
|
||||
Either::Right(std::iter::once(Signature::new(
|
||||
|
|
|
|||
|
|
@ -48,9 +48,7 @@ impl<'db> ClassBase<'db> {
|
|||
ClassBase::Class(class) => class.name(db),
|
||||
ClassBase::Dynamic(DynamicType::Any) => "Any",
|
||||
ClassBase::Dynamic(DynamicType::Unknown) => "Unknown",
|
||||
ClassBase::Dynamic(
|
||||
DynamicType::Todo(_) | DynamicType::TodoTypeAlias | DynamicType::TodoUnpack,
|
||||
) => "@Todo",
|
||||
ClassBase::Dynamic(DynamicType::Todo(_) | DynamicType::TodoUnpack) => "@Todo",
|
||||
ClassBase::Dynamic(DynamicType::Divergent(_)) => "Divergent",
|
||||
ClassBase::Protocol => "Protocol",
|
||||
ClassBase::Generic => "Generic",
|
||||
|
|
@ -176,6 +174,7 @@ impl<'db> ClassBase<'db> {
|
|||
| KnownInstanceType::ConstraintSet(_)
|
||||
| KnownInstanceType::UnionType(_)
|
||||
| KnownInstanceType::Literal(_)
|
||||
| KnownInstanceType::LiteralStringAlias(_)
|
||||
// A class inheriting from a newtype would make intuitive sense, but newtype
|
||||
// wrappers are just identity callables at runtime, so this sort of inheritance
|
||||
// doesn't work and isn't allowed.
|
||||
|
|
|
|||
|
|
@ -520,9 +520,6 @@ impl Display for DisplayRepresentation<'_> {
|
|||
Type::KnownBoundMethod(KnownBoundMethodType::StrStartswith(_)) => {
|
||||
f.write_str("<method-wrapper `startswith` of `str` object>")
|
||||
}
|
||||
Type::KnownBoundMethod(KnownBoundMethodType::PathOpen) => {
|
||||
f.write_str("bound method `Path.open`")
|
||||
}
|
||||
Type::KnownBoundMethod(KnownBoundMethodType::ConstraintSetRange) => {
|
||||
f.write_str("bound method `ConstraintSet.range`")
|
||||
}
|
||||
|
|
|
|||
|
|
@ -83,7 +83,7 @@ use crate::types::{
|
|||
ClassLiteral, ClassType, DeprecatedInstance, DynamicType, FindLegacyTypeVarsVisitor,
|
||||
HasRelationToVisitor, IsDisjointVisitor, IsEquivalentVisitor, KnownClass, KnownInstanceType,
|
||||
NormalizedVisitor, SpecialFormType, Truthiness, Type, TypeContext, TypeMapping, TypeRelation,
|
||||
UnionBuilder, binding_type, todo_type, walk_signature,
|
||||
UnionBuilder, binding_type, walk_signature,
|
||||
};
|
||||
use crate::{Db, FxOrderSet, ModuleName, resolve_module};
|
||||
|
||||
|
|
@ -1152,70 +1152,6 @@ fn is_instance_truthiness<'db>(
|
|||
}
|
||||
}
|
||||
|
||||
/// Return true, if the type passed as `mode` would require us to pick a non-trivial overload of
|
||||
/// `builtins.open` / `os.fdopen` / `Path.open`.
|
||||
fn is_mode_with_nontrivial_return_type<'db>(db: &'db dyn Db, mode: Type<'db>) -> bool {
|
||||
// Return true for any mode that doesn't match typeshed's
|
||||
// `OpenTextMode` type alias (<https://github.com/python/typeshed/blob/6937a9b193bfc2f0696452d58aad96d7627aa29a/stdlib/_typeshed/__init__.pyi#L220>).
|
||||
mode.as_string_literal().is_none_or(|mode| {
|
||||
!matches!(
|
||||
mode.value(db),
|
||||
"r+" | "+r"
|
||||
| "rt+"
|
||||
| "r+t"
|
||||
| "+rt"
|
||||
| "tr+"
|
||||
| "t+r"
|
||||
| "+tr"
|
||||
| "w+"
|
||||
| "+w"
|
||||
| "wt+"
|
||||
| "w+t"
|
||||
| "+wt"
|
||||
| "tw+"
|
||||
| "t+w"
|
||||
| "+tw"
|
||||
| "a+"
|
||||
| "+a"
|
||||
| "at+"
|
||||
| "a+t"
|
||||
| "+at"
|
||||
| "ta+"
|
||||
| "t+a"
|
||||
| "+ta"
|
||||
| "x+"
|
||||
| "+x"
|
||||
| "xt+"
|
||||
| "x+t"
|
||||
| "+xt"
|
||||
| "tx+"
|
||||
| "t+x"
|
||||
| "+tx"
|
||||
| "w"
|
||||
| "wt"
|
||||
| "tw"
|
||||
| "a"
|
||||
| "at"
|
||||
| "ta"
|
||||
| "x"
|
||||
| "xt"
|
||||
| "tx"
|
||||
| "r"
|
||||
| "rt"
|
||||
| "tr"
|
||||
| "U"
|
||||
| "rU"
|
||||
| "Ur"
|
||||
| "rtU"
|
||||
| "rUt"
|
||||
| "Urt"
|
||||
| "trU"
|
||||
| "tUr"
|
||||
| "Utr"
|
||||
)
|
||||
})
|
||||
}
|
||||
|
||||
fn signature_cycle_initial<'db>(
|
||||
_db: &'db dyn Db,
|
||||
_id: salsa::Id,
|
||||
|
|
@ -1268,16 +1204,6 @@ pub enum KnownFunction {
|
|||
DunderImport,
|
||||
/// `importlib.import_module`, which returns the submodule.
|
||||
ImportModule,
|
||||
/// `builtins.open`
|
||||
Open,
|
||||
|
||||
/// `os.fdopen`
|
||||
Fdopen,
|
||||
|
||||
/// `tempfile.NamedTemporaryFile`
|
||||
#[strum(serialize = "NamedTemporaryFile")]
|
||||
NamedTemporaryFile,
|
||||
|
||||
/// `typing(_extensions).final`
|
||||
Final,
|
||||
/// `typing(_extensions).disjoint_base`
|
||||
|
|
@ -1376,7 +1302,6 @@ impl KnownFunction {
|
|||
| Self::HasAttr
|
||||
| Self::Len
|
||||
| Self::Repr
|
||||
| Self::Open
|
||||
| Self::DunderImport => module.is_builtins(),
|
||||
Self::AssertType
|
||||
| Self::AssertNever
|
||||
|
|
@ -1396,12 +1321,6 @@ impl KnownFunction {
|
|||
Self::AbstractMethod => {
|
||||
matches!(module, KnownModule::Abc)
|
||||
}
|
||||
Self::Fdopen => {
|
||||
matches!(module, KnownModule::Os)
|
||||
}
|
||||
Self::NamedTemporaryFile => {
|
||||
matches!(module, KnownModule::Tempfile)
|
||||
}
|
||||
Self::Dataclass | Self::Field => {
|
||||
matches!(module, KnownModule::Dataclasses)
|
||||
}
|
||||
|
|
@ -1871,38 +1790,6 @@ impl KnownFunction {
|
|||
|
||||
overload.set_return_type(Type::module_literal(db, file, module));
|
||||
}
|
||||
|
||||
KnownFunction::Open => {
|
||||
// TODO: Temporary special-casing for `builtins.open` to avoid an excessive number of
|
||||
// false positives in lieu of proper support for PEP-613 type aliases.
|
||||
if let [_, Some(mode), ..] = parameter_types
|
||||
&& is_mode_with_nontrivial_return_type(db, *mode)
|
||||
{
|
||||
overload.set_return_type(todo_type!("`builtins.open` return type"));
|
||||
}
|
||||
}
|
||||
|
||||
KnownFunction::Fdopen => {
|
||||
// TODO: Temporary special-casing for `os.fdopen` to avoid an excessive number of
|
||||
// false positives in lieu of proper support for PEP-613 type aliases.
|
||||
if let [_, Some(mode), ..] = parameter_types
|
||||
&& is_mode_with_nontrivial_return_type(db, *mode)
|
||||
{
|
||||
overload.set_return_type(todo_type!("`os.fdopen` return type"));
|
||||
}
|
||||
}
|
||||
|
||||
KnownFunction::NamedTemporaryFile => {
|
||||
// TODO: Temporary special-casing for `tempfile.NamedTemporaryFile` to avoid an excessive number of
|
||||
// false positives in lieu of proper support for PEP-613 type aliases.
|
||||
if let [Some(mode), ..] = parameter_types
|
||||
&& is_mode_with_nontrivial_return_type(db, *mode)
|
||||
{
|
||||
overload
|
||||
.set_return_type(todo_type!("`tempfile.NamedTemporaryFile` return type"));
|
||||
}
|
||||
}
|
||||
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
|
|
@ -1929,15 +1816,10 @@ pub(crate) mod tests {
|
|||
| KnownFunction::IsInstance
|
||||
| KnownFunction::HasAttr
|
||||
| KnownFunction::IsSubclass
|
||||
| KnownFunction::Open
|
||||
| KnownFunction::DunderImport => KnownModule::Builtins,
|
||||
|
||||
KnownFunction::AbstractMethod => KnownModule::Abc,
|
||||
|
||||
KnownFunction::Fdopen => KnownModule::Os,
|
||||
|
||||
KnownFunction::NamedTemporaryFile => KnownModule::Tempfile,
|
||||
|
||||
KnownFunction::Dataclass | KnownFunction::Field => KnownModule::Dataclasses,
|
||||
|
||||
KnownFunction::GetattrStatic => KnownModule::Inspect,
|
||||
|
|
|
|||
|
|
@ -15,7 +15,7 @@ use crate::types::generics::Specialization;
|
|||
use crate::types::signatures::Signature;
|
||||
use crate::types::{CallDunderError, UnionType};
|
||||
use crate::types::{
|
||||
ClassBase, ClassLiteral, DynamicType, KnownClass, KnownInstanceType, Type, TypeContext,
|
||||
ClassBase, ClassLiteral, KnownClass, KnownInstanceType, Type, TypeContext,
|
||||
TypeVarBoundOrConstraints, class::CodeGeneratorKind,
|
||||
};
|
||||
use crate::{Db, HasType, NameKind, SemanticModel};
|
||||
|
|
@ -299,9 +299,10 @@ impl<'db> AllMembers<'db> {
|
|||
Type::KnownInstance(
|
||||
KnownInstanceType::TypeVar(_)
|
||||
| KnownInstanceType::TypeAliasType(_)
|
||||
| KnownInstanceType::UnionType(_),
|
||||
| KnownInstanceType::UnionType(_)
|
||||
| KnownInstanceType::Literal(_)
|
||||
| KnownInstanceType::Annotated(_),
|
||||
) => continue,
|
||||
Type::Dynamic(DynamicType::TodoTypeAlias) => continue,
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -5390,7 +5390,7 @@ impl<'db, 'ast> TypeInferenceBuilder<'db, 'ast> {
|
|||
let target = assignment.target(self.module());
|
||||
let value = assignment.value(self.module());
|
||||
|
||||
let mut declared = self.infer_annotation_expression(
|
||||
let mut declared = self.infer_annotation_expression_allow_pep_613(
|
||||
annotation,
|
||||
DeferredExpressionState::from(self.defer_annotations()),
|
||||
);
|
||||
|
|
@ -5442,6 +5442,13 @@ impl<'db, 'ast> TypeInferenceBuilder<'db, 'ast> {
|
|||
declared.inner = Type::BooleanLiteral(true);
|
||||
}
|
||||
|
||||
// Check if this is a PEP 613 `TypeAlias`. (This must come below the SpecialForm handling
|
||||
// immediately below, since that can overwrite the type to be `TypeAlias`.)
|
||||
let is_pep_613_type_alias = matches!(
|
||||
declared.inner_type(),
|
||||
Type::SpecialForm(SpecialFormType::TypeAlias)
|
||||
);
|
||||
|
||||
// Handle various singletons.
|
||||
if let Some(name_expr) = target.as_name_expr() {
|
||||
if let Some(special_form) =
|
||||
|
|
@ -5487,20 +5494,24 @@ impl<'db, 'ast> TypeInferenceBuilder<'db, 'ast> {
|
|||
}
|
||||
|
||||
// We defer the r.h.s. of PEP-613 `TypeAlias` assignments in stub files.
|
||||
let declared_type = declared.inner_type();
|
||||
let previous_deferred_state = self.deferred_state;
|
||||
|
||||
if matches!(
|
||||
declared_type,
|
||||
Type::SpecialForm(SpecialFormType::TypeAlias)
|
||||
| Type::Dynamic(DynamicType::TodoTypeAlias)
|
||||
) && self.in_stub()
|
||||
{
|
||||
if is_pep_613_type_alias && self.in_stub() {
|
||||
self.deferred_state = DeferredExpressionState::Deferred;
|
||||
}
|
||||
|
||||
let inferred_ty = self
|
||||
.infer_maybe_standalone_expression(value, TypeContext::new(Some(declared_type)));
|
||||
let inferred_ty = if is_pep_613_type_alias && value.is_string_literal_expr() {
|
||||
let aliased_type = self.infer_type_expression(value);
|
||||
Type::KnownInstance(KnownInstanceType::LiteralStringAlias(InternedType::new(
|
||||
self.db(),
|
||||
aliased_type,
|
||||
)))
|
||||
} else {
|
||||
self.infer_maybe_standalone_expression(
|
||||
value,
|
||||
TypeContext::new(Some(declared.inner_type())),
|
||||
)
|
||||
};
|
||||
|
||||
self.deferred_state = previous_deferred_state;
|
||||
|
||||
|
|
@ -5517,6 +5528,13 @@ impl<'db, 'ast> TypeInferenceBuilder<'db, 'ast> {
|
|||
inferred_ty
|
||||
};
|
||||
|
||||
if is_pep_613_type_alias {
|
||||
self.add_declaration_with_binding(
|
||||
target.into(),
|
||||
definition,
|
||||
&DeclaredAndInferredType::AreTheSame(TypeAndQualifiers::declared(inferred_ty)),
|
||||
);
|
||||
} else {
|
||||
self.add_declaration_with_binding(
|
||||
target.into(),
|
||||
definition,
|
||||
|
|
@ -5525,9 +5543,18 @@ impl<'db, 'ast> TypeInferenceBuilder<'db, 'ast> {
|
|||
inferred_ty,
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
self.store_expression_type(target, inferred_ty);
|
||||
} else {
|
||||
if is_pep_613_type_alias {
|
||||
if let Some(builder) = self.context.report_lint(&INVALID_TYPE_FORM, annotation) {
|
||||
builder.into_diagnostic(
|
||||
"`TypeAlias` must be assigned a value in annotated assignments",
|
||||
);
|
||||
}
|
||||
declared.inner = Type::unknown();
|
||||
}
|
||||
if self.in_stub() {
|
||||
self.add_declaration_with_binding(
|
||||
target.into(),
|
||||
|
|
@ -9286,20 +9313,10 @@ impl<'db, 'ast> TypeInferenceBuilder<'db, 'ast> {
|
|||
(unknown @ Type::Dynamic(DynamicType::Unknown), _, _)
|
||||
| (_, unknown @ Type::Dynamic(DynamicType::Unknown), _) => Some(unknown),
|
||||
|
||||
(
|
||||
todo @ Type::Dynamic(
|
||||
DynamicType::Todo(_) | DynamicType::TodoUnpack | DynamicType::TodoTypeAlias,
|
||||
),
|
||||
_,
|
||||
_,
|
||||
)
|
||||
| (
|
||||
_,
|
||||
todo @ Type::Dynamic(
|
||||
DynamicType::Todo(_) | DynamicType::TodoUnpack | DynamicType::TodoTypeAlias,
|
||||
),
|
||||
_,
|
||||
) => Some(todo),
|
||||
(todo @ Type::Dynamic(DynamicType::Todo(_) | DynamicType::TodoUnpack), _, _)
|
||||
| (_, todo @ Type::Dynamic(DynamicType::Todo(_) | DynamicType::TodoUnpack), _) => {
|
||||
Some(todo)
|
||||
}
|
||||
|
||||
(Type::Never, _, _) | (_, Type::Never, _) => Some(Type::Never),
|
||||
|
||||
|
|
|
|||
|
|
@ -18,21 +18,17 @@ impl<'db> TypeInferenceBuilder<'db, '_> {
|
|||
annotation: &ast::Expr,
|
||||
deferred_state: DeferredExpressionState,
|
||||
) -> TypeAndQualifiers<'db> {
|
||||
// `DeferredExpressionState::InStringAnnotation` takes precedence over other deferred states.
|
||||
// However, if it's not a stringified annotation, we must still ensure that annotation expressions
|
||||
// are always deferred in stub files.
|
||||
let state = if deferred_state.in_string_annotation() {
|
||||
deferred_state
|
||||
} else if self.in_stub() {
|
||||
DeferredExpressionState::Deferred
|
||||
} else {
|
||||
deferred_state
|
||||
};
|
||||
self.infer_annotation_expression_inner(annotation, deferred_state, false)
|
||||
}
|
||||
|
||||
let previous_deferred_state = std::mem::replace(&mut self.deferred_state, state);
|
||||
let annotation_ty = self.infer_annotation_expression_impl(annotation);
|
||||
self.deferred_state = previous_deferred_state;
|
||||
annotation_ty
|
||||
/// Infer the type of an annotation expression with the given [`DeferredExpressionState`],
|
||||
/// allowing a PEP 613 `typing.TypeAlias` annotation.
|
||||
pub(super) fn infer_annotation_expression_allow_pep_613(
|
||||
&mut self,
|
||||
annotation: &ast::Expr,
|
||||
deferred_state: DeferredExpressionState,
|
||||
) -> TypeAndQualifiers<'db> {
|
||||
self.infer_annotation_expression_inner(annotation, deferred_state, true)
|
||||
}
|
||||
|
||||
/// Similar to [`infer_annotation_expression`], but accepts an optional annotation expression
|
||||
|
|
@ -47,17 +43,42 @@ impl<'db> TypeInferenceBuilder<'db, '_> {
|
|||
annotation.map(|expr| self.infer_annotation_expression(expr, deferred_state))
|
||||
}
|
||||
|
||||
fn infer_annotation_expression_inner(
|
||||
&mut self,
|
||||
annotation: &ast::Expr,
|
||||
deferred_state: DeferredExpressionState,
|
||||
allow_pep_613: bool,
|
||||
) -> TypeAndQualifiers<'db> {
|
||||
// `DeferredExpressionState::InStringAnnotation` takes precedence over other deferred states.
|
||||
// However, if it's not a stringified annotation, we must still ensure that annotation expressions
|
||||
// are always deferred in stub files.
|
||||
let state = if deferred_state.in_string_annotation() {
|
||||
deferred_state
|
||||
} else if self.in_stub() {
|
||||
DeferredExpressionState::Deferred
|
||||
} else {
|
||||
deferred_state
|
||||
};
|
||||
|
||||
let previous_deferred_state = std::mem::replace(&mut self.deferred_state, state);
|
||||
let annotation_ty = self.infer_annotation_expression_impl(annotation, allow_pep_613);
|
||||
self.deferred_state = previous_deferred_state;
|
||||
annotation_ty
|
||||
}
|
||||
|
||||
/// Implementation of [`infer_annotation_expression`].
|
||||
///
|
||||
/// [`infer_annotation_expression`]: TypeInferenceBuilder::infer_annotation_expression
|
||||
fn infer_annotation_expression_impl(
|
||||
&mut self,
|
||||
annotation: &ast::Expr,
|
||||
allow_pep_613: bool,
|
||||
) -> TypeAndQualifiers<'db> {
|
||||
fn infer_name_or_attribute<'db>(
|
||||
ty: Type<'db>,
|
||||
annotation: &ast::Expr,
|
||||
builder: &TypeInferenceBuilder<'db, '_>,
|
||||
allow_pep_613: bool,
|
||||
) -> TypeAndQualifiers<'db> {
|
||||
match ty {
|
||||
Type::SpecialForm(SpecialFormType::ClassVar) => TypeAndQualifiers::new(
|
||||
|
|
@ -85,6 +106,22 @@ impl<'db> TypeInferenceBuilder<'db, '_> {
|
|||
TypeOrigin::Declared,
|
||||
TypeQualifiers::READ_ONLY,
|
||||
),
|
||||
Type::SpecialForm(SpecialFormType::TypeAlias) if allow_pep_613 => {
|
||||
TypeAndQualifiers::declared(ty)
|
||||
}
|
||||
// Conditional import of `typing.TypeAlias` or `typing_extensions.TypeAlias` on a
|
||||
// Python version where the former doesn't exist.
|
||||
Type::Union(union)
|
||||
if allow_pep_613
|
||||
&& union.elements(builder.db()).iter().all(|ty| {
|
||||
matches!(
|
||||
ty,
|
||||
Type::SpecialForm(SpecialFormType::TypeAlias) | Type::Dynamic(_)
|
||||
)
|
||||
}) =>
|
||||
{
|
||||
TypeAndQualifiers::declared(Type::SpecialForm(SpecialFormType::TypeAlias))
|
||||
}
|
||||
Type::ClassLiteral(class) if class.is_known(builder.db(), KnownClass::InitVar) => {
|
||||
if let Some(builder) =
|
||||
builder.context.report_lint(&INVALID_TYPE_FORM, annotation)
|
||||
|
|
@ -148,6 +185,7 @@ impl<'db> TypeInferenceBuilder<'db, '_> {
|
|||
self.infer_attribute_expression(attribute),
|
||||
annotation,
|
||||
self,
|
||||
allow_pep_613,
|
||||
),
|
||||
ast::ExprContext::Invalid => TypeAndQualifiers::declared(Type::unknown()),
|
||||
ast::ExprContext::Store | ast::ExprContext::Del => TypeAndQualifiers::declared(
|
||||
|
|
@ -156,9 +194,12 @@ impl<'db> TypeInferenceBuilder<'db, '_> {
|
|||
},
|
||||
|
||||
ast::Expr::Name(name) => match name.ctx {
|
||||
ast::ExprContext::Load => {
|
||||
infer_name_or_attribute(self.infer_name_expression(name), annotation, self)
|
||||
}
|
||||
ast::ExprContext::Load => infer_name_or_attribute(
|
||||
self.infer_name_expression(name),
|
||||
annotation,
|
||||
self,
|
||||
allow_pep_613,
|
||||
),
|
||||
ast::ExprContext::Invalid => TypeAndQualifiers::declared(Type::unknown()),
|
||||
ast::ExprContext::Store | ast::ExprContext::Del => TypeAndQualifiers::declared(
|
||||
todo_type!("Name expression annotation in Store/Del context"),
|
||||
|
|
@ -189,7 +230,7 @@ impl<'db> TypeInferenceBuilder<'db, '_> {
|
|||
}
|
||||
|
||||
let inner_annotation_ty =
|
||||
self.infer_annotation_expression_impl(inner_annotation);
|
||||
self.infer_annotation_expression_impl(inner_annotation, false);
|
||||
|
||||
self.store_expression_type(slice, inner_annotation_ty.inner_type());
|
||||
inner_annotation_ty
|
||||
|
|
@ -202,7 +243,7 @@ impl<'db> TypeInferenceBuilder<'db, '_> {
|
|||
}
|
||||
} else {
|
||||
report_invalid_arguments_to_annotated(&self.context, subscript);
|
||||
self.infer_annotation_expression_impl(slice)
|
||||
self.infer_annotation_expression_impl(slice, false)
|
||||
}
|
||||
}
|
||||
Type::SpecialForm(
|
||||
|
|
@ -220,7 +261,7 @@ impl<'db> TypeInferenceBuilder<'db, '_> {
|
|||
let num_arguments = arguments.len();
|
||||
let type_and_qualifiers = if num_arguments == 1 {
|
||||
let mut type_and_qualifiers =
|
||||
self.infer_annotation_expression_impl(slice);
|
||||
self.infer_annotation_expression_impl(slice, false);
|
||||
|
||||
match type_qualifier {
|
||||
SpecialFormType::ClassVar => {
|
||||
|
|
@ -243,7 +284,7 @@ impl<'db> TypeInferenceBuilder<'db, '_> {
|
|||
type_and_qualifiers
|
||||
} else {
|
||||
for element in arguments {
|
||||
self.infer_annotation_expression_impl(element);
|
||||
self.infer_annotation_expression_impl(element, false);
|
||||
}
|
||||
if let Some(builder) =
|
||||
self.context.report_lint(&INVALID_TYPE_FORM, subscript)
|
||||
|
|
@ -269,12 +310,12 @@ impl<'db> TypeInferenceBuilder<'db, '_> {
|
|||
let num_arguments = arguments.len();
|
||||
let type_and_qualifiers = if num_arguments == 1 {
|
||||
let mut type_and_qualifiers =
|
||||
self.infer_annotation_expression_impl(slice);
|
||||
self.infer_annotation_expression_impl(slice, false);
|
||||
type_and_qualifiers.add_qualifier(TypeQualifiers::INIT_VAR);
|
||||
type_and_qualifiers
|
||||
} else {
|
||||
for element in arguments {
|
||||
self.infer_annotation_expression_impl(element);
|
||||
self.infer_annotation_expression_impl(element, false);
|
||||
}
|
||||
if let Some(builder) =
|
||||
self.context.report_lint(&INVALID_TYPE_FORM, subscript)
|
||||
|
|
|
|||
|
|
@ -825,6 +825,10 @@ impl<'db> TypeInferenceBuilder<'db, '_> {
|
|||
self.infer_type_expression(slice);
|
||||
todo_type!("Generic manual PEP-695 type alias")
|
||||
}
|
||||
KnownInstanceType::LiteralStringAlias(_) => {
|
||||
self.infer_type_expression(slice);
|
||||
todo_type!("Generic stringified PEP-613 type alias")
|
||||
}
|
||||
KnownInstanceType::UnionType(_) => {
|
||||
self.infer_type_expression(slice);
|
||||
todo_type!("Generic specialization of types.UnionType")
|
||||
|
|
|
|||
|
|
@ -269,9 +269,6 @@ fn dynamic_elements_ordering(left: DynamicType, right: DynamicType) -> Ordering
|
|||
(DynamicType::TodoUnpack, _) => Ordering::Less,
|
||||
(_, DynamicType::TodoUnpack) => Ordering::Greater,
|
||||
|
||||
(DynamicType::TodoTypeAlias, _) => Ordering::Less,
|
||||
(_, DynamicType::TodoTypeAlias) => Ordering::Greater,
|
||||
|
||||
(DynamicType::Divergent(left), DynamicType::Divergent(right)) => {
|
||||
left.scope.cmp(&right.scope)
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue