Skip to content

Commit 33e34c7

Browse files
Evgeny GerashchenkoEvgeny Gerashchenko
authored andcommitted
KT-2190 Highlight usages invoked on "throw" or "return" should highlight all exit-points of function
#KT-2190 fixed
1 parent 86f91ad commit 33e34c7

File tree

17 files changed

+413
-0
lines changed

17 files changed

+413
-0
lines changed

generators/src/org/jetbrains/kotlin/generators/tests/GenerateTests.kt

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -67,6 +67,7 @@ import org.jetbrains.kotlin.idea.folding.AbstractKotlinFoldingTest
6767
import org.jetbrains.kotlin.idea.hierarchy.AbstractHierarchyTest
6868
import org.jetbrains.kotlin.idea.highlighter.AbstractDiagnosticMessageJsTest
6969
import org.jetbrains.kotlin.idea.highlighter.AbstractDiagnosticMessageTest
70+
import org.jetbrains.kotlin.idea.highlighter.AbstractHighlightExitPointsTest
7071
import org.jetbrains.kotlin.idea.highlighter.AbstractHighlightingTest
7172
import org.jetbrains.kotlin.idea.imports.AbstractOptimizeImportsTest
7273
import org.jetbrains.kotlin.idea.intentions.AbstractIntentionTest
@@ -595,6 +596,10 @@ fun main(args: Array<String>) {
595596
model("copyPaste/imports", pattern = """^([^\.]+)\.kt$""", testMethod = "doTestCut", testClassName = "Cut", recursive = false)
596597
}
597598

599+
testClass(javaClass<AbstractHighlightExitPointsTest>()) {
600+
model("exitPoints")
601+
}
602+
598603
testClass(javaClass<AbstractLineMarkersTest>()) {
599604
model("codeInsight/lineMarker")
600605
}

