[PY-82607] introduce a caching for all computations in PyTypingTypeProvider

GitOrigin-RevId: fb694274af15e64738054c03b0360f5aff4c513a
This commit is contained in:
Vladimir.Koshelev
2025-08-01 15:43:57 +02:00
committed by intellij-monorepo-bot
parent 62f37bb800
commit 57b05ad202
4 changed files with 39 additions and 3 deletions

View File

@@ -49,6 +49,7 @@ jvm_library(
"//python/python-parser:parser",
"//python/python-syntax-core:syntax-core",
"//python/impl.helperLocator:community-helpersLocator",
"@lib//:hash4j",
],
exports = ["//python/python-syntax-core:syntax-core"],
runtime_deps = [":psi-impl_resources"]

View File

@@ -45,5 +45,6 @@
<orderEntry type="module" module-name="intellij.python.parser" />
<orderEntry type="module" module-name="intellij.python.syntax.core" exported="" />
<orderEntry type="module" module-name="intellij.python.community.helpersLocator" />
<orderEntry type="library" name="hash4j" level="project" />
</component>
</module>

View File

@@ -1,6 +1,7 @@
// Copyright 2000-2017 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.
package com.jetbrains.python.codeInsight.typing;
import com.dynatrace.hash4j.hashing.HashValue128;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.ImmutableSet;
import com.intellij.openapi.util.*;
@@ -48,8 +49,11 @@ import org.jetbrains.annotations.Nullable;
import java.util.*;
import java.util.function.Function;
import java.util.regex.Pattern;
import java.util.stream.Collectors;
import java.util.stream.Stream;
import static com.dynatrace.hash4j.hashing.Hashing.murmur3_128;
import static com.dynatrace.hash4j.hashing.Hashing.xxh3_128;
import static com.intellij.openapi.util.RecursionManager.doPreventingRecursion;
import static com.jetbrains.python.psi.PyKnownDecorator.TYPING_FINAL;
import static com.jetbrains.python.psi.PyKnownDecorator.TYPING_FINAL_EXT;
@@ -777,9 +781,16 @@ public final class PyTypingTypeProvider extends PyTypeProviderWithCustomContext<
}
private static @Nullable Ref<PyType> getType(@NotNull PyExpression expression, @NotNull Context context) {
PyType type = context.getKnownType(expression);
if (type != null) {
return Ref.create(type);
}
for (Pair<PyQualifiedNameOwner, PsiElement> pair : tryResolvingWithAliases(expression, context.getTypeContext())) {
final Ref<PyType> typeRef = getTypeForResolvedElement(expression, pair.getFirst(), pair.getSecond(), context);
if (typeRef != null) {
if (typeRef.get() != null) {
context.assumeType(expression, typeRef.get());
}
return typeRef;
}
}
@@ -1124,8 +1135,9 @@ public final class PyTypingTypeProvider extends PyTypeProviderWithCustomContext<
// We need this check for the type argument list because getParameterizedType() relies on getClassType() for
// getting the type corresponding to the subscription expression operand.
if (classLikeType instanceof PyClassType classType &&
isGeneric(classLikeType, typeContext) &&
!(typeHint.getParent() instanceof PySubscriptionExpression se && typeHint.equals(se.getOperand()))) {
!(getStubRetainedTypeHintContext(typeHint) instanceof PyClass) &&
!(typeHint.getParent() instanceof PySubscriptionExpression se && typeHint.equals(se.getOperand())) &&
isGeneric(classType, context.myContext)) {
PyCollectionType parameterized = parameterizeClassDefaultAware(classType.getPyClass(), List.of(), context);
if (parameterized != null) {
return Ref.create(parameterized.toInstance());
@@ -2315,6 +2327,22 @@ public final class PyTypingTypeProvider extends PyTypeProviderWithCustomContext<
return myTypeAliasStack;
}
public @Nullable PyType getKnownType(@NotNull PyExpression expression) {
//noinspection SuspiciousMethodCalls
return myContext.getContextTypeCache().get(new Pair<>(expression, getContextStrongHashValue()));
}
public void assumeType(@NotNull PyExpression expression, @NotNull PyType type) {
myContext.getContextTypeCache().put(new Pair<>(expression, getContextStrongHashValue()), type);
}
private @NotNull HashValue128 getContextStrongHashValue() {
var result = xxh3_128().hashCharsTo128Bits(Stream.concat(Stream.of(myComputeTypeParameterScope ? "1" : "0"),
myTypeAliasStack.stream().map(it -> it.getQualifiedName()))
.collect(Collectors.joining("#")));
return result;
}
@Override
public boolean equals(Object o) {
if (this == o) return true;