From 61b55f5a7172756fa36eff70c25849c68907abb9 Mon Sep 17 00:00:00 2001 From: "evgeny.bovykin" Date: Thu, 25 Sep 2025 13:48:01 +0200 Subject: [PATCH] PY-84289 Compute `getStringBasedType` with `doPreventingRecursion` to avoid repeating calculations GitOrigin-RevId: a548f05bea971f6b3e3acdb1f1bc7898d3411d5e --- .../typing/PyTypingTypeProvider.java | 6 ++-- .../a.py | 1 + .../main.py | 35 +++++++++++++++++++ .../PyTypeHintsInspectionTest.java | 13 +++++++ 4 files changed, 53 insertions(+), 2 deletions(-) create mode 100644 python/testData/inspections/PyTypeHintsInspection/ExponentialAnalysisTimeWhenMapLookupKeyEqualsVariableName/a.py create mode 100644 python/testData/inspections/PyTypeHintsInspection/ExponentialAnalysisTimeWhenMapLookupKeyEqualsVariableName/main.py diff --git a/python/python-psi-impl/src/com/jetbrains/python/codeInsight/typing/PyTypingTypeProvider.java b/python/python-psi-impl/src/com/jetbrains/python/codeInsight/typing/PyTypingTypeProvider.java index f72268c95d61..719b12704723 100644 --- a/python/python-psi-impl/src/com/jetbrains/python/codeInsight/typing/PyTypingTypeProvider.java +++ b/python/python-psi-impl/src/com/jetbrains/python/codeInsight/typing/PyTypingTypeProvider.java @@ -1322,8 +1322,10 @@ public final class PyTypingTypeProvider extends PyTypeProviderWithCustomContext< } private static @Nullable Ref getStringBasedType(@NotNull String contents, @NotNull PsiElement anchor, @NotNull Context context) { - final PyExpression expr = toExpression(contents, anchor); - return expr != null ? getType(expr, context) : null; + return doPreventingRecursion(Pair.create(anchor, contents), true, () -> { + final PyExpression expr = toExpression(contents, anchor); + return expr != null ? getType(expr, context) : null; + }); } private static @Nullable PyType getStringLiteralType(@NotNull PsiElement element, @NotNull Context context) { diff --git a/python/testData/inspections/PyTypeHintsInspection/ExponentialAnalysisTimeWhenMapLookupKeyEqualsVariableName/a.py b/python/testData/inspections/PyTypeHintsInspection/ExponentialAnalysisTimeWhenMapLookupKeyEqualsVariableName/a.py new file mode 100644 index 000000000000..013cf035b444 --- /dev/null +++ b/python/testData/inspections/PyTypeHintsInspection/ExponentialAnalysisTimeWhenMapLookupKeyEqualsVariableName/a.py @@ -0,0 +1 @@ +config_response = {} \ No newline at end of file diff --git a/python/testData/inspections/PyTypeHintsInspection/ExponentialAnalysisTimeWhenMapLookupKeyEqualsVariableName/main.py b/python/testData/inspections/PyTypeHintsInspection/ExponentialAnalysisTimeWhenMapLookupKeyEqualsVariableName/main.py new file mode 100644 index 000000000000..77d82cc1be43 --- /dev/null +++ b/python/testData/inspections/PyTypeHintsInspection/ExponentialAnalysisTimeWhenMapLookupKeyEqualsVariableName/main.py @@ -0,0 +1,35 @@ +from a import config_response +config_map = config_response["spec"]["config_map"] +kafka_consumer_key = config_map["component.job.static.job2"]["spec.plugin.kafka.connectivity.in"] + +config_response = {} +config_map = config_response["spec"]["config_map"] +kafka_consumer_key = config_map["component.job.static.job2"]["spec.plugin.kafka.connectivity.in"] + +config_response = {} +config_map = config_response["spec"]["config_map"] +kafka_consumer_key = config_map["component.job.static.job2"]["spec.plugin.kafka.connectivity.in"] + +config_response = {} +config_map = config_response["spec"]["config_map"] +kafka_consumer_key = config_map["component.job.static.job2"]["spec.plugin.kafka.connectivity.in"] + +config_response = {} +config_map = config_response["spec"]["config_map"] +kafka_consumer_key = config_map["component.job.static.job2"]["spec.plugin.kafka.connectivity.in"] + +config_response = {} +config_map = config_response["spec"]["config_map"] +kafka_consumer_key = config_map["component.job.static.job2"]["spec.plugin.kafka.connectivity.in"] + +config_response = {} +config_map = config_response["spec"]["config_map"] +kafka_consumer_key = config_map["component.job.static.job2"]["spec.plugin.kafka.connectivity.in"] + +config_response = {} +config_map = config_response["spec"]["config_map"] +kafka_consumer_key = config_map["component.job.static.job2"]["spec.plugin.kafka.connectivity.in"] + +config_response = {} +config_map = config_response["spec"]["config_map"] +kafka_consumer_key = config_map["component.job.static.job2"]["spec.plugin.kafka.connectivity.in"] \ No newline at end of file diff --git a/python/testSrc/com/jetbrains/python/inspections/PyTypeHintsInspectionTest.java b/python/testSrc/com/jetbrains/python/inspections/PyTypeHintsInspectionTest.java index 25ed7b4821ad..ac133d6545d5 100644 --- a/python/testSrc/com/jetbrains/python/inspections/PyTypeHintsInspectionTest.java +++ b/python/testSrc/com/jetbrains/python/inspections/PyTypeHintsInspectionTest.java @@ -3156,6 +3156,19 @@ public class PyTypeHintsInspectionTest extends PyInspectionTestCase { doMultiFileTest(); } + + // PY-84289 + public void testExponentialAnalysisTimeWhenMapLookupKeyEqualsVariableName() { + long before = System.currentTimeMillis(); + doMultiFileTest("main.py"); + long after = System.currentTimeMillis(); + long diff = after - before; + // junit3 doesn't support timeouts out of the box + if (diff > 5000) { + fail("Took too long to analyze main.py: " + diff + " ms"); + } + } + @NotNull @Override protected Class getInspectionClass() {