idea/src/META-INF/plugin.xml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -370,6 +370,7 @@
370370
<gotoTargetRendererProvider id="JetGotoTargetRenderProvider" implementation="org.jetbrains.kotlin.idea.JetGotoTargetRenderProvider"
371371
order="first"/>
372372
<elementDescriptionProvider implementation="org.jetbrains.kotlin.idea.findUsages.JetElementDescriptionProvider" order="first"/>
373+
<highlightUsagesHandlerFactory implementation="org.jetbrains.kotlin.idea.highlighter.KotlinHighlightExitPointsHandlerFactory"/>
373374
<findUsagesHandlerFactory implementation="org.jetbrains.kotlin.idea.findUsages.KotlinFindUsagesHandlerFactory"/>
374375
<usageTypeProvider implementation="org.jetbrains.kotlin.idea.findUsages.JetUsageTypeProvider"/>
375376
<refactoring.safeDeleteProcessor
Lines changed: 93 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,93 @@
1+
/*
2+
* Copyright 2010-2015 JetBrains s.r.o.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package org.jetbrains.kotlin.idea.highlighter
18+
19+
import com.intellij.codeInsight.highlighting.HighlightUsagesHandlerBase
20+
import com.intellij.codeInsight.highlighting.HighlightUsagesHandlerFactoryBase
21+
import com.intellij.openapi.editor.Editor
22+
import com.intellij.psi.PsiElement
23+
import com.intellij.psi.PsiFile
24+
import com.intellij.psi.impl.source.tree.LeafPsiElement
25+
import com.intellij.psi.tree.TokenSet
26+
import com.intellij.psi.util.PsiTreeUtil
27+
import com.intellij.util.Consumer
28+
import org.jetbrains.kotlin.idea.caches.resolve.analyze
29+
import org.jetbrains.kotlin.lexer.JetTokens
30+
import org.jetbrains.kotlin.psi.*
31+
import org.jetbrains.kotlin.psi.psiUtil.getNonStrictParentOfType
32+
import org.jetbrains.kotlin.psi.psiUtil.getParentOfType
33+
import org.jetbrains.kotlin.psi.psiUtil.getStrictParentOfType
34+
import org.jetbrains.kotlin.psi.psiUtil.parents
35+
import org.jetbrains.kotlin.resolve.calls.callUtil.isInlined
36+
37+
public class KotlinHighlightExitPointsHandlerFactory: HighlightUsagesHandlerFactoryBase() {
38+
companion object {
39+
private val RETURN_AND_THROW = TokenSet.create(JetTokens.RETURN_KEYWORD, JetTokens.THROW_KEYWORD)
40+
}
41+
42+
override fun createHighlightUsagesHandler(editor: Editor, file: PsiFile, target: PsiElement): HighlightUsagesHandlerBase<*>? {
43+
if (target is LeafPsiElement && (target.getElementType() in RETURN_AND_THROW)) {
44+
val returnOrThrow: JetExpression = PsiTreeUtil.getParentOfType(target, javaClass<JetReturnExpression>(), javaClass<JetThrowExpression>())
45+
return MyHandler(editor, file, returnOrThrow)
46+
}
47+
return null
48+
}
49+
50+
private class MyHandler(editor: Editor, file: PsiFile, val target: JetExpression) : HighlightUsagesHandlerBase<PsiElement>(editor, file) {
51+
override fun getTargets() = listOf(target)
52+
53+
override fun selectTargets(targets: MutableList<PsiElement>, selectionConsumer: Consumer<MutableList<PsiElement>>) {
54+
selectionConsumer.consume(targets)
55+
}
56+
57+
override fun computeUsages(targets: MutableList<PsiElement>?) {
58+
val relevantFunction = target.getRelevantFunction()
59+
relevantFunction?.accept(object : JetVisitorVoid() {
60+
override fun visitJetElement(element: JetElement) {
61+
element.acceptChildren(this)
62+
}
63+
64+
private fun visitReturnOrThrow(expression: JetExpression) {
65+
if (expression.getRelevantFunction() == relevantFunction) {
66+
addOccurrence(expression)
67+
}
68+
}
69+
70+
override fun visitReturnExpression(expression: JetReturnExpression) {
71+
visitReturnOrThrow(expression)
72+
}
73+
74+
override fun visitThrowExpression(expression: JetThrowExpression) {
75+
visitReturnOrThrow(expression)
76+
}
77+
})
78+
}
79+
}
80+
}
81+
82+
private fun JetExpression.getRelevantFunction(): JetFunction? {
83+
if (this is JetReturnExpression) {
84+
(this.getTargetLabel()?.getReference()?.resolve() as? JetFunction)?.let { return it }
85+
}
86+
for (parent in parents(false)) {
87+
when (parent) {
88+
is JetFunctionLiteral -> if (!parent.isInlined((parent.getParent() as JetFunctionLiteralExpression).analyze())) return parent
89+
is JetNamedFunction -> return parent
90+
}
91+
}
92+
return null
93+
}

idea/testData/exitPoints/inline1.kt

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
fun f(a: Int): Int {
2+
if (a < 5) {
3+
run {
4+
<caret>return 1
5+
}
6+
}
7+
else {
8+
return 2
9+
}
10+
}
11+
12+
inline public fun <T> run(f: () -> T): T { }
13+
14+
//HIGHLIGHTED: return 1
15+
//HIGHLIGHTED: return 2

idea/testData/exitPoints/inline2.kt

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
fun f(a: Int): Int {
2+
if (a < 5) {
3+
run {
4+
return 1
5+
}
6+
}
7+
else {
8+
<caret>return 2
9+
}
10+
}
11+
12+
inline public fun <T> run(f: () -> T): T { }
13+
14+
//HIGHLIGHTED: return 1
15+
//HIGHLIGHTED: return 2
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
fun f(a: Int): Int {
2+
if (a < 5) {
3+
run {
4+
<caret>return@run 1
5+
}
6+
}
7+
else {
8+
return 2
9+
}
10+
}
11+
12+
inline public fun <T> run(f: () -> T): T { }
13+
14+
//HIGHLIGHTED: return@run 1
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
fun f(a: Int): Int {
2+
if (a < 5) {
3+
run {
4+
return@run 1
5+
}
6+
}
7+
else {
8+
<caret>return 2
9+
}
10+
}
11+
12+
inline public fun <T> run(f: () -> T): T { }
13+
14+
//HIGHLIGHTED: return 2
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
fun f(a: Int): Int {
2+
fun localFun() {
3+
<caret>return
4+
}
5+
6+
if (a < 5) {
7+
return 1
8+
}
9+
else {
10+
return 2
11+
}
12+
}
13+
14+
//HIGHLIGHTED: return
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
fun f(a: Int): Int {
2+
fun localFun() {
3+
return
4+
}
5+
6+
if (a < 5) {
7+
return 1
8+
}
9+
else {
10+
<caret>return 2
11+
}
12+
}
13+
14+
//HIGHLIGHTED: return 1
15+
//HIGHLIGHTED: return 2
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
fun f(a: Int): Int {
2+
fun localFun() {
3+
if (a > 5) {
4+
return
5+
}
6+
<caret>throw Error()
7+
}
8+
9+
if (a < 5) {
10+
return 1
11+
}
12+
else {
13+
throw Exception()
14+
}
15+
}
16+
17+
//HIGHLIGHTED: return
18+
//HIGHLIGHTED: throw Error()
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
import javax.swing.SwingUtilities
2+
3+
fun f(a: Int): Int {
4+
if (a < 5) {
5+
SwingUtilities.invokeLater(fun (): Unit {
6+
<caret>return
7+
})
8+
return 1
9+
}
10+
else {
11+
return 2
12+
}
13+
}
14+
15+
//HIGHLIGHTED: return
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
import javax.swing.SwingUtilities
2+
3+
fun f(a: Int): Int {
4+
if (a < 5) {
5+
SwingUtilities.invokeLater(fun (): Unit {
6+
return
7+
})
8+
<caret>return 1
9+
}
10+
else {
11+
return 2
12+
}
13+
}
14+
15+
//HIGHLIGHTED: return 1
16+
//HIGHLIGHTED: return 2

idea/testData/exitPoints/simple.kt

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
fun f(a: Int): Int {
2+
if (a < 5) {
3+
<caret>return 1
4+
}
5+
else {
6+
return 2
7+
}
8+
}
9+
10+
//HIGHLIGHTED: return 1
11+
//HIGHLIGHTED: return 2

idea/testData/exitPoints/throw1.kt

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
fun f(a: Int): Int {
2+
if (a < 5) {
3+
return 1
4+
}
5+
else {
6+
<caret>throw Error()
7+
}
8+
}
9+
10+
//HIGHLIGHTED: return 1
11+
//HIGHLIGHTED: throw Error()

idea/testData/exitPoints/throw2.kt

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
fun f(a: Int): Int {
2+
if (a < 5) {
3+
<caret>return 1
4+
}
5+
else {
6+
throw Error()
7+
}
8+
}
9+
10+
//HIGHLIGHTED: return 1
11+
//HIGHLIGHTED: throw Error()
Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
/*
2+
* Copyright 2010-2015 JetBrains s.r.o.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package org.jetbrains.kotlin.idea.highlighter
18+
19+
import com.intellij.codeInsight.highlighting.HighlightUsagesHandler
20+
import com.intellij.testFramework.fixtures.LightCodeInsightFixtureTestCase
21+
import org.jetbrains.kotlin.test.InTextDirectivesUtils
22+
import kotlin.test.assertEquals
23+
24+
public abstract class AbstractHighlightExitPointsTest : LightCodeInsightFixtureTestCase() {
25+
public fun doTest(testDataPath: String) {
26+
myFixture.configureByFile(testDataPath)
27+
HighlightUsagesHandler.invoke(myFixture.getProject(), myFixture.getEditor(), myFixture.getFile());
28+
29+
val text = myFixture.getFile().getText()
30+
val expectedToBeHighlighted = InTextDirectivesUtils.findLinesWithPrefixesRemoved(text, "//HIGHLIGHTED:")
31+
val highlighters = myFixture.getEditor().getMarkupModel().getAllHighlighters()
32+
val actual = highlighters.map { text.substring(it.getStartOffset(), it.getEndOffset()) }
33+
assertEquals(expectedToBeHighlighted, actual)
34+
}
35+
}

0 commit comments

Comments
 (0)