[red-knot] Fix false positives on types.UnionType instances in type expressions (#17297)

This commit is contained in:
Alex Waygood 2025-04-09 18:33:16 +01:00 committed by GitHub
parent 484a8ed36d
commit 781b653511
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
3 changed files with 30 additions and 1 deletions

View file

@ -2,7 +2,7 @@
## Annotation ## Annotation
`typing.Union` can be used to construct union types same as `|` operator. `typing.Union` can be used to construct union types in the same way as the `|` operator.
```py ```py
from typing import Union from typing import Union
@ -69,3 +69,20 @@ from typing import Union
def f(x: Union) -> None: def f(x: Union) -> None:
reveal_type(x) # revealed: Unknown reveal_type(x) # revealed: Unknown
``` ```
## Implicit type aliases using new-style unions
We don't recognise these as type aliases yet, but we also don't emit false-positive diagnostics if
you use them in type expressions:
```toml
[environment]
python-version = "3.10"
```
```py
X = int | str
def f(y: X):
reveal_type(y) # revealed: @Todo(Support for `types.UnionType` instances in type expressions)
```

View file

@ -4035,6 +4035,9 @@ impl<'db> Type<'db> {
Some(KnownClass::GenericAlias) => Ok(todo_type!( Some(KnownClass::GenericAlias) => Ok(todo_type!(
"Support for `typing.GenericAlias` instances in type expressions" "Support for `typing.GenericAlias` instances in type expressions"
)), )),
Some(KnownClass::UnionType) => Ok(todo_type!(
"Support for `types.UnionType` instances in type expressions"
)),
_ => Err(InvalidTypeExpressionError { _ => Err(InvalidTypeExpressionError {
invalid_expressions: smallvec::smallvec![InvalidTypeExpression::InvalidType( invalid_expressions: smallvec::smallvec![InvalidTypeExpression::InvalidType(
*self *self

View file

@ -1170,6 +1170,7 @@ pub(crate) enum KnownClass {
MethodType, MethodType,
MethodWrapperType, MethodWrapperType,
WrapperDescriptorType, WrapperDescriptorType,
UnionType,
// Typeshed // Typeshed
NoneType, // Part of `types` for Python >= 3.10 NoneType, // Part of `types` for Python >= 3.10
// Typing // Typing
@ -1233,6 +1234,7 @@ impl<'db> KnownClass {
| Self::ParamSpecKwargs | Self::ParamSpecKwargs
| Self::TypeVarTuple | Self::TypeVarTuple
| Self::WrapperDescriptorType | Self::WrapperDescriptorType
| Self::UnionType
| Self::MethodWrapperType => Truthiness::AlwaysTrue, | Self::MethodWrapperType => Truthiness::AlwaysTrue,
Self::NoneType => Truthiness::AlwaysFalse, Self::NoneType => Truthiness::AlwaysFalse,
@ -1305,6 +1307,7 @@ impl<'db> KnownClass {
Self::ModuleType => "ModuleType", Self::ModuleType => "ModuleType",
Self::FunctionType => "FunctionType", Self::FunctionType => "FunctionType",
Self::MethodType => "MethodType", Self::MethodType => "MethodType",
Self::UnionType => "UnionType",
Self::MethodWrapperType => "MethodWrapperType", Self::MethodWrapperType => "MethodWrapperType",
Self::WrapperDescriptorType => "WrapperDescriptorType", Self::WrapperDescriptorType => "WrapperDescriptorType",
Self::NoneType => "NoneType", Self::NoneType => "NoneType",
@ -1487,6 +1490,7 @@ impl<'db> KnownClass {
| Self::FunctionType | Self::FunctionType
| Self::MethodType | Self::MethodType
| Self::MethodWrapperType | Self::MethodWrapperType
| Self::UnionType
| Self::WrapperDescriptorType => KnownModule::Types, | Self::WrapperDescriptorType => KnownModule::Types,
Self::NoneType => KnownModule::Typeshed, Self::NoneType => KnownModule::Typeshed,
Self::Any Self::Any
@ -1539,6 +1543,7 @@ impl<'db> KnownClass {
| Self::VersionInfo | Self::VersionInfo
| Self::EllipsisType | Self::EllipsisType
| Self::TypeAliasType | Self::TypeAliasType
| Self::UnionType
| Self::NotImplementedType => true, | Self::NotImplementedType => true,
Self::Any Self::Any
@ -1643,6 +1648,7 @@ impl<'db> KnownClass {
| Self::Sized | Self::Sized
| Self::Enum | Self::Enum
| Self::Super | Self::Super
| Self::UnionType
| Self::NewType => false, | Self::NewType => false,
} }
} }
@ -1681,6 +1687,7 @@ impl<'db> KnownClass {
"ModuleType" => Self::ModuleType, "ModuleType" => Self::ModuleType,
"FunctionType" => Self::FunctionType, "FunctionType" => Self::FunctionType,
"MethodType" => Self::MethodType, "MethodType" => Self::MethodType,
"UnionType" => Self::UnionType,
"MethodWrapperType" => Self::MethodWrapperType, "MethodWrapperType" => Self::MethodWrapperType,
"WrapperDescriptorType" => Self::WrapperDescriptorType, "WrapperDescriptorType" => Self::WrapperDescriptorType,
"NewType" => Self::NewType, "NewType" => Self::NewType,
@ -1758,6 +1765,7 @@ impl<'db> KnownClass {
| Self::Enum | Self::Enum
| Self::Super | Self::Super
| Self::NotImplementedType | Self::NotImplementedType
| Self::UnionType
| Self::WrapperDescriptorType => module == self.canonical_module(db), | Self::WrapperDescriptorType => module == self.canonical_module(db),
Self::NoneType => matches!(module, KnownModule::Typeshed | KnownModule::Types), Self::NoneType => matches!(module, KnownModule::Typeshed | KnownModule::Types),
Self::SpecialForm Self::SpecialForm
@ -2259,6 +2267,7 @@ mod tests {
for class in KnownClass::iter() { for class in KnownClass::iter() {
let version_added = match class { let version_added = match class {
KnownClass::UnionType => PythonVersion::PY310,
KnownClass::BaseExceptionGroup => PythonVersion::PY311, KnownClass::BaseExceptionGroup => PythonVersion::PY311,
KnownClass::GenericAlias => PythonVersion::PY39, KnownClass::GenericAlias => PythonVersion::PY39,
_ => PythonVersion::PY37, _ => PythonVersion::PY37,