KT-2190 Highlight usages invoked on "throw" or "return" should highlight all exit-points of function

#KT-2190 fixed
This commit is contained in:
Evgeny Gerashchenko
2015-04-02 22:38:12 +03:00
parent 86f91ade1a
commit 33e34c759d
17 changed files with 413 additions and 0 deletions
@@ -67,6 +67,7 @@ import org.jetbrains.kotlin.idea.folding.AbstractKotlinFoldingTest
import org.jetbrains.kotlin.idea.hierarchy.AbstractHierarchyTest
import org.jetbrains.kotlin.idea.highlighter.AbstractDiagnosticMessageJsTest
import org.jetbrains.kotlin.idea.highlighter.AbstractDiagnosticMessageTest
import org.jetbrains.kotlin.idea.highlighter.AbstractHighlightExitPointsTest
import org.jetbrains.kotlin.idea.highlighter.AbstractHighlightingTest
import org.jetbrains.kotlin.idea.imports.AbstractOptimizeImportsTest
import org.jetbrains.kotlin.idea.intentions.AbstractIntentionTest
@@ -595,6 +596,10 @@ fun main(args: Array<String>) {
model("copyPaste/imports", pattern = """^([^\.]+)\.kt$""", testMethod = "doTestCut", testClassName = "Cut", recursive = false)
}
testClass(javaClass<AbstractHighlightExitPointsTest>()) {
model("exitPoints")
}
testClass(javaClass<AbstractLineMarkersTest>()) {
model("codeInsight/lineMarker")
}
+1
View File
@@ -370,6 +370,7 @@
<gotoTargetRendererProvider id="JetGotoTargetRenderProvider" implementation="org.jetbrains.kotlin.idea.JetGotoTargetRenderProvider"
order="first"/>
<elementDescriptionProvider implementation="org.jetbrains.kotlin.idea.findUsages.JetElementDescriptionProvider" order="first"/>
<highlightUsagesHandlerFactory implementation="org.jetbrains.kotlin.idea.highlighter.KotlinHighlightExitPointsHandlerFactory"/>
<findUsagesHandlerFactory implementation="org.jetbrains.kotlin.idea.findUsages.KotlinFindUsagesHandlerFactory"/>
<usageTypeProvider implementation="org.jetbrains.kotlin.idea.findUsages.JetUsageTypeProvider"/>
<refactoring.safeDeleteProcessor
@@ -0,0 +1,93 @@
/*
* Copyright 2010-2015 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.jetbrains.kotlin.idea.highlighter
import com.intellij.codeInsight.highlighting.HighlightUsagesHandlerBase
import com.intellij.codeInsight.highlighting.HighlightUsagesHandlerFactoryBase
import com.intellij.openapi.editor.Editor
import com.intellij.psi.PsiElement
import com.intellij.psi.PsiFile
import com.intellij.psi.impl.source.tree.LeafPsiElement
import com.intellij.psi.tree.TokenSet
import com.intellij.psi.util.PsiTreeUtil
import com.intellij.util.Consumer
import org.jetbrains.kotlin.idea.caches.resolve.analyze
import org.jetbrains.kotlin.lexer.JetTokens
import org.jetbrains.kotlin.psi.*
import org.jetbrains.kotlin.psi.psiUtil.getNonStrictParentOfType
import org.jetbrains.kotlin.psi.psiUtil.getParentOfType
import org.jetbrains.kotlin.psi.psiUtil.getStrictParentOfType
import org.jetbrains.kotlin.psi.psiUtil.parents
import org.jetbrains.kotlin.resolve.calls.callUtil.isInlined
public class KotlinHighlightExitPointsHandlerFactory: HighlightUsagesHandlerFactoryBase() {
companion object {
private val RETURN_AND_THROW = TokenSet.create(JetTokens.RETURN_KEYWORD, JetTokens.THROW_KEYWORD)
}
override fun createHighlightUsagesHandler(editor: Editor, file: PsiFile, target: PsiElement): HighlightUsagesHandlerBase<*>? {
if (target is LeafPsiElement && (target.getElementType() in RETURN_AND_THROW)) {
val returnOrThrow: JetExpression = PsiTreeUtil.getParentOfType(target, javaClass<JetReturnExpression>(), javaClass<JetThrowExpression>())
return MyHandler(editor, file, returnOrThrow)
}
return null
}
private class MyHandler(editor: Editor, file: PsiFile, val target: JetExpression) : HighlightUsagesHandlerBase<PsiElement>(editor, file) {
override fun getTargets() = listOf(target)
override fun selectTargets(targets: MutableList<PsiElement>, selectionConsumer: Consumer<MutableList<PsiElement>>) {
selectionConsumer.consume(targets)
}
override fun computeUsages(targets: MutableList<PsiElement>?) {
val relevantFunction = target.getRelevantFunction()
relevantFunction?.accept(object : JetVisitorVoid() {
override fun visitJetElement(element: JetElement) {
element.acceptChildren(this)
}
private fun visitReturnOrThrow(expression: JetExpression) {
if (expression.getRelevantFunction() == relevantFunction) {
addOccurrence(expression)
}
}
override fun visitReturnExpression(expression: JetReturnExpression) {
visitReturnOrThrow(expression)
}
override fun visitThrowExpression(expression: JetThrowExpression) {
visitReturnOrThrow(expression)
}
})
}
}
}
private fun JetExpression.getRelevantFunction(): JetFunction? {
if (this is JetReturnExpression) {
(this.getTargetLabel()?.getReference()?.resolve() as? JetFunction)?.let { return it }
}
for (parent in parents(false)) {
when (parent) {
is JetFunctionLiteral -> if (!parent.isInlined((parent.getParent() as JetFunctionLiteralExpression).analyze())) return parent
is JetNamedFunction -> return parent
}
}
return null
}
+15
View File
@@ -0,0 +1,15 @@
fun f(a: Int): Int {
if (a < 5) {
run {
<caret>return 1
}
}
else {
return 2
}
}
inline public fun <T> run(f: () -> T): T { }
//HIGHLIGHTED: return 1
//HIGHLIGHTED: return 2
+15
View File
@@ -0,0 +1,15 @@
fun f(a: Int): Int {
if (a < 5) {
run {
return 1
}
}
else {
<caret>return 2
}
}
inline public fun <T> run(f: () -> T): T { }
//HIGHLIGHTED: return 1
//HIGHLIGHTED: return 2
@@ -0,0 +1,14 @@
fun f(a: Int): Int {
if (a < 5) {
run {
<caret>return@run 1
}
}
else {
return 2
}
}
inline public fun <T> run(f: () -> T): T { }
//HIGHLIGHTED: return@run 1
@@ -0,0 +1,14 @@
fun f(a: Int): Int {
if (a < 5) {
run {
return@run 1
}
}
else {
<caret>return 2
}
}
inline public fun <T> run(f: () -> T): T { }
//HIGHLIGHTED: return 2
@@ -0,0 +1,14 @@
fun f(a: Int): Int {
fun localFun() {
<caret>return
}
if (a < 5) {
return 1
}
else {
return 2
}
}
//HIGHLIGHTED: return
@@ -0,0 +1,15 @@
fun f(a: Int): Int {
fun localFun() {
return
}
if (a < 5) {
return 1
}
else {
<caret>return 2
}
}
//HIGHLIGHTED: return 1
//HIGHLIGHTED: return 2
@@ -0,0 +1,18 @@
fun f(a: Int): Int {
fun localFun() {
if (a > 5) {
return
}
<caret>throw Error()
}
if (a < 5) {
return 1
}
else {
throw Exception()
}
}
//HIGHLIGHTED: return
//HIGHLIGHTED: throw Error()
+15
View File
@@ -0,0 +1,15 @@
import javax.swing.SwingUtilities
fun f(a: Int): Int {
if (a < 5) {
SwingUtilities.invokeLater(fun (): Unit {
<caret>return
})
return 1
}
else {
return 2
}
}
//HIGHLIGHTED: return
+16
View File
@@ -0,0 +1,16 @@
import javax.swing.SwingUtilities
fun f(a: Int): Int {
if (a < 5) {
SwingUtilities.invokeLater(fun (): Unit {
return
})
<caret>return 1
}
else {
return 2
}
}
//HIGHLIGHTED: return 1
//HIGHLIGHTED: return 2
+11
View File
@@ -0,0 +1,11 @@
fun f(a: Int): Int {
if (a < 5) {
<caret>return 1
}
else {
return 2
}
}
//HIGHLIGHTED: return 1
//HIGHLIGHTED: return 2
+11
View File
@@ -0,0 +1,11 @@
fun f(a: Int): Int {
if (a < 5) {
return 1
}
else {
<caret>throw Error()
}
}
//HIGHLIGHTED: return 1
//HIGHLIGHTED: throw Error()
+11
View File
@@ -0,0 +1,11 @@
fun f(a: Int): Int {
if (a < 5) {
<caret>return 1
}
else {
throw Error()
}
}
//HIGHLIGHTED: return 1
//HIGHLIGHTED: throw Error()
@@ -0,0 +1,35 @@
/*
* Copyright 2010-2015 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.jetbrains.kotlin.idea.highlighter
import com.intellij.codeInsight.highlighting.HighlightUsagesHandler
import com.intellij.testFramework.fixtures.LightCodeInsightFixtureTestCase
import org.jetbrains.kotlin.test.InTextDirectivesUtils
import kotlin.test.assertEquals
public abstract class AbstractHighlightExitPointsTest : LightCodeInsightFixtureTestCase() {
public fun doTest(testDataPath: String) {
myFixture.configureByFile(testDataPath)
HighlightUsagesHandler.invoke(myFixture.getProject(), myFixture.getEditor(), myFixture.getFile());
val text = myFixture.getFile().getText()
val expectedToBeHighlighted = InTextDirectivesUtils.findLinesWithPrefixesRemoved(text, "//HIGHLIGHTED:")
val highlighters = myFixture.getEditor().getMarkupModel().getAllHighlighters()
val actual = highlighters.map { text.substring(it.getStartOffset(), it.getEndOffset()) }
assertEquals(expectedToBeHighlighted, actual)
}
}
@@ -0,0 +1,110 @@
/*
* Copyright 2010-2015 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.jetbrains.kotlin.idea.highlighter;
import com.intellij.testFramework.TestDataPath;
import org.jetbrains.kotlin.test.InnerTestClasses;
import org.jetbrains.kotlin.test.JUnit3RunnerWithInners;
import org.jetbrains.kotlin.test.JetTestUtils;
import org.jetbrains.kotlin.test.TestMetadata;
import org.junit.runner.RunWith;
import java.io.File;
import java.util.regex.Pattern;
/** This class is generated by {@link org.jetbrains.kotlin.generators.tests.TestsPackage}. DO NOT MODIFY MANUALLY */
@SuppressWarnings("all")
@TestMetadata("idea/testData/exitPoints")
@TestDataPath("$PROJECT_ROOT")
@RunWith(JUnit3RunnerWithInners.class)
public class HighlightExitPointsTestGenerated extends AbstractHighlightExitPointsTest {
public void testAllFilesPresentInExitPoints() throws Exception {
JetTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("idea/testData/exitPoints"), Pattern.compile("^(.+)\\.kt$"), true);
}
@TestMetadata("inline1.kt")
public void testInline1() throws Exception {
String fileName = JetTestUtils.navigationMetadata("idea/testData/exitPoints/inline1.kt");
doTest(fileName);
}
@TestMetadata("inline2.kt")
public void testInline2() throws Exception {
String fileName = JetTestUtils.navigationMetadata("idea/testData/exitPoints/inline2.kt");
doTest(fileName);
}
@TestMetadata("inlineLocalReturn1.kt")
public void testInlineLocalReturn1() throws Exception {
String fileName = JetTestUtils.navigationMetadata("idea/testData/exitPoints/inlineLocalReturn1.kt");
doTest(fileName);
}
@TestMetadata("inlineLocalReturn2.kt")
public void testInlineLocalReturn2() throws Exception {
String fileName = JetTestUtils.navigationMetadata("idea/testData/exitPoints/inlineLocalReturn2.kt");
doTest(fileName);
}
@TestMetadata("localFunction1.kt")
public void testLocalFunction1() throws Exception {
String fileName = JetTestUtils.navigationMetadata("idea/testData/exitPoints/localFunction1.kt");
doTest(fileName);
}
@TestMetadata("localFunction2.kt")
public void testLocalFunction2() throws Exception {
String fileName = JetTestUtils.navigationMetadata("idea/testData/exitPoints/localFunction2.kt");
doTest(fileName);
}
@TestMetadata("localFunctionThrow.kt")
public void testLocalFunctionThrow() throws Exception {
String fileName = JetTestUtils.navigationMetadata("idea/testData/exitPoints/localFunctionThrow.kt");
doTest(fileName);
}
@TestMetadata("notInline1.kt")
public void testNotInline1() throws Exception {
String fileName = JetTestUtils.navigationMetadata("idea/testData/exitPoints/notInline1.kt");
doTest(fileName);
}
@TestMetadata("notInline2.kt")
public void testNotInline2() throws Exception {
String fileName = JetTestUtils.navigationMetadata("idea/testData/exitPoints/notInline2.kt");
doTest(fileName);
}
@TestMetadata("simple.kt")
public void testSimple() throws Exception {
String fileName = JetTestUtils.navigationMetadata("idea/testData/exitPoints/simple.kt");
doTest(fileName);
}
@TestMetadata("throw1.kt")
public void testThrow1() throws Exception {
String fileName = JetTestUtils.navigationMetadata("idea/testData/exitPoints/throw1.kt");
doTest(fileName);
}
@TestMetadata("throw2.kt")
public void testThrow2() throws Exception {
String fileName = JetTestUtils.navigationMetadata("idea/testData/exitPoints/throw2.kt");
doTest(fileName);
}
}