From a414843f54cf1780c03e53ba007d00c1f8213d49 Mon Sep 17 00:00:00 2001 From: Alexey Sedunov Date: Wed, 2 Nov 2016 16:15:07 +0300 Subject: [PATCH] Control-Flow Analysis: Use PSI to generate pseudocode if nested resolved call is confused with outer one (e.g. a {} when invoke() is missing) #KT-14500 Fixed --- ChangeLog.md | 1 + .../kotlin/cfg/ControlFlowProcessor.kt | 6 ++- ...unresolvedInvokeOnResolvedVar.instructions | 52 +++++++++++++++++++ .../cfg/bugs/unresolvedInvokeOnResolvedVar.kt | 5 ++ .../bugs/unresolvedInvokeOnResolvedVar.values | 19 +++++++ .../invalidVariableCall.instructions | 10 ++-- .../expressions/invalidVariableCall.values | 6 +-- .../kotlin/cfg/ControlFlowTestGenerated.java | 6 +++ .../kotlin/cfg/PseudoValueTestGenerated.java | 6 +++ 9 files changed, 103 insertions(+), 8 deletions(-) create mode 100644 compiler/testData/cfg/bugs/unresolvedInvokeOnResolvedVar.instructions create mode 100644 compiler/testData/cfg/bugs/unresolvedInvokeOnResolvedVar.kt create mode 100644 compiler/testData/cfg/bugs/unresolvedInvokeOnResolvedVar.values diff --git a/ChangeLog.md b/ChangeLog.md index bafd0a652cc..4e6f3b2fcb1 100644 --- a/ChangeLog.md +++ b/ChangeLog.md @@ -224,6 +224,7 @@ These artifacts include extensions for the types available in the latter JDKs, s - [`KT-14569`](https://youtrack.jetbrains.com/issue/KT-14569) Convert Property to Function Intention: Search occurrences using progress dialog - [`KT-14501`](https://youtrack.jetbrains.com/issue/KT-14501) Create from Usage: Support array access expressions/binary expressions with type mismatch errors +- [`KT-14500`](https://youtrack.jetbrains.com/issue/KT-14500) Create from Usage: Suggest functional type based on the call with lambda argument and unresolved invoke() #### Refactorings diff --git a/compiler/frontend/src/org/jetbrains/kotlin/cfg/ControlFlowProcessor.kt b/compiler/frontend/src/org/jetbrains/kotlin/cfg/ControlFlowProcessor.kt index 9a42dc76e6b..de19c7ca107 100644 --- a/compiler/frontend/src/org/jetbrains/kotlin/cfg/ControlFlowProcessor.kt +++ b/compiler/frontend/src/org/jetbrains/kotlin/cfg/ControlFlowProcessor.kt @@ -40,6 +40,7 @@ import org.jetbrains.kotlin.lexer.KtTokens.* import org.jetbrains.kotlin.psi.* import org.jetbrains.kotlin.psi.psiUtil.getQualifiedElementSelector import org.jetbrains.kotlin.psi.psiUtil.getStrictParentOfType +import org.jetbrains.kotlin.psi.psiUtil.isAncestor import org.jetbrains.kotlin.resolve.BindingContext import org.jetbrains.kotlin.resolve.BindingContextUtils import org.jetbrains.kotlin.resolve.BindingTrace @@ -1466,7 +1467,10 @@ class ControlFlowProcessor(private val trace: BindingTrace) { } private fun generateCall(callElement: KtElement): Boolean { - return checkAndGenerateCall(callElement.getResolvedCall(trace.bindingContext)) + val resolvedCall = callElement.getResolvedCall(trace.bindingContext) + val callElementFromResolvedCall = resolvedCall?.call?.callElement ?: return false + if (callElement.isAncestor(callElementFromResolvedCall, true)) return false + return checkAndGenerateCall(resolvedCall) } private fun checkAndGenerateCall(resolvedCall: ResolvedCall<*>?): Boolean { diff --git a/compiler/testData/cfg/bugs/unresolvedInvokeOnResolvedVar.instructions b/compiler/testData/cfg/bugs/unresolvedInvokeOnResolvedVar.instructions new file mode 100644 index 00000000000..71203ed58dc --- /dev/null +++ b/compiler/testData/cfg/bugs/unresolvedInvokeOnResolvedVar.instructions @@ -0,0 +1,52 @@ +== A == +class A +--------------------- +L0: + 1 +L1: + NEXT:[] +error: + PREV:[] +sink: + PREV:[, ] +===================== +== test == +fun test(a: A) { + a { } +} +--------------------- +L0: + 1 + v(a: A) + magic[FAKE_INITIALIZER](a: A) -> + w(a|) + 2 mark({ a { } }) + mark({ }) + jmp?(L2) NEXT:[r({ }) -> , d({ })] + d({ }) NEXT:[] +L2 [after local declaration]: + r({ }) -> PREV:[jmp?(L2)] + r(a) -> + mark(a { }) + magic[UNRESOLVED_CALL](a { }|, ) -> +L1: + 1 NEXT:[] +error: + PREV:[] +sink: + PREV:[, , d({ })] +===================== +== anonymous_0 == +{ } +--------------------- +L3: + 3 + 4 mark() + read (Unit) +L4: + 3 NEXT:[] +error: + PREV:[] +sink: + PREV:[, ] +===================== diff --git a/compiler/testData/cfg/bugs/unresolvedInvokeOnResolvedVar.kt b/compiler/testData/cfg/bugs/unresolvedInvokeOnResolvedVar.kt new file mode 100644 index 00000000000..93018822b09 --- /dev/null +++ b/compiler/testData/cfg/bugs/unresolvedInvokeOnResolvedVar.kt @@ -0,0 +1,5 @@ +class A + +fun test(a: A) { + a { } +} \ No newline at end of file diff --git a/compiler/testData/cfg/bugs/unresolvedInvokeOnResolvedVar.values b/compiler/testData/cfg/bugs/unresolvedInvokeOnResolvedVar.values new file mode 100644 index 00000000000..193257939f9 --- /dev/null +++ b/compiler/testData/cfg/bugs/unresolvedInvokeOnResolvedVar.values @@ -0,0 +1,19 @@ +== A == +class A +--------------------- +===================== +== test == +fun test(a: A) { + a { } +} +--------------------- + : A NEW: magic[FAKE_INITIALIZER](a: A) -> +a : * NEW: r(a) -> +{ } : * NEW: r({ }) -> +a { } : * NEW: magic[UNRESOLVED_CALL](a { }|, ) -> +{ a { } } : * COPY +===================== +== anonymous_0 == +{ } +--------------------- +===================== diff --git a/compiler/testData/cfg/expressions/invalidVariableCall.instructions b/compiler/testData/cfg/expressions/invalidVariableCall.instructions index 1f7571707c9..666ea155115 100644 --- a/compiler/testData/cfg/expressions/invalidVariableCall.instructions +++ b/compiler/testData/cfg/expressions/invalidVariableCall.instructions @@ -10,10 +10,12 @@ L0: w(i|) 2 mark({ i() }) r(i) -> + mark(i()) + magic[UNRESOLVED_CALL](i()|) -> L1: - 1 NEXT:[] + 1 NEXT:[] error: - PREV:[] + PREV:[] sink: - PREV:[, ] -===================== \ No newline at end of file + PREV:[, ] +===================== diff --git a/compiler/testData/cfg/expressions/invalidVariableCall.values b/compiler/testData/cfg/expressions/invalidVariableCall.values index 20de0d7f054..86eed54dea8 100644 --- a/compiler/testData/cfg/expressions/invalidVariableCall.values +++ b/compiler/testData/cfg/expressions/invalidVariableCall.values @@ -5,6 +5,6 @@ fun foo(i: Int) { --------------------- : Int NEW: magic[FAKE_INITIALIZER](i: Int) -> i : * NEW: r(i) -> -i() !: * -{ i() } !: * COPY -===================== \ No newline at end of file +i() : * NEW: magic[UNRESOLVED_CALL](i()|) -> +{ i() } : * COPY +===================== diff --git a/compiler/tests/org/jetbrains/kotlin/cfg/ControlFlowTestGenerated.java b/compiler/tests/org/jetbrains/kotlin/cfg/ControlFlowTestGenerated.java index 6243e434868..764f65a1692 100644 --- a/compiler/tests/org/jetbrains/kotlin/cfg/ControlFlowTestGenerated.java +++ b/compiler/tests/org/jetbrains/kotlin/cfg/ControlFlowTestGenerated.java @@ -162,6 +162,12 @@ public class ControlFlowTestGenerated extends AbstractControlFlowTest { String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/cfg/bugs/setWithTypeMismatch.kt"); doTest(fileName); } + + @TestMetadata("unresolvedInvokeOnResolvedVar.kt") + public void testUnresolvedInvokeOnResolvedVar() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/cfg/bugs/unresolvedInvokeOnResolvedVar.kt"); + doTest(fileName); + } } @TestMetadata("compiler/testData/cfg/controlStructures") diff --git a/compiler/tests/org/jetbrains/kotlin/cfg/PseudoValueTestGenerated.java b/compiler/tests/org/jetbrains/kotlin/cfg/PseudoValueTestGenerated.java index ca6d6094a77..467e43ebb01 100644 --- a/compiler/tests/org/jetbrains/kotlin/cfg/PseudoValueTestGenerated.java +++ b/compiler/tests/org/jetbrains/kotlin/cfg/PseudoValueTestGenerated.java @@ -164,6 +164,12 @@ public class PseudoValueTestGenerated extends AbstractPseudoValueTest { String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/cfg/bugs/setWithTypeMismatch.kt"); doTest(fileName); } + + @TestMetadata("unresolvedInvokeOnResolvedVar.kt") + public void testUnresolvedInvokeOnResolvedVar() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/cfg/bugs/unresolvedInvokeOnResolvedVar.kt"); + doTest(fileName); + } } @TestMetadata("compiler/testData/cfg/controlStructures")