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 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.util.getResolutionScope
|
||||
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.resolve.*
|
||||
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.lazy.BodyResolveMode
|
||||
import org.jetbrains.kotlin.resolve.lazy.ResolveSession
|
||||
import org.jetbrains.kotlin.resolve.scopes.*
|
||||
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
|
||||
|
||||
class CodeFragmentAnalyzer(
|
||||
private val resolveSession: ResolveSession,
|
||||
private val qualifierResolver: QualifiedExpressionResolver,
|
||||
private val expressionTypingServices: ExpressionTypingServices,
|
||||
private val typeResolver: TypeResolver
|
||||
) {
|
||||
@set:Inject // component dependency cycle
|
||||
@@ -58,15 +55,8 @@ class CodeFragmentAnalyzer(
|
||||
|
||||
when (val contentElement = codeFragment.getContentElement()) {
|
||||
is KtExpression -> {
|
||||
PreliminaryDeclarationVisitor.createForExpression(
|
||||
contentElement, bindingTrace,
|
||||
expressionTypingServices.languageVersionSettings
|
||||
)
|
||||
|
||||
expressionTypingServices.getTypeInfo(
|
||||
scope, contentElement, TypeUtils.NO_EXPECTED_TYPE,
|
||||
dataFlowInfo, InferenceSession.default, bindingTrace, false
|
||||
)
|
||||
contentElement.analyzeInContext(scope, trace = bindingTrace, dataFlowInfo = dataFlowInfo)
|
||||
analyzeControlFlow(resolveSession, contentElement, bindingTrace)
|
||||
}
|
||||
|
||||
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.frontend.di.createContainerForBodyResolve
|
||||
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.psi.*
|
||||
import org.jetbrains.kotlin.psi.psiUtil.forEachDescendantOfType
|
||||
@@ -413,13 +414,7 @@ class ResolveElementCache(
|
||||
}
|
||||
|
||||
if (bodyResolveMode.doControlFlowAnalysis) {
|
||||
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)
|
||||
analyzeControlFlow(resolveSession, resolveElement, trace)
|
||||
}
|
||||
|
||||
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
|
||||
|
||||
import com.intellij.codeInsight.completion.CompletionType
|
||||
import com.intellij.codeInspection.InspectionProfileEntry
|
||||
import com.intellij.openapi.util.io.FileUtil
|
||||
import com.intellij.openapi.util.text.StringUtil
|
||||
import com.intellij.psi.PsiElement
|
||||
@@ -33,7 +34,7 @@ import kotlin.test.assertTrue
|
||||
abstract class AbstractCodeFragmentHighlightingTest : AbstractPsiCheckerTest() {
|
||||
override fun doTest(filePath: String) {
|
||||
myFixture.configureByCodeFragment(filePath)
|
||||
myFixture.checkHighlighting(true, false, false)
|
||||
checkHighlighting(filePath)
|
||||
}
|
||||
|
||||
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)
|
||||
}
|
||||
}
|
||||
|
||||
Generated
+5
@@ -141,6 +141,11 @@ public class CodeFragmentHighlightingTestGenerated extends AbstractCodeFragmentH
|
||||
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")
|
||||
public void testWithoutBodyFunction() throws Exception {
|
||||
runTest("idea/testData/checker/codeFragments/withoutBodyFunction.kt");
|
||||
|
||||
Reference in New Issue
Block a user