From e9d4a103a0b2a48ebb0404bd89ce883e344a9492 Mon Sep 17 00:00:00 2001 From: "Aleksandr.Govenko" Date: Mon, 8 Sep 2025 03:24:28 +0200 Subject: [PATCH] PY-83900 Inspect Code consumes 100% CPU and takes unusually long on specific simple file GitOrigin-RevId: 81973d162072c94b4d7201dffc68d43669f449a3 --- .../jetbrains/python/psi/PyCaseClause.java | 2 +- .../jetbrains/python/psi/PyClassPattern.java | 2 +- .../jetbrains/python/psi/PyMappingPattern.kt | 2 +- .../src/com/jetbrains/python/psi/PyPattern.kt | 36 --------------- .../jetbrains/python/psi/PySequencePattern.kt | 2 +- .../python/psi/impl/PyCaptureContext.kt | 45 +++++++++++++++++++ .../python/psi/impl/PyCapturePatternImpl.java | 1 - .../python/psi/impl/PyCaseClauseImpl.kt | 2 +- .../python/psi/impl/PyClassPatternImpl.java | 2 +- .../python/psi/impl/PyMappingPatternImpl.kt | 7 ++- .../python/psi/impl/PySequencePatternImpl.kt | 2 +- .../psi/impl/PyWildcardPatternImpl.java | 1 - 12 files changed, 55 insertions(+), 49 deletions(-) create mode 100644 python/python-psi-impl/src/com/jetbrains/python/psi/impl/PyCaptureContext.kt diff --git a/python/python-psi-api/src/com/jetbrains/python/psi/PyCaseClause.java b/python/python-psi-api/src/com/jetbrains/python/psi/PyCaseClause.java index 2be5be9eef60..036170f28dee 100644 --- a/python/python-psi-api/src/com/jetbrains/python/psi/PyCaseClause.java +++ b/python/python-psi-api/src/com/jetbrains/python/psi/PyCaseClause.java @@ -4,7 +4,7 @@ package com.jetbrains.python.psi; import com.jetbrains.python.ast.PyAstCaseClause; import org.jetbrains.annotations.Nullable; -public interface PyCaseClause extends PyAstCaseClause, PyStatementPart, PyCaptureContext { +public interface PyCaseClause extends PyAstCaseClause, PyStatementPart { @Override default @Nullable PyPattern getPattern() { return (PyPattern)PyAstCaseClause.super.getPattern(); diff --git a/python/python-psi-api/src/com/jetbrains/python/psi/PyClassPattern.java b/python/python-psi-api/src/com/jetbrains/python/psi/PyClassPattern.java index 5f14c641b42d..22d34954c6c0 100644 --- a/python/python-psi-api/src/com/jetbrains/python/psi/PyClassPattern.java +++ b/python/python-psi-api/src/com/jetbrains/python/psi/PyClassPattern.java @@ -6,7 +6,7 @@ import org.jetbrains.annotations.NotNull; import java.util.Set; -public interface PyClassPattern extends PyAstClassPattern, PyPattern, PyCaptureContext { +public interface PyClassPattern extends PyAstClassPattern, PyPattern { Set SPECIAL_BUILTINS = Set.of( "bool", "bytearray", "bytes", "dict", "float", "frozenset", "int", "list", "set", "str", "tuple"); diff --git a/python/python-psi-api/src/com/jetbrains/python/psi/PyMappingPattern.kt b/python/python-psi-api/src/com/jetbrains/python/psi/PyMappingPattern.kt index 4e8225f14703..dc4294cb1ef8 100644 --- a/python/python-psi-api/src/com/jetbrains/python/psi/PyMappingPattern.kt +++ b/python/python-psi-api/src/com/jetbrains/python/psi/PyMappingPattern.kt @@ -3,4 +3,4 @@ package com.jetbrains.python.psi import com.jetbrains.python.ast.PyAstMappingPattern -interface PyMappingPattern : PyAstMappingPattern, PyPattern, PyCaptureContext +interface PyMappingPattern : PyAstMappingPattern, PyPattern diff --git a/python/python-psi-api/src/com/jetbrains/python/psi/PyPattern.kt b/python/python-psi-api/src/com/jetbrains/python/psi/PyPattern.kt index a5a1d4a8f328..3b00444d66bd 100644 --- a/python/python-psi-api/src/com/jetbrains/python/psi/PyPattern.kt +++ b/python/python-psi-api/src/com/jetbrains/python/psi/PyPattern.kt @@ -1,7 +1,6 @@ // Copyright 2000-2021 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.psi -import com.intellij.psi.util.findParentOfType import com.jetbrains.python.ast.PyAstPattern import com.jetbrains.python.psi.types.PyType import com.jetbrains.python.psi.types.TypeEvalContext @@ -40,38 +39,3 @@ interface PyPattern : PyAstPattern, PyTypedElement { return true } } - -interface PyCaptureContext : PyElement { - fun getCaptureTypeForChild(pattern: PyPattern, context: TypeEvalContext): PyType? - - companion object { - /** - * Determines what type this pattern would have if it was a capture pattern (like a bare name or _). - * - * In pattern matching, a capture pattern takes on the type of the entire matched expression, - * regardless of any specific pattern constraints. - * - * For example: - * ```python - * x: int | str - * match x: - * case a: # This is a capture pattern - * # Here 'a' has type int | str - * case str(): # This is a class pattern - * # Capture type: int | str (same as what 'case a:' would get) - * # Regular getType: str - * - * y: int - * match y: - * case str() as a: - * # Capture type: int (same as what 'case a:' would get) - * # Regular getType: intersect(int, str) (just 'str' for now) - * ``` - * @see PyPattern#getType(TypeEvalContext, TypeEvalContext.Key) - */ - @JvmStatic - fun getCaptureType(pattern: PyPattern, context: TypeEvalContext): PyType? { - return pattern.findParentOfType()?.getCaptureTypeForChild(pattern, context) - } - } -} diff --git a/python/python-psi-api/src/com/jetbrains/python/psi/PySequencePattern.kt b/python/python-psi-api/src/com/jetbrains/python/psi/PySequencePattern.kt index 4cff9dca4527..b5b0420dd514 100644 --- a/python/python-psi-api/src/com/jetbrains/python/psi/PySequencePattern.kt +++ b/python/python-psi-api/src/com/jetbrains/python/psi/PySequencePattern.kt @@ -4,7 +4,7 @@ package com.jetbrains.python.psi import com.jetbrains.python.ast.PyAstSequencePattern import com.jetbrains.python.ast.findChildrenByClass -interface PySequencePattern : PyAstSequencePattern, PyPattern, PyCaptureContext { +interface PySequencePattern : PyAstSequencePattern, PyPattern { val elements: List get() = findChildrenByClass(PyPattern::class.java).toList() } diff --git a/python/python-psi-impl/src/com/jetbrains/python/psi/impl/PyCaptureContext.kt b/python/python-psi-impl/src/com/jetbrains/python/psi/impl/PyCaptureContext.kt new file mode 100644 index 000000000000..0f6d9f438bb2 --- /dev/null +++ b/python/python-psi-impl/src/com/jetbrains/python/psi/impl/PyCaptureContext.kt @@ -0,0 +1,45 @@ +package com.jetbrains.python.psi.impl + +import com.intellij.psi.util.findParentOfType +import com.jetbrains.python.psi.PyElement +import com.jetbrains.python.psi.PyPattern +import com.jetbrains.python.psi.PyUtil +import com.jetbrains.python.psi.types.PyType +import com.jetbrains.python.psi.types.TypeEvalContext + +interface PyCaptureContext : PyElement { + fun getCaptureTypeForChild(pattern: PyPattern, context: TypeEvalContext): PyType? + + companion object { + /** + * Determines what type this pattern would have if it was a capture pattern (like a bare name or _). + * + * In pattern matching, a capture pattern takes on the type of the entire matched expression, + * regardless of any specific pattern constraints. + * + * For example: + * ```python + * x: int | str + * match x: + * case a: # This is a capture pattern + * # Here 'a' has type int | str + * case str(): # This is a class pattern + * # Capture type: int | str (same as what 'case a:' would get) + * # Regular getType: str + * + * y: int + * match y: + * case str() as a: + * # Capture type: int (same as what 'case a:' would get) + * # Regular getType: intersect(int, str) (just 'str' for now) + * ``` + * @see PyPattern#getType(TypeEvalContext, TypeEvalContext.Key) + */ + @JvmStatic + fun getCaptureType(pattern: PyPattern, context: TypeEvalContext): PyType? { + return PyUtil.getNullableParameterizedCachedValue(pattern, context) { + pattern.findParentOfType()?.getCaptureTypeForChild(pattern, context) + } + } + } +} \ No newline at end of file diff --git a/python/python-psi-impl/src/com/jetbrains/python/psi/impl/PyCapturePatternImpl.java b/python/python-psi-impl/src/com/jetbrains/python/psi/impl/PyCapturePatternImpl.java index 22d74e158d9e..0b21fc62a07c 100644 --- a/python/python-psi-impl/src/com/jetbrains/python/psi/impl/PyCapturePatternImpl.java +++ b/python/python-psi-impl/src/com/jetbrains/python/psi/impl/PyCapturePatternImpl.java @@ -1,7 +1,6 @@ package com.jetbrains.python.psi.impl; import com.intellij.lang.ASTNode; -import com.jetbrains.python.psi.PyCaptureContext; import com.jetbrains.python.psi.PyCapturePattern; import com.jetbrains.python.psi.PyElementVisitor; import com.jetbrains.python.psi.types.PyType; diff --git a/python/python-psi-impl/src/com/jetbrains/python/psi/impl/PyCaseClauseImpl.kt b/python/python-psi-impl/src/com/jetbrains/python/psi/impl/PyCaseClauseImpl.kt index c3b97269fb7d..fb106c5c1cb3 100644 --- a/python/python-psi-impl/src/com/jetbrains/python/psi/impl/PyCaseClauseImpl.kt +++ b/python/python-psi-impl/src/com/jetbrains/python/psi/impl/PyCaseClauseImpl.kt @@ -10,7 +10,7 @@ import com.jetbrains.python.psi.PyPattern import com.jetbrains.python.psi.types.PyType import com.jetbrains.python.psi.types.TypeEvalContext -class PyCaseClauseImpl(astNode: ASTNode?) : PyElementImpl(astNode), PyCaseClause { +class PyCaseClauseImpl(astNode: ASTNode?) : PyElementImpl(astNode), PyCaseClause, PyCaptureContext { override fun acceptPyVisitor(pyVisitor: PyElementVisitor) { pyVisitor.visitPyCaseClause(this) } diff --git a/python/python-psi-impl/src/com/jetbrains/python/psi/impl/PyClassPatternImpl.java b/python/python-psi-impl/src/com/jetbrains/python/psi/impl/PyClassPatternImpl.java index 766e25c1e1bf..dade6dbb619a 100644 --- a/python/python-psi-impl/src/com/jetbrains/python/psi/impl/PyClassPatternImpl.java +++ b/python/python-psi-impl/src/com/jetbrains/python/psi/impl/PyClassPatternImpl.java @@ -19,7 +19,7 @@ import static com.intellij.util.containers.ContainerUtil.getFirstItem; import static com.jetbrains.python.psi.PyUtil.as; import static com.jetbrains.python.psi.PyUtil.multiResolveTopPriority; -public class PyClassPatternImpl extends PyElementImpl implements PyClassPattern { +public class PyClassPatternImpl extends PyElementImpl implements PyClassPattern, PyCaptureContext { public PyClassPatternImpl(ASTNode astNode) { super(astNode); } diff --git a/python/python-psi-impl/src/com/jetbrains/python/psi/impl/PyMappingPatternImpl.kt b/python/python-psi-impl/src/com/jetbrains/python/psi/impl/PyMappingPatternImpl.kt index cf3df8513d07..65d4decc4fa1 100644 --- a/python/python-psi-impl/src/com/jetbrains/python/psi/impl/PyMappingPatternImpl.kt +++ b/python/python-psi-impl/src/com/jetbrains/python/psi/impl/PyMappingPatternImpl.kt @@ -4,12 +4,11 @@ import com.intellij.lang.ASTNode import com.intellij.psi.PsiListLikeElement import com.intellij.psi.util.findParentInFile import com.jetbrains.python.psi.* -import com.jetbrains.python.psi.PyCaptureContext.Companion.getCaptureType import com.jetbrains.python.psi.impl.PyBuiltinCache.Companion.getInstance import com.jetbrains.python.psi.types.* import com.jetbrains.python.psi.types.PyLiteralType.Companion.upcastLiteralToClass -class PyMappingPatternImpl(astNode: ASTNode?) : PyElementImpl(astNode), PyMappingPattern, PsiListLikeElement { +class PyMappingPatternImpl(astNode: ASTNode?) : PyElementImpl(astNode), PyMappingPattern, PyCaptureContext, PsiListLikeElement { override fun acceptPyVisitor(pyVisitor: PyElementVisitor) { pyVisitor.visitPyMappingPattern(this) } @@ -30,7 +29,7 @@ class PyMappingPatternImpl(astNode: ASTNode?) : PyElementImpl(astNode), PyMappin val patternMappingType = wrapInMappingType(PyUnionType.union(keyTypes), PyUnionType.union(valueTypes)) - val filteredType = getCaptureType(this, context).toList().filter { captureType: PyType? -> + val filteredType = PyCaptureContext.getCaptureType(this, context).toList().filter { captureType: PyType? -> val mappingType = PyTypeUtil.convertToType(captureType, "typing.Mapping", this, context) ?: return@filter false PyTypeChecker.match(mappingType, patternMappingType, context) }.let { @@ -53,7 +52,7 @@ class PyMappingPatternImpl(astNode: ASTNode?) : PyElementImpl(astNode), PyMappin if (sequenceMember !is PyKeyValuePattern) return null - return getCaptureType(this, context).toList() + return PyCaptureContext.getCaptureType(this, context).toList() .map { possibleMapping -> possibleMapping.getValueType(sequenceMember, context) } .let { PyUnionType.union(it) } } diff --git a/python/python-psi-impl/src/com/jetbrains/python/psi/impl/PySequencePatternImpl.kt b/python/python-psi-impl/src/com/jetbrains/python/psi/impl/PySequencePatternImpl.kt index f650d58a7179..3893d5c43bde 100644 --- a/python/python-psi-impl/src/com/jetbrains/python/psi/impl/PySequencePatternImpl.kt +++ b/python/python-psi-impl/src/com/jetbrains/python/psi/impl/PySequencePatternImpl.kt @@ -9,7 +9,7 @@ import com.jetbrains.python.psi.types.PyLiteralType.Companion.upcastLiteralToCla import kotlin.contracts.ExperimentalContracts import kotlin.contracts.contract -class PySequencePatternImpl(astNode: ASTNode?) : PyElementImpl(astNode), PySequencePattern, PsiListLikeElement { +class PySequencePatternImpl(astNode: ASTNode?) : PyElementImpl(astNode), PySequencePattern, PyCaptureContext, PsiListLikeElement { override fun acceptPyVisitor(pyVisitor: PyElementVisitor) { pyVisitor.visitPySequencePattern(this) } diff --git a/python/python-psi-impl/src/com/jetbrains/python/psi/impl/PyWildcardPatternImpl.java b/python/python-psi-impl/src/com/jetbrains/python/psi/impl/PyWildcardPatternImpl.java index 2e65e800b4f3..84160dcfbbb4 100644 --- a/python/python-psi-impl/src/com/jetbrains/python/psi/impl/PyWildcardPatternImpl.java +++ b/python/python-psi-impl/src/com/jetbrains/python/psi/impl/PyWildcardPatternImpl.java @@ -1,7 +1,6 @@ package com.jetbrains.python.psi.impl; import com.intellij.lang.ASTNode; -import com.jetbrains.python.psi.PyCaptureContext; import com.jetbrains.python.psi.PyElementVisitor; import com.jetbrains.python.psi.PyWildcardPattern; import com.jetbrains.python.psi.types.PyType;