diff --git a/python/python-parser/src/com/jetbrains/python/PyNames.java b/python/python-parser/src/com/jetbrains/python/PyNames.java index 0d275ca27cbb..21d0a56db41c 100644 --- a/python/python-parser/src/com/jetbrains/python/PyNames.java +++ b/python/python-parser/src/com/jetbrains/python/PyNames.java @@ -243,7 +243,8 @@ public final @NonNls class PyNames { public static final String NOT_IMPLEMENTED_ERROR = "NotImplementedError"; - public static final @NlsSafe String UNKNOWN_TYPE = "Any"; + public static final @NlsSafe String ANY_TYPE = "Any"; + public static final @NlsSafe String UNKNOWN_TYPE = "Unknown"; public static final @NlsSafe String UNNAMED_ELEMENT = ""; diff --git a/python/python-psi-api/src/com/jetbrains/python/psi/types/PyAnyType.kt b/python/python-psi-api/src/com/jetbrains/python/psi/types/PyAnyType.kt new file mode 100644 index 000000000000..1eba990f5f58 --- /dev/null +++ b/python/python-psi-api/src/com/jetbrains/python/psi/types/PyAnyType.kt @@ -0,0 +1,41 @@ +// Copyright 2000-2026 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license. +package com.jetbrains.python.psi.types + +import com.intellij.psi.PsiElement +import com.intellij.util.ProcessingContext +import com.jetbrains.python.PyNames +import com.jetbrains.python.psi.AccessDirection +import com.jetbrains.python.psi.PyExpression +import com.jetbrains.python.psi.resolve.PyResolveContext +import com.jetbrains.python.psi.resolve.RatedResolveResult +import org.jetbrains.annotations.ApiStatus + +@ApiStatus.Experimental +class PyAnyType private constructor(override val name: String) : PyType { + override fun resolveMember( + name: String, + location: PyExpression?, + direction: AccessDirection, + resolveContext: PyResolveContext, + ): List { + return emptyList() + } + + override fun getCompletionVariants(completionPrefix: String?, location: PsiElement, context: ProcessingContext): Array { + return emptyArray() + } + + override val isBuiltin: Boolean = false + + override fun assertValid(message: String?) { + } + + override fun acceptTypeVisitor(visitor: PyTypeVisitor): T? = + if (this === Any) visitor.visitAnyType() + else visitor.visitUnknownType() + + companion object { + val Any: PyAnyType = PyAnyType(PyNames.ANY_TYPE) + val Unknown: PyAnyType = PyAnyType(PyNames.UNKNOWN_TYPE) + } +} diff --git a/python/python-psi-api/src/com/jetbrains/python/psi/types/PyCallableType.java b/python/python-psi-api/src/com/jetbrains/python/psi/types/PyCallableType.java index 49d11f986649..911a7cc51bde 100644 --- a/python/python-psi-api/src/com/jetbrains/python/psi/types/PyCallableType.java +++ b/python/python-psi-api/src/com/jetbrains/python/psi/types/PyCallableType.java @@ -80,14 +80,14 @@ public interface PyCallableType extends PyType { builder.append(name); builder.append(": "); } - builder.append(type != null ? type.getName() : PyNames.UNKNOWN_TYPE); + builder.append(type != null ? type.getName() : PyNames.ANY_TYPE); return builder.toString(); } - return PyNames.UNKNOWN_TYPE; + return PyNames.ANY_TYPE; }, ", ") : "...", - returnType != null ? returnType.getName() : PyNames.UNKNOWN_TYPE); + returnType != null ? returnType.getName() : PyNames.ANY_TYPE); } default @Nullable PyCallable getCallable() { diff --git a/python/python-psi-api/src/com/jetbrains/python/psi/types/PyType.kt b/python/python-psi-api/src/com/jetbrains/python/psi/types/PyType.kt index 027eea0103f8..a3d5c9367d35 100644 --- a/python/python-psi-api/src/com/jetbrains/python/psi/types/PyType.kt +++ b/python/python-psi-api/src/com/jetbrains/python/psi/types/PyType.kt @@ -64,7 +64,7 @@ interface PyType { * @param context to share state between nested invocations * @return completion variants good for [com.intellij.psi.PsiReference.getVariants] return value. */ - fun getCompletionVariants(completionPrefix: String?, location: PsiElement, context: ProcessingContext): Array + fun getCompletionVariants(completionPrefix: String?, location: PsiElement, context: ProcessingContext): Array @get:NlsSafe val name: @NlsSafe String? diff --git a/python/python-psi-api/src/com/jetbrains/python/psi/types/PyTypeVisitor.kt b/python/python-psi-api/src/com/jetbrains/python/psi/types/PyTypeVisitor.kt index 815da212df12..c69256c673af 100644 --- a/python/python-psi-api/src/com/jetbrains/python/psi/types/PyTypeVisitor.kt +++ b/python/python-psi-api/src/com/jetbrains/python/psi/types/PyTypeVisitor.kt @@ -1,85 +1,93 @@ // Copyright 2000-2024 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license. -package com.jetbrains.python.psi.types; - -import org.jetbrains.annotations.ApiStatus; -import org.jetbrains.annotations.NotNull; -import org.jetbrains.annotations.Nullable; +package com.jetbrains.python.psi.types +import org.jetbrains.annotations.ApiStatus /** - * Similarly to {@link com.intellij.psi.PsiElementVisitor}, implements double dispatching for the {@link PyType} hierarchy. - *

- * Because the "unknown" type is historically represented as {@code null} in the type system, {@code PyTypeVisitor.visitPyType(type, visitor)} - * should be used instead of direct {@code type.acceptTypeVisitor(visitor)} to properly account for possible {@code null} values. - *

+ * Similarly to [com.intellij.psi.PsiElementVisitor], implements double dispatching for the [PyType] hierarchy. + * + * + * Because the "unknown" type is historically represented as `null` in the type system, `PyTypeVisitor.visitPyType(type, visitor)` + * should be used instead of direct `type.acceptTypeVisitor(visitor)` to properly account for possible `null` values. + * + * * This class gives access only to the types declared in the intellij.python.psi module. - * Most actual implementations should extend {@link PyTypeVisitorExt}. - *

- * There are helper {@link PyRecursiveTypeVisitor} and {@link PyCloningTypeVisitor} for recursive type + * Most actual implementations should extend [PyTypeVisitorExt]. + * + * + * There are helper [PyRecursiveTypeVisitor] and [PyCloningTypeVisitor] for recursive type * traversal and deep cloning of a type respectively. - * + * * @see PyRecursiveTypeVisitor + * * @see PyCloningTypeVisitor - * @see PyType#acceptTypeVisitor(PyTypeVisitor) - * @see #visit(PyType, PyTypeVisitor) - * @see #visitUnknownType() + * + * @see PyType.acceptTypeVisitor + * @see .visit + * @see .visitUnknownType */ @ApiStatus.Experimental -public abstract class PyTypeVisitor { - /** - * Use this method instead of {@link PyType#acceptTypeVisitor(PyTypeVisitor)} to take into account - * "unknown" {@code null} types, for which {@link #visitUnknownType()} should be called. - */ - public static T visit(@Nullable PyType type, @NotNull PyTypeVisitor visitor) { - return type == null ? visitor.visitUnknownType() : type.acceptTypeVisitor(visitor); +abstract class PyTypeVisitor { + open fun visitPyType(type: PyType): T? { + return null } - public T visitPyType(@NotNull PyType type) { - return null; + open fun visitPyClassType(classType: PyClassType): T? { + return visitPyClassLikeType(classType) } - public T visitPyClassType(@NotNull PyClassType classType) { - return visitPyClassLikeType(classType); - } - - public T visitPyClassLikeType(@NotNull PyClassLikeType classLikeType) { + open fun visitPyClassLikeType(classLikeType: PyClassLikeType): T? { // Don't treat PyClassLikeType as PyCallableType. It's usually not what a visitor's user expects. - return visitPyType(classLikeType); + return visitPyType(classLikeType) } - public T visitPyFunctionType(@NotNull PyFunctionType functionType) { - return visitPyCallableType(functionType); + open fun visitPyFunctionType(functionType: PyFunctionType): T? { + return visitPyCallableType(functionType) } - public T visitPyCallableType(@NotNull PyCallableType callableType) { - return visitPyType(callableType); + open fun visitPyCallableType(callableType: PyCallableType): T? { + return visitPyType(callableType) } - public T visitPyTypeVarType(@NotNull PyTypeVarType typeVarType) { - return visitPyTypeParameterType(typeVarType); + open fun visitPyTypeVarType(typeVarType: PyTypeVarType): T? { + return visitPyTypeParameterType(typeVarType) } - public T visitPyTypeVarTupleType(@NotNull PyTypeVarTupleType typeVarTupleType) { - return visitPyTypeParameterType(typeVarTupleType); + open fun visitPyTypeVarTupleType(typeVarTupleType: PyTypeVarTupleType): T? { + return visitPyTypeParameterType(typeVarTupleType) } - public T visitPyTypeParameterType(@NotNull PyTypeParameterType typeParameterType) { - return visitPyType(typeParameterType); + open fun visitPyTypeParameterType(typeParameterType: PyTypeParameterType): T? { + return visitPyType(typeParameterType) } - public T visitPyUnpackedTupleType(@NotNull PyUnpackedTupleType unpackedTupleType) { - return visitPyType(unpackedTupleType); + open fun visitPyUnpackedTupleType(unpackedTupleType: PyUnpackedTupleType): T? { + return visitPyType(unpackedTupleType) } - public T visitPyCallableParameterListType(@NotNull PyCallableParameterListType callableParameterListType) { - return visitPyType(callableParameterListType); + open fun visitPyCallableParameterListType(callableParameterListType: PyCallableParameterListType): T? { + return visitPyType(callableParameterListType) } - public T visitPyNeverType(@NotNull PyNeverType neverType) { - return visitPyType(neverType); + open fun visitPyNeverType(neverType: PyNeverType): T? { + return visitPyType(neverType) } - public T visitUnknownType() { - return null; + open fun visitAnyType(): T? { + return null + } + + open fun visitUnknownType(): T? { + return null + } + + companion object { + /** + * Use this method instead of [PyType.acceptTypeVisitor] to take into account + * "unknown" `null` types, for which [.visitUnknownType] should be called. + */ + @JvmStatic + fun visit(type: PyType?, visitor: PyTypeVisitor): T? = + if (type == null) visitor.visitUnknownType() else type.acceptTypeVisitor(visitor) } } diff --git a/python/python-psi-impl/src/com/jetbrains/python/documentation/PyTypeRenderer.java b/python/python-psi-impl/src/com/jetbrains/python/documentation/PyTypeRenderer.java index c9419e3328fd..d2cc5b4ac342 100644 --- a/python/python-psi-impl/src/com/jetbrains/python/documentation/PyTypeRenderer.java +++ b/python/python-psi-impl/src/com/jetbrains/python/documentation/PyTypeRenderer.java @@ -220,7 +220,7 @@ public abstract class PyTypeRenderer extends PyTypeVisitorExt<@NotNull HtmlChunk } @Override - public @NotNull HtmlChunk visitPyIntersectionType(com.jetbrains.python.psi.types.@NotNull PyIntersectionType intersectionType) { + public @NotNull HtmlChunk visitPyIntersectionType(@NotNull PyIntersectionType intersectionType) { // There is no way to represent intersections through the standard type hints at the moment return visitUnknownType(); } @@ -465,9 +465,15 @@ public abstract class PyTypeRenderer extends PyTypeVisitorExt<@NotNull HtmlChunk return result.toFragment(); } + @Override + public HtmlChunk visitAnyType() { + return HtmlChunk.raw(isRenderingFqn() ? PyTypingTypeProvider.ANY : PyNames.ANY_TYPE); //NON-NLS + } + @Override public HtmlChunk visitUnknownType() { - return HtmlChunk.raw(isRenderingFqn() ? "typing.Any" : "Any"); //NON-NLS + // TODO: show "Unknown" instead of "typing.Any" when we convert to it + return visitAnyType(); } @Override @@ -533,10 +539,7 @@ public abstract class PyTypeRenderer extends PyTypeVisitorExt<@NotNull HtmlChunk case ", " -> { yield styled(separator, PyHighlighter.PY_COMMA); } - case " | " -> { - yield styled(separator, PyHighlighter.PY_OPERATION_SIGN); - } - case " & " -> { + case " | ", " & " -> { yield styled(separator, PyHighlighter.PY_OPERATION_SIGN); } default -> { diff --git a/python/python-psi-impl/src/com/jetbrains/python/inspections/PyDocstringTypesInspection.java b/python/python-psi-impl/src/com/jetbrains/python/inspections/PyDocstringTypesInspection.java index ac95aadf0c80..640559666a82 100644 --- a/python/python-psi-impl/src/com/jetbrains/python/inspections/PyDocstringTypesInspection.java +++ b/python/python-psi-impl/src/com/jetbrains/python/inspections/PyDocstringTypesInspection.java @@ -115,7 +115,7 @@ public final class PyDocstringTypesInspection extends PyInspection { return type.getName(); } else { - return PyNames.UNKNOWN_TYPE; + return PyNames.ANY_TYPE; } } diff --git a/python/python-psi-impl/src/com/jetbrains/python/psi/types/PyCallableParameterListTypeImpl.java b/python/python-psi-impl/src/com/jetbrains/python/psi/types/PyCallableParameterListTypeImpl.java index a850f397a65d..7c849749a659 100644 --- a/python/python-psi-impl/src/com/jetbrains/python/psi/types/PyCallableParameterListTypeImpl.java +++ b/python/python-psi-impl/src/com/jetbrains/python/psi/types/PyCallableParameterListTypeImpl.java @@ -25,7 +25,7 @@ public final class PyCallableParameterListTypeImpl implements PyCallableParamete return String.format("[%s]", StringUtil.join(myParameters, param -> { PyType type = param.getType(context); - return type != null ? type.getName() : PyNames.UNKNOWN_TYPE; + return type != null ? type.getName() : PyNames.ANY_TYPE; }, ", ")); } diff --git a/python/python-psi-impl/src/com/jetbrains/python/psi/types/PyConcatenateType.kt b/python/python-psi-impl/src/com/jetbrains/python/psi/types/PyConcatenateType.kt index 9a2c1fde1eb4..d7c327a2786c 100644 --- a/python/python-psi-impl/src/com/jetbrains/python/psi/types/PyConcatenateType.kt +++ b/python/python-psi-impl/src/com/jetbrains/python/psi/types/PyConcatenateType.kt @@ -6,7 +6,7 @@ package com.jetbrains.python.psi.types class PyConcatenateType(val firstTypes: List, val paramSpec: PyParamSpecType?) : PyCallableParameterVariadicType { override val name: String = "Concatenate(${firstTypes.joinToString { it?.name ?: "Any" }}, ${paramSpec?.name ?: "..."})" - override fun acceptTypeVisitor(visitor: PyTypeVisitor): T { + override fun acceptTypeVisitor(visitor: PyTypeVisitor): T? { if (visitor is PyTypeVisitorExt) { return visitor.visitPyConcatenateType(this) } diff --git a/python/python-psi-impl/src/com/jetbrains/python/psi/types/PyDynamicallyEvaluatedType.java b/python/python-psi-impl/src/com/jetbrains/python/psi/types/PyDynamicallyEvaluatedType.java index 2931d3defd18..ce31d0916875 100644 --- a/python/python-psi-impl/src/com/jetbrains/python/psi/types/PyDynamicallyEvaluatedType.java +++ b/python/python-psi-impl/src/com/jetbrains/python/psi/types/PyDynamicallyEvaluatedType.java @@ -41,6 +41,6 @@ public final class PyDynamicallyEvaluatedType extends PyUnionType { @Override public String getName() { PyType res = excludeNull(); - return res != null ? res.getName() : PyNames.UNKNOWN_TYPE; + return res != null ? res.getName() : PyNames.ANY_TYPE; } } diff --git a/python/python-psi-impl/src/com/jetbrains/python/psi/types/PyIntersectionType.kt b/python/python-psi-impl/src/com/jetbrains/python/psi/types/PyIntersectionType.kt index c0690d774674..a493f32490fd 100644 --- a/python/python-psi-impl/src/com/jetbrains/python/psi/types/PyIntersectionType.kt +++ b/python/python-psi-impl/src/com/jetbrains/python/psi/types/PyIntersectionType.kt @@ -51,7 +51,7 @@ class PyIntersectionType private constructor(members: Collection) : PyT } } - override fun acceptTypeVisitor(visitor: PyTypeVisitor): T { + override fun acceptTypeVisitor(visitor: PyTypeVisitor): T? { if (visitor is PyTypeVisitorExt) { return visitor.visitPyIntersectionType(this) } diff --git a/python/python-psi-impl/src/com/jetbrains/python/psi/types/PyLiteralStringType.kt b/python/python-psi-impl/src/com/jetbrains/python/psi/types/PyLiteralStringType.kt index dee5b8e30ec0..532bacc59c57 100644 --- a/python/python-psi-impl/src/com/jetbrains/python/psi/types/PyLiteralStringType.kt +++ b/python/python-psi-impl/src/com/jetbrains/python/psi/types/PyLiteralStringType.kt @@ -33,7 +33,7 @@ class PyLiteralStringType private constructor(val cls: PyClass) : PyClassTypeImp return Objects.hash(super.hashCode(), cls) } - override fun acceptTypeVisitor(visitor: PyTypeVisitor): T { + override fun acceptTypeVisitor(visitor: PyTypeVisitor): T? { if (visitor is PyTypeVisitorExt) { return visitor.visitPyLiteralStringType(this) } diff --git a/python/python-psi-impl/src/com/jetbrains/python/psi/types/PyLiteralType.kt b/python/python-psi-impl/src/com/jetbrains/python/psi/types/PyLiteralType.kt index 0aef5ce0534e..e91e7bdfba01 100644 --- a/python/python-psi-impl/src/com/jetbrains/python/psi/types/PyLiteralType.kt +++ b/python/python-psi-impl/src/com/jetbrains/python/psi/types/PyLiteralType.kt @@ -52,7 +52,7 @@ class PyLiteralType private constructor(cls: PyClass, val expression: PyExpressi override fun hashCode(): Int = 31 * pyClass.hashCode() - override fun acceptTypeVisitor(visitor: PyTypeVisitor): T { + override fun acceptTypeVisitor(visitor: PyTypeVisitor): T? { if (visitor is PyTypeVisitorExt) { return visitor.visitPyLiteralType(this) } diff --git a/python/python-psi-impl/src/com/jetbrains/python/psi/types/PyNarrowedType.kt b/python/python-psi-impl/src/com/jetbrains/python/psi/types/PyNarrowedType.kt index 51dafba869eb..17700e2c2b88 100644 --- a/python/python-psi-impl/src/com/jetbrains/python/psi/types/PyNarrowedType.kt +++ b/python/python-psi-impl/src/com/jetbrains/python/psi/types/PyNarrowedType.kt @@ -88,7 +88,7 @@ class PyNarrowedType private constructor( } } - override fun acceptTypeVisitor(visitor: PyTypeVisitor): T { + override fun acceptTypeVisitor(visitor: PyTypeVisitor): T? { if (visitor is PyTypeVisitorExt) { return visitor.visitPyNarrowedType(this) } diff --git a/python/python-psi-impl/src/com/jetbrains/python/psi/types/PyTupleType.java b/python/python-psi-impl/src/com/jetbrains/python/psi/types/PyTupleType.java index 054129b46f5e..6225fb59e3e3 100644 --- a/python/python-psi-impl/src/com/jetbrains/python/psi/types/PyTupleType.java +++ b/python/python-psi-impl/src/com/jetbrains/python/psi/types/PyTupleType.java @@ -56,7 +56,7 @@ public class PyTupleType extends PyClassTypeImpl implements PyCollectionType { } private static @Nullable String getTypeName(@Nullable PyType type) { - return type == null ? PyNames.UNKNOWN_TYPE : type.getName(); + return type == null ? PyNames.ANY_TYPE : type.getName(); } @Override diff --git a/python/python-psi-impl/src/com/jetbrains/python/psi/types/PyTypeChecker.kt b/python/python-psi-impl/src/com/jetbrains/python/psi/types/PyTypeChecker.kt index edfe0048c5b8..a2f2847042e1 100644 --- a/python/python-psi-impl/src/com/jetbrains/python/psi/types/PyTypeChecker.kt +++ b/python/python-psi-impl/src/com/jetbrains/python/psi/types/PyTypeChecker.kt @@ -1269,12 +1269,12 @@ object PyTypeChecker { generics: GenericsImpl, ) { PyRecursiveTypeVisitor.traverse(type, context, object : PyTypeTraverser() { - override fun visitPyTypeVarType(typeVarType: PyTypeVarType): PyRecursiveTypeVisitor.Traversal { + override fun visitPyTypeVarType(typeVarType: PyTypeVarType): PyRecursiveTypeVisitor.Traversal? { generics.typeVars.add(typeVarType) return super.visitPyTypeVarType(typeVarType) } - override fun visitPyTypeVarTupleType(typeVarTupleType: PyTypeVarTupleType): PyRecursiveTypeVisitor.Traversal { + override fun visitPyTypeVarTupleType(typeVarTupleType: PyTypeVarTupleType): PyRecursiveTypeVisitor.Traversal? { generics.typeVarTuples.add(typeVarTupleType) return super.visitPyTypeVarTupleType(typeVarTupleType) } diff --git a/python/python-psi-impl/src/com/jetbrains/python/psi/types/PyTypeParser.java b/python/python-psi-impl/src/com/jetbrains/python/psi/types/PyTypeParser.java index dccdb7bbacfe..b003b16a5c64 100644 --- a/python/python-psi-impl/src/com/jetbrains/python/psi/types/PyTypeParser.java +++ b/python/python-psi-impl/src/com/jetbrains/python/psi/types/PyTypeParser.java @@ -546,7 +546,7 @@ public final class PyTypeParser { final String name = token.getText().toString(); final TextRange range = token.getRange(); - if (PyNames.UNKNOWN_TYPE.equals(name)) { + if (PyNames.ANY_TYPE.equals(name)) { return EMPTY_RESULT; } else if (PyNames.NONE.equals(name)) { diff --git a/python/python-psi-impl/src/com/jetbrains/python/psi/types/PyTypedDictType.kt b/python/python-psi-impl/src/com/jetbrains/python/psi/types/PyTypedDictType.kt index 02b8630e5929..5923d2f22375 100644 --- a/python/python-psi-impl/src/com/jetbrains/python/psi/types/PyTypedDictType.kt +++ b/python/python-psi-impl/src/com/jetbrains/python/psi/types/PyTypedDictType.kt @@ -318,7 +318,7 @@ class PyTypedDictType( val hasErrors: Boolean get() = valueTypeErrors.isNotEmpty() || missingKeys.isNotEmpty() || extraKeys.isNotEmpty() } - override fun acceptTypeVisitor(visitor: PyTypeVisitor): T { + override fun acceptTypeVisitor(visitor: PyTypeVisitor): T? { if (visitor is PyTypeVisitorExt) { return visitor.visitPyTypedDictType(this) } diff --git a/python/python-psi-impl/src/com/jetbrains/python/psi/types/PyTypingNewType.kt b/python/python-psi-impl/src/com/jetbrains/python/psi/types/PyTypingNewType.kt index d3e4bdd54bc9..6c71cc938398 100644 --- a/python/python-psi-impl/src/com/jetbrains/python/psi/types/PyTypingNewType.kt +++ b/python/python-psi-impl/src/com/jetbrains/python/psi/types/PyTypingNewType.kt @@ -96,7 +96,7 @@ class PyTypingNewType( return 31 * classType.hashCode() + name.hashCode() } - override fun acceptTypeVisitor(visitor: PyTypeVisitor): T { + override fun acceptTypeVisitor(visitor: PyTypeVisitor): T? { if (visitor is PyTypeVisitorExt) { return visitor.visitPyTypingNewType(this) } diff --git a/python/python-psi-impl/src/com/jetbrains/python/psi/types/PyUnsafeUnionType.kt b/python/python-psi-impl/src/com/jetbrains/python/psi/types/PyUnsafeUnionType.kt index cbc36b1d1527..54b717ca5837 100644 --- a/python/python-psi-impl/src/com/jetbrains/python/psi/types/PyUnsafeUnionType.kt +++ b/python/python-psi-impl/src/com/jetbrains/python/psi/types/PyUnsafeUnionType.kt @@ -91,7 +91,7 @@ class PyUnsafeUnionType private constructor(members: Collection) : PyTy } } - override fun acceptTypeVisitor(visitor: PyTypeVisitor): T { + override fun acceptTypeVisitor(visitor: PyTypeVisitor): T? { if (visitor is PyTypeVisitorExt) { return visitor.visitPyUnsafeUnionType(this) }