From ad35b8a70bfbf0ad89c8796f4ae70ccb01cf6343 Mon Sep 17 00:00:00 2001 From: Maksim Zuev Date: Fri, 13 Jun 2025 12:08:28 +0200 Subject: [PATCH] [debugger] Convert JavaDfaAssistProvider to Kotlin GitOrigin-RevId: 1ad4e08de387adee04d5dd5792dd604c90747fd0 --- .../engine/dfaassist/DfaAssistProvider.kt | 4 +- .../dfaassist/java/JavaDfaAssistProvider.kt | 261 +++++++++--------- 2 files changed, 130 insertions(+), 135 deletions(-) diff --git a/java/debugger/impl/src/com/intellij/debugger/engine/dfaassist/DfaAssistProvider.kt b/java/debugger/impl/src/com/intellij/debugger/engine/dfaassist/DfaAssistProvider.kt index 3ac7f5ca92dd..07f4eb819910 100644 --- a/java/debugger/impl/src/com/intellij/debugger/engine/dfaassist/DfaAssistProvider.kt +++ b/java/debugger/impl/src/com/intellij/debugger/engine/dfaassist/DfaAssistProvider.kt @@ -56,7 +56,7 @@ interface DfaAssistProvider { /** * @param proxy proxy to create JDI values - * @param var DfaVariableValue to find value for + * @param dfaVar DfaVariableValue to find value for * @param anchor anchor previously returned by [.getAnchor] call, where analysis takes place * @return JDI value for a variable; null if value is not known; NullConst if value is known to be null * (use [.wrap] utility method for this purpose). @@ -65,7 +65,7 @@ interface DfaAssistProvider { @Throws(EvaluateException::class) fun getJdiValueForDfaVariable( proxy: StackFrameProxyEx, - `var`: DfaVariableValue, + dfaVar: DfaVariableValue, anchor: PsiElement, ): Value? diff --git a/java/debugger/impl/src/com/intellij/debugger/engine/dfaassist/java/JavaDfaAssistProvider.kt b/java/debugger/impl/src/com/intellij/debugger/engine/dfaassist/java/JavaDfaAssistProvider.kt index b623901558ce..8f89c40bc133 100644 --- a/java/debugger/impl/src/com/intellij/debugger/engine/dfaassist/java/JavaDfaAssistProvider.kt +++ b/java/debugger/impl/src/com/intellij/debugger/engine/dfaassist/java/JavaDfaAssistProvider.kt @@ -1,196 +1,191 @@ // Copyright 2000-2024 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license. -package com.intellij.debugger.engine.dfaassist.java; +package com.intellij.debugger.engine.dfaassist.java -import com.intellij.codeInspection.dataFlow.TypeConstraint; -import com.intellij.codeInspection.dataFlow.TypeConstraints; -import com.intellij.codeInspection.dataFlow.jvm.SpecialField; -import com.intellij.codeInspection.dataFlow.jvm.descriptors.ArrayElementDescriptor; -import com.intellij.codeInspection.dataFlow.value.DfaVariableValue; -import com.intellij.codeInspection.dataFlow.value.VariableDescriptor; -import com.intellij.debugger.engine.DebuggerUtils; -import com.intellij.debugger.engine.JVMNameUtil; -import com.intellij.debugger.engine.dfaassist.DebuggerDfaListener; -import com.intellij.debugger.engine.dfaassist.DfaAssistProvider; -import com.intellij.debugger.engine.evaluation.EvaluateException; -import com.intellij.debugger.engine.evaluation.expression.CaptureTraverser; -import com.intellij.debugger.engine.jdi.LocalVariableProxy; -import com.intellij.debugger.impl.DebuggerUtilsEx; -import com.intellij.debugger.jdi.StackFrameProxyEx; -import com.intellij.psi.*; -import com.intellij.psi.tree.IElementType; -import com.intellij.psi.util.PsiTreeUtil; -import com.intellij.util.containers.ContainerUtil; -import com.sun.jdi.*; -import org.jetbrains.annotations.NotNull; -import org.jetbrains.annotations.Nullable; +import com.intellij.codeInspection.dataFlow.TypeConstraint +import com.intellij.codeInspection.dataFlow.TypeConstraints +import com.intellij.codeInspection.dataFlow.jvm.SpecialField +import com.intellij.codeInspection.dataFlow.jvm.descriptors.ArrayElementDescriptor +import com.intellij.codeInspection.dataFlow.value.DfaVariableValue +import com.intellij.debugger.engine.DebuggerUtils +import com.intellij.debugger.engine.JVMNameUtil +import com.intellij.debugger.engine.dfaassist.DebuggerDfaListener +import com.intellij.debugger.engine.dfaassist.DfaAssistProvider +import com.intellij.debugger.engine.dfaassist.DfaAssistProvider.Companion.wrap +import com.intellij.debugger.engine.evaluation.EvaluateException +import com.intellij.debugger.engine.evaluation.expression.CaptureTraverser +import com.intellij.debugger.impl.DebuggerUtilsEx +import com.intellij.debugger.jdi.StackFrameProxyEx +import com.intellij.psi.* +import com.intellij.psi.util.PsiTreeUtil +import com.intellij.util.containers.ContainerUtil +import com.sun.jdi.* -public final class JavaDfaAssistProvider implements DfaAssistProvider { - @Override - public boolean locationMatches(@NotNull PsiElement element, @NotNull Location location) { - Method method = location.method(); - PsiElement context = DebuggerUtilsEx.getContainingMethod(element); - if (context instanceof PsiMethod psiMethod) { - String name = psiMethod.isConstructor() ? "" : psiMethod.getName(); - return name.equals(method.name()) && psiMethod.getParameterList().getParametersCount() == method.argumentTypeNames().size(); +class JavaDfaAssistProvider : DfaAssistProvider { + override fun locationMatches(element: PsiElement, location: Location): Boolean { + val method = location.method() + val context = DebuggerUtilsEx.getContainingMethod(element) + if (context is PsiMethod) { + val name = if (context.isConstructor()) "" else context.getName() + return name == method.name() && context.getParameterList().getParametersCount() == method.argumentTypeNames().size } - if (context instanceof PsiLambdaExpression lambda) { + if (context is PsiLambdaExpression) { return DebuggerUtilsEx.isLambda(method) && - method.argumentTypeNames().size() >= lambda.getParameterList().getParametersCount(); + method.argumentTypeNames().size >= context.getParameterList().getParametersCount() } - if (context instanceof PsiClassInitializer initializer) { - String expectedMethod = initializer.hasModifierProperty(PsiModifier.STATIC) ? "" : ""; - return method.name().equals(expectedMethod); + if (context is PsiClassInitializer) { + val expectedMethod = if (context.hasModifierProperty(PsiModifier.STATIC)) "" else "" + return method.name() == expectedMethod } - return false; + return false } - @Override - public @Nullable PsiElement getAnchor(@NotNull PsiElement element) { - while (element instanceof PsiWhiteSpace || element instanceof PsiComment) { - element = element.getNextSibling(); + override fun getAnchor(element: PsiElement): PsiElement? { + var element = element + while (element is PsiWhiteSpace || element is PsiComment) { + element = element.getNextSibling() } - while (!(element instanceof PsiStatement)) { - PsiElement parent = element.getParent(); - if (!(parent instanceof PsiStatement) && (parent == null || element.getTextRangeInParent().getStartOffset() > 0)) { - if (parent instanceof PsiCodeBlock && ((PsiCodeBlock)parent).getRBrace() == element) { - PsiElement grandParent = parent.getParent(); - if (grandParent instanceof PsiBlockStatement) { - return PsiTreeUtil.getNextSiblingOfType(grandParent, PsiStatement.class); + while (element !is PsiStatement) { + val parent = element.getParent() + if (parent !is PsiStatement && (parent == null || element.textRangeInParent.startOffset > 0)) { + if (parent is PsiCodeBlock && parent.getRBrace() === element) { + val grandParent = parent.getParent() + if (grandParent is PsiBlockStatement) { + return PsiTreeUtil.getNextSiblingOfType(grandParent, PsiStatement::class.java) } } - if (parent instanceof PsiPolyadicExpression) { + if (parent is PsiPolyadicExpression) { // If we are inside the expression we can position only at locations where the stack is empty // currently only && and || chains inside if/return/yield are allowed - IElementType tokenType = ((PsiPolyadicExpression)parent).getOperationTokenType(); - if (tokenType.equals(JavaTokenType.ANDAND) || tokenType.equals(JavaTokenType.OROR)) { - PsiElement grandParent = parent.getParent(); - if (grandParent instanceof PsiIfStatement || grandParent instanceof PsiYieldStatement || - grandParent instanceof PsiReturnStatement) { - if (element instanceof PsiExpression) { - return element; + val tokenType = parent.getOperationTokenType() + if (tokenType == JavaTokenType.ANDAND || tokenType == JavaTokenType.OROR) { + val grandParent = parent.getParent() + if (grandParent is PsiIfStatement || grandParent is PsiYieldStatement || + grandParent is PsiReturnStatement + ) { + if (element is PsiExpression) { + return element } - return PsiTreeUtil.getNextSiblingOfType(element, PsiExpression.class); + return PsiTreeUtil.getNextSiblingOfType(element, PsiExpression::class.java) } } } - return null; + return null } - element = parent; + element = parent } - return element; + return element } - @Override - public @Nullable PsiElement getCodeBlock(@NotNull PsiElement anchor) { - if (anchor instanceof PsiSwitchLabelStatementBase) { - return null; // unsupported yet + override fun getCodeBlock(anchor: PsiElement): PsiElement? { + if (anchor is PsiSwitchLabelStatementBase) { + return null // unsupported yet } - PsiElement e = anchor; - while (e != null && !(e instanceof PsiClass) && !(e instanceof PsiFileSystemItem)) { - e = e.getParent(); - if (e instanceof PsiCodeBlock) { - PsiElement parent = e.getParent(); - if (parent instanceof PsiMethod || parent instanceof PsiLambdaExpression || parent instanceof PsiClassInitializer || + var e: PsiElement? = anchor + while (e != null && (e !is PsiClass) && (e !is PsiFileSystemItem)) { + e = e.getParent() + if (e is PsiCodeBlock) { + val parent = e.getParent() + if (parent is PsiMethod || parent is PsiLambdaExpression || parent is PsiClassInitializer || // We cannot properly restore context if we started from finally, so let's analyze just finally block - parent instanceof PsiTryStatement && ((PsiTryStatement)parent).getFinallyBlock() == e || - parent instanceof PsiBlockStatement && - (parent.getParent() instanceof PsiSwitchLabeledRuleStatement && - ((PsiSwitchLabeledRuleStatement)parent.getParent()).getEnclosingSwitchBlock() instanceof PsiSwitchExpression)) { - return e; + parent is PsiTryStatement && parent.getFinallyBlock() === e || parent is PsiBlockStatement && + (parent.getParent() is PsiSwitchLabeledRuleStatement && + (parent.getParent() as PsiSwitchLabeledRuleStatement).getEnclosingSwitchBlock() is PsiSwitchExpression)) { + return e } } } - return null; + return null } - @Override - public @Nullable Value getJdiValueForDfaVariable(@NotNull StackFrameProxyEx proxy, - @NotNull DfaVariableValue var, - @NotNull PsiElement anchor) throws EvaluateException { - if (var.getQualifier() != null) { - VariableDescriptor descriptor = var.getDescriptor(); - if (descriptor instanceof SpecialField) { + @Throws(EvaluateException::class) + override fun getJdiValueForDfaVariable( + proxy: StackFrameProxyEx, + dfaVar: DfaVariableValue, + anchor: PsiElement, + ): Value? { + val qualifier = dfaVar.qualifier + if (qualifier != null) { + val descriptor = dfaVar.descriptor + if (descriptor is SpecialField) { // Special fields facts are applied from qualifiers - return null; + return null } - Value qualifierValue = getJdiValueForDfaVariable(proxy, var.getQualifier(), anchor); - if (qualifierValue == null) return null; - PsiElement element = descriptor.getPsiElement(); - if (element instanceof PsiField psiField && qualifierValue instanceof ObjectReference objectRef) { - ReferenceType type = objectRef.referenceType(); - PsiClass psiClass = psiField.getContainingClass(); - if (psiClass != null && type.name().equals(JVMNameUtil.getClassVMName(psiClass))) { - Field field = DebuggerUtils.findField(type, psiField.getName()); + val qualifierValue = getJdiValueForDfaVariable(proxy, qualifier, anchor) + if (qualifierValue == null) return null + val element = descriptor.psiElement + if (element is PsiField && qualifierValue is ObjectReference) { + val type = qualifierValue.referenceType() + val psiClass = element.getContainingClass() + if (psiClass != null && type.name() == JVMNameUtil.getClassVMName(psiClass)) { + val field = DebuggerUtils.findField(type, element.getName()) if (field != null) { - return DfaAssistProvider.wrap(objectRef.getValue(field)); + return wrap(qualifierValue.getValue(field)) } } } - if (descriptor instanceof ArrayElementDescriptor arrayDesc && qualifierValue instanceof ArrayReference arrayRef) { - int index = arrayDesc.getIndex(); - int length = arrayRef.length(); - if (index >= 0 && index < length) { - return DfaAssistProvider.wrap(arrayRef.getValue(index)); + if (descriptor is ArrayElementDescriptor && qualifierValue is ArrayReference) { + val index = descriptor.index + val length = qualifierValue.length() + if (index in 0..