From fb5a3a200aef088940057acc5f3a470c06716d92 Mon Sep 17 00:00:00 2001 From: Mikhail Golubev Date: Mon, 27 Feb 2023 09:42:24 +0200 Subject: [PATCH] PY-59241 Extract PyTypeVarType and PyTypeParameter interfaces, deprecate PyGenericType Existing usages haven't been updated yet not to hinder the upcoming integration of TypeVarTuple (PY-53105) and LiteralString (PY-58857) support. GitOrigin-RevId: 16aafd07edfdc98dee0240dce7cd3383b5eb0b00 --- .../python/psi/types/PyTypeParameterType.java | 82 +++++++++++++++++++ .../python/psi/types/PyTypeVarType.java | 32 ++++++++ .../python/psi/types/PyGenericType.java | 15 ++-- .../python/psi/types/PyTypeVarTypeImpl.java | 14 ++++ 4 files changed, 138 insertions(+), 5 deletions(-) create mode 100644 python/python-psi-api/src/com/jetbrains/python/psi/types/PyTypeParameterType.java create mode 100644 python/python-psi-api/src/com/jetbrains/python/psi/types/PyTypeVarType.java create mode 100644 python/python-psi-impl/src/com/jetbrains/python/psi/types/PyTypeVarTypeImpl.java diff --git a/python/python-psi-api/src/com/jetbrains/python/psi/types/PyTypeParameterType.java b/python/python-psi-api/src/com/jetbrains/python/psi/types/PyTypeParameterType.java new file mode 100644 index 000000000000..9bde566a1aa5 --- /dev/null +++ b/python/python-psi-api/src/com/jetbrains/python/psi/types/PyTypeParameterType.java @@ -0,0 +1,82 @@ +// Copyright 2000-2023 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.jetbrains.python.psi.PyQualifiedNameOwner; +import com.jetbrains.python.psi.PyTargetExpression; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +/** + * Represents an entity in the type system that can be used to parameterize other types making them "generic". + * A typical example of a type parameter is PEP 484 {@code TypeVar} like "T" in the following: + *
{@code
+ *
+ * from typing import TypeVar
+ *
+ * T = TypeVar('T')
+ *
+ * def identity(x: T) -> T:
+ *     return x
+ *
+ * }
+ *

+ * making the type of "identity" parameterized by "T". + * Other examples of type parameters are {@code ParamSpec} and {@code TypeVarTuple}. + *

