mirror of
https://gitflic.ru/project/openide/openide.git
synced 2026-04-19 13:02:30 +07:00
PY-80195 Literals of Enums broken after import (2025.1 EAP)
Consider enum attribute of an unknown type an enum member. (cherry picked from commit 72c2fd37f18e4dde65d735f1032495df5aeda791) GitOrigin-RevId: 7e56afe6a2d2efe0ea84ac20672aaf7712d8ed23
This commit is contained in:
committed by
intellij-monorepo-bot
parent
ed52e34951
commit
c52f4bbadf
@@ -57,6 +57,11 @@ public final class PyStdlibTypeProvider extends PyTypeProviderBase {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public @Nullable PyType getCallableType(@NotNull PyCallable callable, @NotNull TypeEvalContext context) {
|
||||
return Ref.deref(getTransformedEnumAttributeType(callable, context));
|
||||
}
|
||||
|
||||
@Override
|
||||
public @Nullable PyType getReferenceExpressionType(@NotNull PyReferenceExpression referenceExpression, @NotNull TypeEvalContext context) {
|
||||
if (!referenceExpression.isQualified()) {
|
||||
@@ -86,12 +91,12 @@ public final class PyStdlibTypeProvider extends PyTypeProviderBase {
|
||||
*/
|
||||
@ApiStatus.Internal
|
||||
public static @Nullable PyLiteralType getEnumMemberType(@NotNull PsiElement element, @NotNull TypeEvalContext context) {
|
||||
return ObjectUtils.tryCast(Ref.deref(getEnumAttributeType(element, context)), PyLiteralType.class);
|
||||
return ObjectUtils.tryCast(Ref.deref(getTransformedEnumAttributeType(element, context)), PyLiteralType.class);
|
||||
}
|
||||
|
||||
private static @Nullable Ref<PyType> getEnumType(@NotNull PsiElement referenceTarget, @NotNull TypeEvalContext context,
|
||||
@Nullable PsiElement anchor) {
|
||||
@Nullable Ref<PyType> enumAttributeType = getEnumAttributeType(referenceTarget, context);
|
||||
@Nullable Ref<PyType> enumAttributeType = getTransformedEnumAttributeType(referenceTarget, context);
|
||||
if (enumAttributeType != null) {
|
||||
return enumAttributeType;
|
||||
}
|
||||
@@ -135,11 +140,12 @@ public final class PyStdlibTypeProvider extends PyTypeProviderBase {
|
||||
return null;
|
||||
}
|
||||
|
||||
private static @Nullable Ref<PyType> getEnumAttributeType(@NotNull PsiElement element, @NotNull TypeEvalContext context) {
|
||||
return RecursionManager.doPreventingRecursion(element, false, () -> getEnumAttributeTypeImpl(element, context));
|
||||
// Returns the type of enum attribute value transformed by 'EnumType' metaclass or null, if the attribute value is not transformed
|
||||
private static @Nullable Ref<PyType> getTransformedEnumAttributeType(@NotNull PsiElement element, @NotNull TypeEvalContext context) {
|
||||
return RecursionManager.doPreventingRecursion(element, false, () -> getTransformedEnumAttributeTypeImpl(element, context));
|
||||
}
|
||||
|
||||
private static @Nullable Ref<PyType> getEnumAttributeTypeImpl(@NotNull PsiElement element, @NotNull TypeEvalContext context) {
|
||||
private static @Nullable Ref<PyType> getTransformedEnumAttributeTypeImpl(@NotNull PsiElement element, @NotNull TypeEvalContext context) {
|
||||
if (!(element instanceof PyTargetExpression) && !(element instanceof PyDecoratable)) return null;
|
||||
if (!(ScopeUtil.getScopeOwner(element) instanceof PyClass cls && isCustomEnum(cls, context))) return null;
|
||||
|
||||
@@ -216,6 +222,7 @@ public final class PyStdlibTypeProvider extends PyTypeProviderBase {
|
||||
else {
|
||||
if (!targetExpression.hasAssignedValue()) return null;
|
||||
|
||||
// Handle enum.auto(), enum.member(), enum.nonmember()
|
||||
PyTargetExpressionStub stub = targetExpression.getStub();
|
||||
PyEnumAttributeStub attributeStub = stub != null
|
||||
? stub.getCustomStub(PyEnumAttributeStub.class)
|
||||
@@ -228,29 +235,22 @@ public final class PyStdlibTypeProvider extends PyTypeProviderBase {
|
||||
|
||||
QualifiedName assignedQName = targetExpression.getAssignedQName();
|
||||
if (assignedQName != null) {
|
||||
PsiElement value = ContainerUtil.getFirstItem(PyResolveUtil.resolveQualifiedNameInScope(assignedQName, enumClass, context));
|
||||
if (value != null) {
|
||||
Ref<PyType> type = getEnumAttributeType(value, context);
|
||||
return type == null ? null : getEnumAttributeInfo(enumClass, type.get(), context);
|
||||
}
|
||||
PsiElement resolved = ContainerUtil.getFirstItem(PyResolveUtil.resolveQualifiedNameInScope(assignedQName, enumClass, context));
|
||||
PyType type = resolved instanceof PyTypedElement ? context.getType((PyTypedElement)resolved) : null;
|
||||
return getEnumAttributeInfo(enumClass, type, context);
|
||||
}
|
||||
|
||||
PyLiteralKind literalKind = stub != null
|
||||
? stub.getAssignedLiteralKind()
|
||||
: PyLiteralKind.fromExpression(targetExpression.findAssignedValue());
|
||||
if (literalKind == null) {
|
||||
return EnumAttributeInfo.nonMember(null);
|
||||
}
|
||||
else {
|
||||
PyType type = PyUtil.convertToType(literalKind, PyBuiltinCache.getInstance(targetExpression));
|
||||
return new EnumAttributeInfo(type, EnumAttributeKind.MEMBER);
|
||||
}
|
||||
PyType type = literalKind != null ? PyUtil.convertToType(literalKind, PyBuiltinCache.getInstance(targetExpression)) : null;
|
||||
return new EnumAttributeInfo(type, EnumAttributeKind.MEMBER);
|
||||
}
|
||||
}
|
||||
|
||||
private static @NotNull EnumAttributeInfo getEnumAttributeInfo(@NotNull PyClass enumClass, @Nullable PyType type, @NotNull TypeEvalContext context) {
|
||||
if (type == null) {
|
||||
return EnumAttributeInfo.nonMember(null);
|
||||
return new EnumAttributeInfo(null, EnumAttributeKind.MEMBER);
|
||||
}
|
||||
PyQualifiedNameOwner typeDeclarationElement = type.getDeclarationElement();
|
||||
if (typeDeclarationElement != null) {
|
||||
|
||||
@@ -0,0 +1,5 @@
|
||||
from typing import Literal
|
||||
from m import SimpleEnum, SuperEnum
|
||||
|
||||
p: Literal[SuperEnum.PINK] = SuperEnum.PINK
|
||||
q: Literal[SimpleEnum.FOO] = <warning descr="Expected type 'Literal[SimpleEnum.FOO]', got 'Literal[SuperEnum.PINK]' instead">SuperEnum.PINK</warning>
|
||||
@@ -0,0 +1,10 @@
|
||||
import enum
|
||||
|
||||
|
||||
class SuperEnum(enum.Enum):
|
||||
PINK = "PINK", "hot"
|
||||
FLOSS = "FLOSS", "sweet"
|
||||
|
||||
|
||||
class SimpleEnum(enum.Enum):
|
||||
FOO = "FOO"
|
||||
@@ -0,0 +1,21 @@
|
||||
from typing import Literal
|
||||
from m import *
|
||||
|
||||
|
||||
v1: Literal[<warning descr="'Literal' may be parameterized with literal ints, byte and unicode strings, bools, Enum values, None, other literal types, or type aliases to other literal types">A.X</warning>]
|
||||
|
||||
X = Color.R
|
||||
v2: Literal[<warning descr="'Literal' may be parameterized with literal ints, byte and unicode strings, bools, Enum values, None, other literal types, or type aliases to other literal types">X</warning>]
|
||||
|
||||
v3: Literal[Color.G]
|
||||
v4: Literal[Color.RED]
|
||||
v5: Literal[<warning descr="'Literal' may be parameterized with literal ints, byte and unicode strings, bools, Enum values, None, other literal types, or type aliases to other literal types">Color.foo</warning>]
|
||||
v6: Literal[Color.bar]
|
||||
|
||||
v7: Literal[SuperEnum.PINK]
|
||||
|
||||
v8: Literal[E.FOO]
|
||||
v9: Literal[E.BAR]
|
||||
v10: Literal[E.BUZ]
|
||||
v11: Literal[E.QUX]
|
||||
v12: Literal[<warning descr="'Literal' may be parameterized with literal ints, byte and unicode strings, bools, Enum values, None, other literal types, or type aliases to other literal types">E.meth2</warning>]
|
||||
@@ -0,0 +1,38 @@
|
||||
from enum import Enum, member, nonmember
|
||||
from typing import Any
|
||||
|
||||
|
||||
class Color(Enum):
|
||||
R = 1
|
||||
G = 2
|
||||
RED = R
|
||||
foo = nonmember(3)
|
||||
@member
|
||||
def bar(self): ...
|
||||
|
||||
|
||||
class A:
|
||||
X = Color.R
|
||||
|
||||
|
||||
class SuperEnum(Enum):
|
||||
PINK = "PINK", "hot"
|
||||
FLOSS = "FLOSS", "sweet"
|
||||
|
||||
|
||||
tuple = 1, "ab"
|
||||
o = object()
|
||||
def get_object() -> object: ...
|
||||
def get_any() -> Any: ...
|
||||
|
||||
|
||||
class E(Enum):
|
||||
FOO = tuple
|
||||
BAR = o
|
||||
BUZ = get_object()
|
||||
QUX = get_any()
|
||||
|
||||
def meth(self): ...
|
||||
|
||||
meth2 = meth
|
||||
|
||||
@@ -623,6 +623,11 @@ public class Py3TypeCheckerInspectionTest extends PyInspectionTestCase {
|
||||
);
|
||||
}
|
||||
|
||||
// PY-80195
|
||||
public void testMultiValueEnum() {
|
||||
doMultiFileTest();
|
||||
}
|
||||
|
||||
// PY-42418
|
||||
public void testParametrizedBuiltinCollectionsAndTheirTypingAliasesAreEquivalent() {
|
||||
doTest();
|
||||
|
||||
@@ -1236,7 +1236,7 @@ public class PyTypeHintsInspectionTest extends PyInspectionTestCase {
|
||||
public void testEnumLiteral() {
|
||||
doTestByText("""
|
||||
from enum import Enum, member, nonmember
|
||||
from typing import Literal
|
||||
from typing import Literal, Any
|
||||
|
||||
class Color(Enum):
|
||||
R = 1
|
||||
@@ -1251,6 +1251,25 @@ public class PyTypeHintsInspectionTest extends PyInspectionTestCase {
|
||||
class A:
|
||||
X = Color.R
|
||||
|
||||
class SuperEnum(Enum):
|
||||
PINK = "PINK", "hot"
|
||||
FLOSS = "FLOSS", "sweet"
|
||||
|
||||
tuple = 1, "ab"
|
||||
o = object()
|
||||
def get_object() -> object: ...
|
||||
def get_any() -> Any: ...
|
||||
|
||||
class E(Enum):
|
||||
FOO = tuple
|
||||
BAR = o
|
||||
BUZ = get_object()
|
||||
QUX = get_any()
|
||||
|
||||
def meth(self): ...
|
||||
|
||||
meth2 = meth
|
||||
|
||||
v1: Literal[<warning descr="'Literal' may be parameterized with literal ints, byte and unicode strings, bools, Enum values, None, other literal types, or type aliases to other literal types">A.X</warning>]
|
||||
|
||||
X = Color.R
|
||||
@@ -1259,7 +1278,20 @@ public class PyTypeHintsInspectionTest extends PyInspectionTestCase {
|
||||
v3: Literal[Color.G]
|
||||
v4: Literal[Color.RED]
|
||||
v5: Literal[<warning descr="'Literal' may be parameterized with literal ints, byte and unicode strings, bools, Enum values, None, other literal types, or type aliases to other literal types">Color.foo</warning>]
|
||||
v6: Literal[Color.bar]""");
|
||||
v6: Literal[Color.bar]
|
||||
|
||||
v7: Literal[SuperEnum.PINK]
|
||||
|
||||
v8: Literal[E.FOO]
|
||||
v9: Literal[E.BAR]
|
||||
v10: Literal[E.BUZ]
|
||||
v11: Literal[E.QUX]
|
||||
v12: Literal[<warning descr="'Literal' may be parameterized with literal ints, byte and unicode strings, bools, Enum values, None, other literal types, or type aliases to other literal types">E.meth2</warning>]""");
|
||||
}
|
||||
|
||||
// PY-79227
|
||||
public void testEnumLiteralMultiFile() {
|
||||
doMultiFileTest();
|
||||
}
|
||||
|
||||
// PY-35235
|
||||
|
||||
Reference in New Issue
Block a user