Debugger: Run control flow on code fragments (KT-30120)
This commit is contained in:
+4
-14
@@ -18,6 +18,8 @@ package org.jetbrains.kotlin.idea.caches.resolve
|
|||||||
|
|
||||||
import com.intellij.psi.PsiElement
|
import com.intellij.psi.PsiElement
|
||||||
import org.jetbrains.kotlin.descriptors.*
|
import org.jetbrains.kotlin.descriptors.*
|
||||||
|
import org.jetbrains.kotlin.idea.analysis.analyzeInContext
|
||||||
|
import org.jetbrains.kotlin.idea.caches.resolve.util.analyzeControlFlow
|
||||||
import org.jetbrains.kotlin.idea.project.ResolveElementCache
|
import org.jetbrains.kotlin.idea.project.ResolveElementCache
|
||||||
import org.jetbrains.kotlin.idea.util.getResolutionScope
|
import org.jetbrains.kotlin.idea.util.getResolutionScope
|
||||||
import org.jetbrains.kotlin.incremental.components.NoLookupLocation
|
import org.jetbrains.kotlin.incremental.components.NoLookupLocation
|
||||||
@@ -27,21 +29,16 @@ import org.jetbrains.kotlin.psi.psiUtil.getParentOfType
|
|||||||
import org.jetbrains.kotlin.psi.psiUtil.getParentOfTypes3
|
import org.jetbrains.kotlin.psi.psiUtil.getParentOfTypes3
|
||||||
import org.jetbrains.kotlin.resolve.*
|
import org.jetbrains.kotlin.resolve.*
|
||||||
import org.jetbrains.kotlin.resolve.bindingContextUtil.getDataFlowInfoAfter
|
import org.jetbrains.kotlin.resolve.bindingContextUtil.getDataFlowInfoAfter
|
||||||
import org.jetbrains.kotlin.resolve.calls.components.InferenceSession
|
|
||||||
import org.jetbrains.kotlin.resolve.calls.smartcasts.DataFlowInfo
|
import org.jetbrains.kotlin.resolve.calls.smartcasts.DataFlowInfo
|
||||||
import org.jetbrains.kotlin.resolve.lazy.BodyResolveMode
|
import org.jetbrains.kotlin.resolve.lazy.BodyResolveMode
|
||||||
import org.jetbrains.kotlin.resolve.lazy.ResolveSession
|
import org.jetbrains.kotlin.resolve.lazy.ResolveSession
|
||||||
import org.jetbrains.kotlin.resolve.scopes.*
|
import org.jetbrains.kotlin.resolve.scopes.*
|
||||||
import org.jetbrains.kotlin.resolve.scopes.utils.addImportingScopes
|
import org.jetbrains.kotlin.resolve.scopes.utils.addImportingScopes
|
||||||
import org.jetbrains.kotlin.types.TypeUtils
|
|
||||||
import org.jetbrains.kotlin.types.expressions.ExpressionTypingServices
|
|
||||||
import org.jetbrains.kotlin.types.expressions.PreliminaryDeclarationVisitor
|
|
||||||
import javax.inject.Inject
|
import javax.inject.Inject
|
||||||
|
|
||||||
class CodeFragmentAnalyzer(
|
class CodeFragmentAnalyzer(
|
||||||
private val resolveSession: ResolveSession,
|
private val resolveSession: ResolveSession,
|
||||||
private val qualifierResolver: QualifiedExpressionResolver,
|
private val qualifierResolver: QualifiedExpressionResolver,
|
||||||
private val expressionTypingServices: ExpressionTypingServices,
|
|
||||||
private val typeResolver: TypeResolver
|
private val typeResolver: TypeResolver
|
||||||
) {
|
) {
|
||||||
@set:Inject // component dependency cycle
|
@set:Inject // component dependency cycle
|
||||||
@@ -58,15 +55,8 @@ class CodeFragmentAnalyzer(
|
|||||||
|
|
||||||
when (val contentElement = codeFragment.getContentElement()) {
|
when (val contentElement = codeFragment.getContentElement()) {
|
||||||
is KtExpression -> {
|
is KtExpression -> {
|
||||||
PreliminaryDeclarationVisitor.createForExpression(
|
contentElement.analyzeInContext(scope, trace = bindingTrace, dataFlowInfo = dataFlowInfo)
|
||||||
contentElement, bindingTrace,
|
analyzeControlFlow(resolveSession, contentElement, bindingTrace)
|
||||||
expressionTypingServices.languageVersionSettings
|
|
||||||
)
|
|
||||||
|
|
||||||
expressionTypingServices.getTypeInfo(
|
|
||||||
scope, contentElement, TypeUtils.NO_EXPECTED_TYPE,
|
|
||||||
dataFlowInfo, InferenceSession.default, bindingTrace, false
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
is KtTypeReference -> {
|
is KtTypeReference -> {
|
||||||
|
|||||||
+23
@@ -0,0 +1,23 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2010-2019 JetBrains s.r.o. and Kotlin Programming Language contributors.
|
||||||
|
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package org.jetbrains.kotlin.idea.caches.resolve.util
|
||||||
|
|
||||||
|
import org.jetbrains.kotlin.cfg.ControlFlowInformationProvider
|
||||||
|
import org.jetbrains.kotlin.idea.project.languageVersionSettings
|
||||||
|
import org.jetbrains.kotlin.psi.KtElement
|
||||||
|
import org.jetbrains.kotlin.resolve.BindingTrace
|
||||||
|
import org.jetbrains.kotlin.resolve.DelegatingBindingTrace
|
||||||
|
import org.jetbrains.kotlin.resolve.lazy.ResolveSession
|
||||||
|
|
||||||
|
fun analyzeControlFlow(resolveSession: ResolveSession, resolveElement: KtElement, trace: BindingTrace) {
|
||||||
|
val controlFlowTrace = DelegatingBindingTrace(
|
||||||
|
trace.bindingContext, "Element control flow resolve", resolveElement, allowSliceRewrite = true
|
||||||
|
)
|
||||||
|
ControlFlowInformationProvider(
|
||||||
|
resolveElement, controlFlowTrace, resolveElement.languageVersionSettings, resolveSession.platformDiagnosticSuppressor
|
||||||
|
).checkDeclaration()
|
||||||
|
controlFlowTrace.addOwnDataTo(trace, null, false)
|
||||||
|
}
|
||||||
@@ -32,6 +32,7 @@ import org.jetbrains.kotlin.descriptors.*
|
|||||||
import org.jetbrains.kotlin.descriptors.annotations.Annotations
|
import org.jetbrains.kotlin.descriptors.annotations.Annotations
|
||||||
import org.jetbrains.kotlin.frontend.di.createContainerForBodyResolve
|
import org.jetbrains.kotlin.frontend.di.createContainerForBodyResolve
|
||||||
import org.jetbrains.kotlin.idea.caches.resolve.CodeFragmentAnalyzer
|
import org.jetbrains.kotlin.idea.caches.resolve.CodeFragmentAnalyzer
|
||||||
|
import org.jetbrains.kotlin.idea.caches.resolve.util.analyzeControlFlow
|
||||||
import org.jetbrains.kotlin.name.Name
|
import org.jetbrains.kotlin.name.Name
|
||||||
import org.jetbrains.kotlin.psi.*
|
import org.jetbrains.kotlin.psi.*
|
||||||
import org.jetbrains.kotlin.psi.psiUtil.forEachDescendantOfType
|
import org.jetbrains.kotlin.psi.psiUtil.forEachDescendantOfType
|
||||||
@@ -413,13 +414,7 @@ class ResolveElementCache(
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (bodyResolveMode.doControlFlowAnalysis) {
|
if (bodyResolveMode.doControlFlowAnalysis) {
|
||||||
val controlFlowTrace = DelegatingBindingTrace(
|
analyzeControlFlow(resolveSession, resolveElement, trace)
|
||||||
trace.bindingContext, "Element control flow resolve", resolveElement, allowSliceRewrite = true
|
|
||||||
)
|
|
||||||
ControlFlowInformationProvider(
|
|
||||||
resolveElement, controlFlowTrace, resolveElement.languageVersionSettings, resolveSession.platformDiagnosticSuppressor
|
|
||||||
).checkDeclaration()
|
|
||||||
controlFlowTrace.addOwnDataTo(trace, null, false)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return Pair(trace.bindingContext, statementFilterUsed)
|
return Pair(trace.bindingContext, statementFilterUsed)
|
||||||
|
|||||||
@@ -0,0 +1,16 @@
|
|||||||
|
// INSPECTION_CLASS: org.jetbrains.kotlin.idea.inspections.UnusedEqualsInspection
|
||||||
|
|
||||||
|
fun main() {
|
||||||
|
val list = java.util.ArrayList<Int>()
|
||||||
|
<caret>val x = 0
|
||||||
|
}
|
||||||
|
|
||||||
|
fun <T> Collection<T>.isAny(predicate: (T) -> Boolean): Boolean {
|
||||||
|
for (item in this) {
|
||||||
|
if (predicate(item)) {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return false
|
||||||
|
}
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
list.isAny { it == 2 }
|
||||||
+18
-1
@@ -6,6 +6,7 @@
|
|||||||
package org.jetbrains.kotlin.idea.debugger.evaluate
|
package org.jetbrains.kotlin.idea.debugger.evaluate
|
||||||
|
|
||||||
import com.intellij.codeInsight.completion.CompletionType
|
import com.intellij.codeInsight.completion.CompletionType
|
||||||
|
import com.intellij.codeInspection.InspectionProfileEntry
|
||||||
import com.intellij.openapi.util.io.FileUtil
|
import com.intellij.openapi.util.io.FileUtil
|
||||||
import com.intellij.openapi.util.text.StringUtil
|
import com.intellij.openapi.util.text.StringUtil
|
||||||
import com.intellij.psi.PsiElement
|
import com.intellij.psi.PsiElement
|
||||||
@@ -33,7 +34,7 @@ import kotlin.test.assertTrue
|
|||||||
abstract class AbstractCodeFragmentHighlightingTest : AbstractPsiCheckerTest() {
|
abstract class AbstractCodeFragmentHighlightingTest : AbstractPsiCheckerTest() {
|
||||||
override fun doTest(filePath: String) {
|
override fun doTest(filePath: String) {
|
||||||
myFixture.configureByCodeFragment(filePath)
|
myFixture.configureByCodeFragment(filePath)
|
||||||
myFixture.checkHighlighting(true, false, false)
|
checkHighlighting(filePath)
|
||||||
}
|
}
|
||||||
|
|
||||||
fun doTestWithImport(filePath: String) {
|
fun doTestWithImport(filePath: String) {
|
||||||
@@ -49,6 +50,22 @@ abstract class AbstractCodeFragmentHighlightingTest : AbstractPsiCheckerTest() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
checkHighlighting(filePath)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun checkHighlighting(filePath: String) {
|
||||||
|
val inspectionName = InTextDirectivesUtils.findStringWithPrefixes(File(filePath).readText(), "// INSPECTION_CLASS: ")
|
||||||
|
if (inspectionName != null) {
|
||||||
|
val inspection = Class.forName(inspectionName).newInstance() as InspectionProfileEntry
|
||||||
|
myFixture.enableInspections(inspection)
|
||||||
|
try {
|
||||||
|
myFixture.checkHighlighting(true, false, false)
|
||||||
|
} finally {
|
||||||
|
myFixture.disableInspections(inspection)
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
myFixture.checkHighlighting(true, false, false)
|
myFixture.checkHighlighting(true, false, false)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Generated
+5
@@ -141,6 +141,11 @@ public class CodeFragmentHighlightingTestGenerated extends AbstractCodeFragmentH
|
|||||||
runTest("idea/testData/checker/codeFragments/startingFromReturn.kt");
|
runTest("idea/testData/checker/codeFragments/startingFromReturn.kt");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@TestMetadata("unusedEquals.kt")
|
||||||
|
public void testUnusedEquals() throws Exception {
|
||||||
|
runTest("idea/testData/checker/codeFragments/unusedEquals.kt");
|
||||||
|
}
|
||||||
|
|
||||||
@TestMetadata("withoutBodyFunction.kt")
|
@TestMetadata("withoutBodyFunction.kt")
|
||||||
public void testWithoutBodyFunction() throws Exception {
|
public void testWithoutBodyFunction() throws Exception {
|
||||||
runTest("idea/testData/checker/codeFragments/withoutBodyFunction.kt");
|
runTest("idea/testData/checker/codeFragments/withoutBodyFunction.kt");
|
||||||
|
|||||||
Reference in New Issue
Block a user