+ * Declarations using "magical" factories from typing, such as {@code T = TypeVar("T")}, are the most common source of type parameters. + * The corresponding {@link PyTargetExpression} can be retrieved with {@link #getDeclarationElement()}. + * However, they can also come from docstrings or be created dynamically by type providers and, hence, not have a physical declaration. + */ +public interface PyTypeParameterType extends PyType { + /** + * Returns the name of this type parameter, such as "T" for the {@link PyTypeVarType} instance introduced by {@code T = TypeVar("T")}. + */ + @Override + @NotNull String getName(); + + /** + * Normally, a type parameter must be bound to a specific declaration to avoid collisions with other parameters with the same name. + *

+ * For instance, here + *

{@code
+   * from typing import TypeVar
+   *
+   * T = TypeVar('T')
+   *
+   * def min(xs: list[T]) -> T | None:
+   *     ...
+   * }
+ *

+ * "T" is bound to the "min" function definition, and here + *

{@code
+   * from typing import Generic, TypeVar
+   *
+   * T = TypeVar('T')
+   *
+   * class ListOps(Generic[T]):
+   *     def min(self, xs: list[T]) -> T | None:
+   *         ...
+   * }
+ *

+ * it is bound to the enclosing class "ListOps". + *

+ * In the following, type parameters "T" of "f" and "g" are unrelated to each other, despite sharing the same name: + *

{@code
+   * from typing import TypeVar
+   *
+   * T = TypeVar('T')
+   *
+   * def f(x: T) -> T:
+   *     return g(x)
+   *
+   * def g(x: T) -> T:
+   *     return f(x)
+   * }
+ *

+ * See the section Scoping rules for type variables + * in PEP 484. + *

+ * Results of {@code getScopeOwner()} and {@link #getName()} constitute unique "coordinates" of a given type parameter. + */ + @Nullable PyQualifiedNameOwner getScopeOwner(); +} diff --git a/python/python-psi-api/src/com/jetbrains/python/psi/types/PyTypeVarType.java b/python/python-psi-api/src/com/jetbrains/python/psi/types/PyTypeVarType.java new file mode 100644 index 000000000000..b7d90375e5b6 --- /dev/null +++ b/python/python-psi-api/src/com/jetbrains/python/psi/types/PyTypeVarType.java @@ -0,0 +1,32 @@ +// Copyright 2000-2023 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.Nullable; + +/** + * Represents a type parameter that should be substituted with a single type during the unification process. + * Normally, it's declared using {@code TypeVar} function from the "typing" module, as in + *

{@code
+ * from typing import TypeVar
+ *
+ * T = TypeVar('T')
+ * }
+ * but can also come from other sources, such as docstrings. + */ +public interface PyTypeVarType extends PyTypeParameterType, PyInstantiableType { + /** + * Returns the upper bound for this type parameter if it was specified. + *

+ * For instance, for the following declaration + *

{@code
+   * from typing import TypeVar
+   *
+   * T = TypeVar('T', bound=list[int])
+   * }
+ * this method should return the type corresponding to the type hint {@code list[int]}. + *

+ * See the section Type variables with an upper bound + * in PEP 484. + */ + @Nullable PyType getBound(); +} diff --git a/python/python-psi-impl/src/com/jetbrains/python/psi/types/PyGenericType.java b/python/python-psi-impl/src/com/jetbrains/python/psi/types/PyGenericType.java index 676e5d8fefeb..00e0828648ca 100644 --- a/python/python-psi-impl/src/com/jetbrains/python/psi/types/PyGenericType.java +++ b/python/python-psi-impl/src/com/jetbrains/python/psi/types/PyGenericType.java @@ -17,7 +17,12 @@ import org.jetbrains.annotations.Nullable; import java.util.List; import java.util.Objects; -public class PyGenericType implements PyType, PyInstantiableType { +/** + * @deprecated Use {@link PyTypeVarType} and {@link PyTypeVarTypeImpl} instead. + * See PY-59241 for the reasoning and transition plan. + */ +@Deprecated +public class PyGenericType implements PyTypeVarType { @NotNull private final String myName; @Nullable private final PyType myBound; private final boolean myIsDefinition; @@ -126,8 +131,8 @@ public class PyGenericType implements PyType, PyInstantiableType return "PyGenericType: " + (scopeName != null ? scopeName + ":" : "") + getName(); } - @Nullable - public PyType getBound() { + @Override + public @Nullable PyType getBound() { return myBound; } @@ -136,8 +141,8 @@ public class PyGenericType implements PyType, PyInstantiableType return myIsDefinition; } - @Nullable - public PyQualifiedNameOwner getScopeOwner() { + @Override + public @Nullable PyQualifiedNameOwner getScopeOwner() { return myScopeOwner; } diff --git a/python/python-psi-impl/src/com/jetbrains/python/psi/types/PyTypeVarTypeImpl.java b/python/python-psi-impl/src/com/jetbrains/python/psi/types/PyTypeVarTypeImpl.java new file mode 100644 index 000000000000..6bac87fe7c43 --- /dev/null +++ b/python/python-psi-impl/src/com/jetbrains/python/psi/types/PyTypeVarTypeImpl.java @@ -0,0 +1,14 @@ +package com.jetbrains.python.psi.types; + +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +public final class PyTypeVarTypeImpl extends PyGenericType { + public PyTypeVarTypeImpl(@NotNull String name, @Nullable PyType bound) { + super(name, bound); + } + + public PyTypeVarTypeImpl(@NotNull String name, @Nullable PyType bound, boolean isDefinition) { + super(name, bound, isDefinition); + } +}