refactor python: j2k PyType

GitOrigin-RevId: 8ce307bf77950477e61ab508bd69905fc156d353
This commit is contained in:
Morgan Bartholomew
2026-02-15 12:37:37 +00:00
committed by intellij-monorepo-bot
parent 4670df9372
commit 4436f7d5aa
15 changed files with 96 additions and 112 deletions
@@ -8,7 +8,7 @@ import com.jetbrains.python.psi.PyExpression
import com.jetbrains.python.psi.resolve.PyResolveContext
import com.jetbrains.python.psi.resolve.RatedResolveResult
class PyNeverType private constructor(private val name: String) : PyType {
class PyNeverType private constructor(override val name: String) : PyType {
companion object {
@JvmField val NEVER: PyNeverType = PyNeverType("Never")
@JvmField val NO_RETURN: PyNeverType = PyNeverType("NoReturn")
@@ -17,8 +17,7 @@ class PyNeverType private constructor(private val name: String) : PyType {
fun PyType?.toNoReturnIfNeeded(): PyType? = if (this === NEVER) NO_RETURN else this
}
override fun getName(): String = name
override fun isBuiltin(): Boolean = true
override val isBuiltin: Boolean = true
override fun assertValid(message: String?) {}
override fun equals(other: Any?): Boolean = other is PyNeverType
@@ -33,11 +32,11 @@ class PyNeverType private constructor(private val name: String) : PyType {
override fun getCompletionVariants(
completionPrefix: String?,
location: PsiElement?,
context: ProcessingContext?,
location: PsiElement,
context: ProcessingContext,
): Array<Any> = emptyArray()
override fun <T> acceptTypeVisitor(visitor: PyTypeVisitor<T>): T {
override fun <T> acceptTypeVisitor(visitor: PyTypeVisitor<T>): T? {
return visitor.visitPyNeverType(this)
}
}
@@ -1,37 +1,30 @@
// 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;
package com.jetbrains.python.psi.types
import com.intellij.openapi.util.Key;
import com.intellij.openapi.util.NlsSafe;
import com.intellij.psi.PsiElement;
import com.intellij.util.ProcessingContext;
import com.jetbrains.python.psi.AccessDirection;
import com.jetbrains.python.psi.PyExpression;
import com.jetbrains.python.psi.PyQualifiedNameOwner;
import com.jetbrains.python.psi.resolve.PyResolveContext;
import com.jetbrains.python.psi.resolve.RatedResolveResult;
import org.jetbrains.annotations.ApiStatus;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.List;
import java.util.Set;
import com.intellij.openapi.util.Key
import com.intellij.openapi.util.NlsSafe
import com.intellij.psi.PsiElement
import com.intellij.util.ProcessingContext
import com.jetbrains.python.psi.AccessDirection
import com.jetbrains.python.psi.PyExpression
import com.jetbrains.python.psi.PyQualifiedNameOwner
import com.jetbrains.python.psi.resolve.PyResolveContext
import com.jetbrains.python.psi.resolve.RatedResolveResult
import org.jetbrains.annotations.ApiStatus
/**
* Represents a type of an expression.
*/
public interface PyType {
interface PyType {
/**
* Returns the declaration element that can be used to refer to this type inside type hints. Normally, it's a symbol
* that can be imported to mentioned the type in type annotations and comments anywhere else.
* <p>
* Typical examples are target expressions in LHS of assignments in {@code TypeVar} and named tuple definitions, as well as
*
*
* Typical examples are target expressions in LHS of assignments in `TypeVar` and named tuple definitions, as well as
* class definitions themselves for plain class and generic types.
*/
default @Nullable PyQualifiedNameOwner getDeclarationElement() {
return null;
}
val declarationElement: PyQualifiedNameOwner? get() = null
/**
* Resolves an attribute of type.
@@ -42,16 +35,17 @@ public interface PyType {
* or an empty list if name is not found but other contexts are worth looking at,
* or a list of elements that define the name, a la multiResolve().
*/
@Nullable
List<? extends RatedResolveResult> resolveMember(@NotNull String name,
final @Nullable PyExpression location,
final @NotNull AccessDirection direction,
final @NotNull PyResolveContext resolveContext);
fun resolveMember(
name: String,
location: PyExpression?,
direction: AccessDirection,
resolveContext: PyResolveContext,
): List<@JvmWildcard RatedResolveResult>?
@ApiStatus.Experimental
default @NotNull List<@NotNull PyTypeMember> getAllMembers(final @NotNull PyResolveContext resolveContext) {
return List.of();
fun getAllMembers(resolveContext: PyResolveContext): List<@JvmWildcard PyTypeMember> {
return emptyList()
}
/**
@@ -59,8 +53,8 @@ public interface PyType {
* There can be several members with the same name (for example, methods with @overload)
*/
@ApiStatus.Experimental
default @NotNull List<@NotNull PyTypeMember> findMember(@NotNull String name, final @NotNull PyResolveContext resolveContext) {
return List.of();
fun findMember(name: String, resolveContext: PyResolveContext): List<@JvmWildcard PyTypeMember> {
return emptyList()
}
/**
@@ -68,37 +62,34 @@ public interface PyType {
*
* @param location the reference on which the completion was invoked
* @param context to share state between nested invocations
* @return completion variants good for {@link com.intellij.psi.PsiReference#getVariants} return value.
* @return completion variants good for [com.intellij.psi.PsiReference.getVariants] return value.
*/
Object[] getCompletionVariants(String completionPrefix, PsiElement location, ProcessingContext context);
fun getCompletionVariants(completionPrefix: String?, location: PsiElement, context: ProcessingContext): Array<out @JvmWildcard Any>
/**
* Context key for access to a set of names already found by variant search.
*/
Key<Set<String>> CTX_NAMES = new Key<>("Completion variants names");
/**
* TODO rename it to something like getPresentableName(), because it's not clear that these names are actually visible to end-user
*
* @return name of the type
*/
@Nullable
@NlsSafe
String getName();
@get:NlsSafe
val name: @NlsSafe String?
/**
* @return true if the type is a known built-in type.
*/
boolean isBuiltin();
val isBuiltin: Boolean
void assertValid(String message);
fun assertValid(message: String?)
/**
* For nullable {@code PyType} instance use {@link PyTypeVisitor#visit(PyType, PyTypeVisitor)}
* to visit {@code null} values with {@link PyTypeVisitor#visitUnknownType()}.
* For nullable `PyType` instance use [PyTypeVisitor.visit]
* to visit `null` values with [PyTypeVisitor.visitUnknownType].
*/
@ApiStatus.Experimental
default <T> T acceptTypeVisitor(@NotNull PyTypeVisitor<T> visitor) {
return visitor.visitPyType(this);
fun <T> acceptTypeVisitor(visitor: PyTypeVisitor<T>): T? {
return visitor.visitPyType(this)
}
companion object {
/**
* Context key for access to a set of names already found by variant search.
*/
@JvmField
val CTX_NAMES: Key<MutableSet<String>> = Key("Completion variants names")
}
}
@@ -302,7 +302,11 @@ sealed class TypeEvalContext(
return mutableListOf()
}
override fun getCompletionVariants(completionPrefix: String?, location: PsiElement?, context: ProcessingContext?): Array<Any?>? {
override fun getCompletionVariants(
completionPrefix: String?,
location: PsiElement,
context: ProcessingContext,
): Array<Any> {
return ArrayUtil.EMPTY_OBJECT_ARRAY
}
@@ -326,9 +326,9 @@ public final class PyCustomType implements PyClassLikeType {
}
@Override
public @NotNull List<@NotNull PyTypeMember> findMember(@NotNull String name, @NotNull PyResolveContext resolveContext) {
public @NotNull List<? extends @NotNull PyTypeMember> findMember(@NotNull String name, @NotNull PyResolveContext resolveContext) {
for (PyClassLikeType type : myTypesToMimic) {
List<PyTypeMember> members = type.findMember(name, resolveContext);
var members = type.findMember(name, resolveContext);
if (!members.isEmpty()) {
return members;
}
@@ -27,7 +27,7 @@ fun PyClassLikeType.isProtocol(context: TypeEvalContext): Boolean = containsProt
fun PyClass.isProtocol(context: TypeEvalContext): Boolean = containsProtocol(getSuperClassTypes(context))
fun PyClassType.isRuntimeCheckable(context: TypeEvalContext): Boolean =
fun PyClassType.isRuntimeCheckable(context: TypeEvalContext): Boolean =
PyKnownDecoratorUtil.getKnownDecorators(pyClass, context).any {
it in listOf(TYPING_RUNTIME_CHECKABLE, TYPING_RUNTIME_CHECKABLE_EXT, TYPING_RUNTIME, TYPING_RUNTIME_EXT)
}
@@ -45,12 +45,11 @@ fun inspectProtocolSubclass(protocol: PyClassType, subclass: PyClassType, contex
val resolveContext = PyResolveContext.defaultContext(context)
val result = mutableListOf<Pair<PyTypeMember, List<PyTypeMember>>>()
val protocolMembers = protocol.toInstance().getAllMembers(resolveContext)
val superClassesMembers = protocol.toInstance().getSuperClassTypes(context)
.filterNotNull()
.filter { it.isProtocol(context) }
.flatMap { it.toInstance().getAllMembers(resolveContext).asIterable() }
protocolMembers.addAll(superClassesMembers)
val protocolMembers = protocol.toInstance().getAllMembers(resolveContext) + superClassesMembers
for (protocolMember in protocolMembers) {
val protocolElement = protocolMember.element ?: continue
@@ -174,7 +174,7 @@ public class PyClassPatternImpl extends PyElementImpl implements PyClassPattern,
@Nullable
static Ref<PyType> getMemberType(@NotNull PyType type, @NotNull String name, @NotNull TypeEvalContext context) {
final PyResolveContext resolveContext = PyResolveContext.defaultContext(context);
List<PyTypeMember> members = type.findMember(name, resolveContext);
var members = type.findMember(name, resolveContext);
if (members.isEmpty()) return null;
return Ref.create(PyUnionType.union(ContainerUtil.map(members, PyTypeMember::getType)));
}
@@ -4,9 +4,9 @@ package com.jetbrains.python.psi.types
* Type of typing.Concatenate to store corresponding first type and parameter specification
*/
class PyConcatenateType(val firstTypes: List<PyType?>, val paramSpec: PyParamSpecType?) : PyCallableParameterVariadicType {
override fun getName(): String = "Concatenate(${firstTypes.joinToString { it?.name ?: "Any" }}, ${paramSpec?.name ?: "..."})"
override val name: String = "Concatenate(${firstTypes.joinToString { it?.name ?: "Any" }}, ${paramSpec?.name ?: "..."})"
override fun <T : Any?> acceptTypeVisitor(visitor: PyTypeVisitor<T?>): T? {
override fun <T> acceptTypeVisitor(visitor: PyTypeVisitor<T>): T {
if (visitor is PyTypeVisitorExt) {
return visitor.visitPyConcatenateType(this)
}
@@ -20,8 +20,8 @@ class PyIntersectionType private constructor(members: Collection<PyType?>) : PyT
location: PyExpression?,
direction: AccessDirection,
resolveContext: PyResolveContext,
): List<RatedResolveResult?>? {
val ret = SmartList<RatedResolveResult?>()
): List<RatedResolveResult>? {
val ret = SmartList<RatedResolveResult>()
var allNulls = true
for (member in members) {
if (member != null) {
@@ -35,19 +35,15 @@ class PyIntersectionType private constructor(members: Collection<PyType?>) : PyT
return if (allNulls) null else ret
}
override fun getCompletionVariants(completionPrefix: String?, location: PsiElement?, context: ProcessingContext?): Array<out Any> {
override fun getCompletionVariants(completionPrefix: String?, location: PsiElement, context: ProcessingContext): Array<Any> {
return members.flatMap { it?.getCompletionVariants(completionPrefix, location, context)?.asList() ?: emptyList() }
.distinct()
.toTypedArray()
}
override fun getName(): @NlsSafe String {
return members.joinToString(separator = " & ") { it?.name ?: "Any" }
}
override val name: @NlsSafe String = members.joinToString(separator = " & ") { it?.name ?: "Any" }
override fun isBuiltin(): Boolean {
return members.all { it != null && it.isBuiltin }
}
override val isBuiltin: Boolean = members.all { it != null && it.isBuiltin }
override fun assertValid(message: String?) {
for (member in members) {
@@ -15,9 +15,7 @@ import java.util.Objects
*/
class PyLiteralStringType private constructor(val cls: PyClass) : PyClassTypeImpl(cls, false) {
override fun getName(): String {
return "LiteralString"
}
override val name: String = "LiteralString"
override fun toString(): String {
return "PyLiteralStringType"
@@ -35,7 +33,7 @@ class PyLiteralStringType private constructor(val cls: PyClass) : PyClassTypeImp
return Objects.hash(super.hashCode(), cls)
}
override fun <T : Any?> acceptTypeVisitor(visitor: PyTypeVisitor<T?>): T? {
override fun <T> acceptTypeVisitor(visitor: PyTypeVisitor<T>): T {
if (visitor is PyTypeVisitorExt) {
return visitor.visitPyLiteralStringType(this)
}
@@ -42,7 +42,7 @@ import org.jetbrains.annotations.ApiStatus
*/
class PyLiteralType private constructor(cls: PyClass, val expression: PyExpression) : PyClassTypeImpl(cls, false) {
override fun getName(): String = "Literal[${expression.text}]"
override val name: String = "Literal[${expression.text}]"
override fun toString(): String = "PyLiteralType: ${expression.text}"
@@ -52,7 +52,7 @@ class PyLiteralType private constructor(cls: PyClass, val expression: PyExpressi
override fun hashCode(): Int = 31 * pyClass.hashCode()
override fun <T : Any?> acceptTypeVisitor(visitor: PyTypeVisitor<T?>): T? {
override fun <T> acceptTypeVisitor(visitor: PyTypeVisitor<T>): T {
if (visitor is PyTypeVisitorExt) {
return visitor.visitPyLiteralType(this)
}
@@ -88,7 +88,7 @@ class PyNarrowedType private constructor(
}
}
override fun <T : Any?> acceptTypeVisitor(visitor: PyTypeVisitor<T?>): T? {
override fun <T> acceptTypeVisitor(visitor: PyTypeVisitor<T>): T {
if (visitor is PyTypeVisitorExt) {
return visitor.visitPyNarrowedType(this)
}
@@ -131,12 +131,12 @@ public final class PySelfType implements PyTypeParameterType, PyClassType {
}
@Override
public @NotNull List<@NotNull PyTypeMember> getAllMembers(@NotNull PyResolveContext resolveContext) {
public @NotNull List<? extends @NotNull PyTypeMember> getAllMembers(@NotNull PyResolveContext resolveContext) {
return myScopeClassType.getAllMembers(resolveContext);
}
@Override
public @NotNull List<@NotNull PyTypeMember> findMember(@NotNull String name, @NotNull PyResolveContext resolveContext) {
public @NotNull List<? extends @NotNull PyTypeMember> findMember(@NotNull String name, @NotNull PyResolveContext resolveContext) {
return myScopeClassType.findMember(name, resolveContext);
}
@@ -19,7 +19,7 @@ import org.jetbrains.annotations.ApiStatus
import java.util.Objects
class PyTypedDictType(
private val name: String,
override val name: String,
val fields: Map<String, FieldTypeAndTotality>,
private val dictClass: PyClass,
isDefinition: Boolean,
@@ -47,9 +47,7 @@ class PyTypedDictType(
PyTypedDictType(name, fields, dictClass, true, declaration)
}
override fun getName(): String = name
override fun isBuiltin(): Boolean = false
override val isBuiltin: Boolean = false
override fun isCallable(): Boolean = isDefinition
@@ -87,7 +85,7 @@ class PyTypedDictType(
return Objects.hash(super.hashCode(), declaration)
}
override fun getDeclarationElement(): PyQualifiedNameOwner = declaration
override val declarationElement: PyQualifiedNameOwner = declaration
/**
* @isRequired is true - if value type is Required, false - if it is NotRequired, and null if it does not have any type specification
@@ -320,7 +318,7 @@ class PyTypedDictType(
val hasErrors: Boolean get() = valueTypeErrors.isNotEmpty() || missingKeys.isNotEmpty() || extraKeys.isNotEmpty()
}
override fun <T> acceptTypeVisitor(visitor: PyTypeVisitor<T?>): T? {
override fun <T> acceptTypeVisitor(visitor: PyTypeVisitor<T>): T {
if (visitor is PyTypeVisitorExt) {
return visitor.visitPyTypedDictType(this)
}
@@ -14,12 +14,10 @@ import org.jetbrains.annotations.ApiStatus
@ApiStatus.Internal
class PyTypingNewType(
val classType: PyClassType,
private val name: String,
override val name: String,
private val declaration: PyTargetExpression?,
) : PyClassType by classType {
override fun getName(): String = name
override fun getCallType(context: TypeEvalContext, callSite: PyCallSiteExpression): PyType? {
return PyTypingNewType(classType.toInstance(), name, declaration)
}
@@ -32,7 +30,7 @@ class PyTypingNewType(
return if (isDefinition) PyTypingNewType(classType.toInstance(), name, declaration) else this
}
override fun isBuiltin(): Boolean = false
override val isBuiltin: Boolean = false
override fun isCallable(): Boolean = classType.isCallable || isDefinition
@@ -66,9 +64,9 @@ class PyTypingNewType(
}
override fun resolveMember(name: String, location: PyExpression?, direction: AccessDirection, resolveContext: PyResolveContext)
: MutableList<out RatedResolveResult>? {
: List<RatedResolveResult>? {
return if (name == PyNames.CLASS_GETITEM) {
mutableListOf()
listOf()
}
else {
classType.resolveMember(name, location, direction, resolveContext)
@@ -79,7 +77,7 @@ class PyTypingNewType(
return listOf(classType) + classType.getAncestorTypes(context)
}
override fun getDeclarationElement(): PyQualifiedNameOwner? = declaration ?: classType.declarationElement
override val declarationElement: PyQualifiedNameOwner? = declaration ?: classType.declarationElement
override fun equals(other: Any?): Boolean {
if (this === other) return true
@@ -98,7 +96,7 @@ class PyTypingNewType(
return 31 * classType.hashCode() + name.hashCode()
}
override fun <T : Any?> acceptTypeVisitor(visitor: PyTypeVisitor<T?>): T? {
override fun <T> acceptTypeVisitor(visitor: PyTypeVisitor<T>): T {
if (visitor is PyTypeVisitorExt) {
return visitor.visitPyTypingNewType(this)
}
@@ -111,7 +109,7 @@ class PyTypingNewType(
* For type annotations {@link com.jetbrains.python.psi.types.PyTypingNewType} is used.
*/
@ApiStatus.Internal
class PyTypingNewTypeFactoryType(private val type: PyTypingNewType)
class PyTypingNewTypeFactoryType(type: PyTypingNewType)
: PyCallableTypeImpl(listOf(PyCallableParameterImpl.nonPsi(type.classType.toInstance())), type.toInstance()) {
override fun getName(): String = type.name
override val name: String = type.name
}
@@ -55,12 +55,17 @@ class PyUnsafeUnionType private constructor(members: Collection<PyType?>) : PyTy
val members: Set<PyType?> = LinkedHashSet(members)
get() = Collections.unmodifiableSet<PyType?>(field)
override fun resolveMember(name: String, location: PyExpression?, direction: AccessDirection, resolveContext: PyResolveContext): List<RatedResolveResult?>? {
val ret = SmartList<RatedResolveResult?>()
override fun resolveMember(
name: String,
location: PyExpression?,
direction: AccessDirection,
resolveContext: PyResolveContext,
): List<RatedResolveResult>? {
val ret = SmartList<RatedResolveResult>()
var allNulls = true
for (member in members) {
if (member != null) {
val result: MutableList<out RatedResolveResult?>? = member.resolveMember(name, location, direction, resolveContext)
val result = member.resolveMember(name, location, direction, resolveContext)
if (result != null) {
allNulls = false
ret.addAll(result)
@@ -70,19 +75,15 @@ class PyUnsafeUnionType private constructor(members: Collection<PyType?>) : PyTy
return if (allNulls) null else ret
}
override fun getCompletionVariants(completionPrefix: String?, location: PsiElement?, context: ProcessingContext?): Array<out Any> {
override fun getCompletionVariants(completionPrefix: String?, location: PsiElement, context: ProcessingContext): Array<Any> {
return members.flatMap { it?.getCompletionVariants(completionPrefix, location, context)?.asList() ?: emptyList() }
.distinct()
.toTypedArray()
}
override fun getName(): @NlsSafe String? {
return members.joinToString(separator = " | ") { it?.name ?: "Any" }
}
override val name: @NlsSafe String = members.joinToString(separator = " | ") { it?.name ?: "Any" }
override fun isBuiltin(): Boolean {
return members.all { it != null && it.isBuiltin }
}
override val isBuiltin: Boolean = members.all { it != null && it.isBuiltin }
override fun assertValid(message: String?) {
for (member in members) {