Unnecessary local variable: highlight with INFORMATION level when initializer has any multi-line blocks (#3359)

#KT-26752 Fixed
This commit is contained in:
Toshiaki Kameyama
2021-05-20 18:12:41 +09:00
committed by GitHub
parent e82857996f
commit b9a4b60b93
20 changed files with 273 additions and 0 deletions
@@ -16,8 +16,10 @@
package org.jetbrains.kotlin.idea.inspections
import com.intellij.codeInspection.ProblemHighlightType
import com.intellij.openapi.editor.Editor
import com.intellij.openapi.project.Project
import com.intellij.psi.impl.source.tree.LeafPsiElement
import com.intellij.psi.search.LocalSearchScope
import com.intellij.psi.search.searches.ReferencesSearch
import org.jetbrains.kotlin.descriptors.FunctionDescriptor
@@ -27,15 +29,18 @@ import org.jetbrains.kotlin.idea.caches.resolve.analyze
import org.jetbrains.kotlin.idea.caches.resolve.resolveToCall
import org.jetbrains.kotlin.idea.core.NewDeclarationNameValidator
import org.jetbrains.kotlin.idea.refactoring.inline.KotlinInlineValHandler
import org.jetbrains.kotlin.idea.util.getLineCount
import org.jetbrains.kotlin.idea.util.nameIdentifierTextRangeInThis
import org.jetbrains.kotlin.lexer.KtTokens
import org.jetbrains.kotlin.psi.*
import org.jetbrains.kotlin.psi.psiUtil.anyDescendantOfType
import org.jetbrains.kotlin.psi.psiUtil.getNextSiblingIgnoringWhitespaceAndComments
import org.jetbrains.kotlin.psi.psiUtil.getStrictParentOfType
import org.jetbrains.kotlin.resolve.BindingContext.DECLARATION_TO_DESCRIPTOR
import org.jetbrains.kotlin.resolve.BindingContext.REFERENCE_TARGET
import org.jetbrains.kotlin.resolve.DescriptorToSourceUtils
import org.jetbrains.kotlin.resolve.lazy.BodyResolveMode
import org.jetbrains.kotlin.utils.addToStdlib.safeAs
class UnnecessaryVariableInspection : AbstractApplicabilityBasedInspection<KtProperty>(KtProperty::class.java) {
@@ -43,6 +48,11 @@ class UnnecessaryVariableInspection : AbstractApplicabilityBasedInspection<KtPro
override fun inspectionHighlightRangeInElement(element: KtProperty) = element.nameIdentifierTextRangeInThis()
override fun inspectionHighlightType(element: KtProperty): ProblemHighlightType {
val hasMultiLineBlock = element.initializer?.hasMultiLineBlock() == true
return if (hasMultiLineBlock) ProblemHighlightType.INFORMATION else ProblemHighlightType.GENERIC_ERROR_OR_WARNING
}
override fun inspectionText(element: KtProperty) = when (statusFor(element)) {
Status.RETURN_ONLY -> KotlinBundle.message("variable.used.only.in.following.return.and.should.be.inlined")
Status.EXACT_COPY -> KotlinBundle.message(
@@ -60,6 +70,11 @@ class UnnecessaryVariableInspection : AbstractApplicabilityBasedInspection<KtPro
KotlinInlineValHandler(withPrompt = false).inlineElement(project, editor, element)
}
private fun LeafPsiElement.startsMultilineBlock(): Boolean =
node.elementType == KtTokens.LBRACE && parent.safeAs<KtExpression>()?.getLineCount()?.let { it > 1 } == true
private fun KtExpression.hasMultiLineBlock(): Boolean = anyDescendantOfType<LeafPsiElement> { it.startsMultilineBlock() }
companion object {
private enum class Status {
RETURN_ONLY,
@@ -0,0 +1,13 @@
// HIGHLIGHT: INFORMATION
fun test(b: Boolean): Int {
val <caret>result = if (b) {
foo()
} else {
bar()
}
return result
}
fun foo() = 1
fun bar() = 2
@@ -0,0 +1,12 @@
// HIGHLIGHT: INFORMATION
fun test(b: Boolean): Int {
return if (b) {
foo()
} else {
bar()
}
}
fun foo() = 1
fun bar() = 2
@@ -0,0 +1,9 @@
// HIGHLIGHT: GENERIC_ERROR_OR_WARNING
fun test(b: Boolean): Int {
val <caret>result = if (b) foo() else bar()
return result
}
fun foo() = 1
fun bar() = 2
@@ -0,0 +1,8 @@
// HIGHLIGHT: GENERIC_ERROR_OR_WARNING
fun test(b: Boolean): Int {
return if (b) foo() else bar()
}
fun foo() = 1
fun bar() = 2
@@ -0,0 +1,12 @@
// HIGHLIGHT: GENERIC_ERROR_OR_WARNING
fun test(b: Boolean): Int {
val <caret>result = if (b)
foo()
else
bar()
return result
}
fun foo() = 1
fun bar() = 2
@@ -0,0 +1,11 @@
// HIGHLIGHT: GENERIC_ERROR_OR_WARNING
fun test(b: Boolean): Int {
return if (b)
foo()
else
bar()
}
fun foo() = 1
fun bar() = 2
@@ -0,0 +1,14 @@
// HIGHLIGHT: GENERIC_ERROR_OR_WARNING
fun test(b: Boolean): Int {
val <caret>result = if (b)
baz { foo() }
else
baz { bar() }
return result
}
fun foo() = 1
fun bar() = 2
fun baz(f: () -> Int) = f()
@@ -0,0 +1,13 @@
// HIGHLIGHT: GENERIC_ERROR_OR_WARNING
fun test(b: Boolean): Int {
return if (b)
baz { foo() }
else
baz { bar() }
}
fun foo() = 1
fun bar() = 2
fun baz(f: () -> Int) = f()
@@ -0,0 +1,18 @@
// HIGHLIGHT: INFORMATION
fun test(b: Boolean): Int {
val <caret>result = if (b)
baz {
foo()
}
else
baz {
bar()
}
return result
}
fun foo() = 1
fun bar() = 2
fun baz(f: () -> Int) = f()
@@ -0,0 +1,17 @@
// HIGHLIGHT: INFORMATION
fun test(b: Boolean): Int {
return if (b)
baz {
foo()
}
else
baz {
bar()
}
}
fun foo() = 1
fun bar() = 2
fun baz(f: () -> Int) = f()
@@ -0,0 +1,9 @@
// HIGHLIGHT: GENERIC_ERROR_OR_WARNING
fun test(): Int {
val <caret>result = foo { bar() }
return result
}
fun foo(f: () -> Int) = f()
fun bar() = 1
@@ -0,0 +1,8 @@
// HIGHLIGHT: GENERIC_ERROR_OR_WARNING
fun test(): Int {
return foo { bar() }
}
fun foo(f: () -> Int) = f()
fun bar() = 1
@@ -0,0 +1,11 @@
// HIGHLIGHT: INFORMATION
fun test(): Int {
val <caret>result = foo {
bar()
}
return result
}
fun foo(f: () -> Int) = f()
fun bar() = 1
@@ -0,0 +1,10 @@
// HIGHLIGHT: INFORMATION
fun test(): Int {
return foo {
bar()
}
}
fun foo(f: () -> Int) = f()
fun bar() = 1
@@ -0,0 +1,13 @@
// HIGHLIGHT: INFORMATION
fun test(): Int {
val <caret>result = try {
foo()
} finally {
bar()
}
return result
}
fun foo() = 1
fun bar() {}
@@ -0,0 +1,12 @@
// HIGHLIGHT: INFORMATION
fun test(): Int {
return try {
foo()
} finally {
bar()
}
}
fun foo() = 1
fun bar() {}
@@ -0,0 +1,12 @@
// HIGHLIGHT: INFORMATION
fun test(i: Int): Int {
val <caret>result = when (i) {
1 -> foo()
else -> bar()
}
return result
}
fun foo() = 1
fun bar() = 2
@@ -0,0 +1,11 @@
// HIGHLIGHT: INFORMATION
fun test(i: Int): Int {
return when (i) {
1 -> foo()
else -> bar()
}
}
fun foo() = 1
fun bar() = 2
@@ -14083,11 +14083,46 @@ public class LocalInspectionTestGenerated extends AbstractLocalInspectionTest {
runTest("idea/testData/inspectionsLocal/unnecessaryVariable/copyOfVar.kt");
}
@TestMetadata("ifElse.kt")
public void testIfElse() throws Exception {
runTest("idea/testData/inspectionsLocal/unnecessaryVariable/ifElse.kt");
}
@TestMetadata("ifElse2.kt")
public void testIfElse2() throws Exception {
runTest("idea/testData/inspectionsLocal/unnecessaryVariable/ifElse2.kt");
}
@TestMetadata("ifElse3.kt")
public void testIfElse3() throws Exception {
runTest("idea/testData/inspectionsLocal/unnecessaryVariable/ifElse3.kt");
}
@TestMetadata("ifElse4.kt")
public void testIfElse4() throws Exception {
runTest("idea/testData/inspectionsLocal/unnecessaryVariable/ifElse4.kt");
}
@TestMetadata("ifElse5.kt")
public void testIfElse5() throws Exception {
runTest("idea/testData/inspectionsLocal/unnecessaryVariable/ifElse5.kt");
}
@TestMetadata("it.kt")
public void testIt() throws Exception {
runTest("idea/testData/inspectionsLocal/unnecessaryVariable/it.kt");
}
@TestMetadata("lambda.kt")
public void testLambda() throws Exception {
runTest("idea/testData/inspectionsLocal/unnecessaryVariable/lambda.kt");
}
@TestMetadata("lambda2.kt")
public void testLambda2() throws Exception {
runTest("idea/testData/inspectionsLocal/unnecessaryVariable/lambda2.kt");
}
@TestMetadata("override.kt")
public void testOverride() throws Exception {
runTest("idea/testData/inspectionsLocal/unnecessaryVariable/override.kt");
@@ -14118,11 +14153,21 @@ public class LocalInspectionTestGenerated extends AbstractLocalInspectionTest {
runTest("idea/testData/inspectionsLocal/unnecessaryVariable/topLevelCopy.kt");
}
@TestMetadata("try.kt")
public void testTry() throws Exception {
runTest("idea/testData/inspectionsLocal/unnecessaryVariable/try.kt");
}
@TestMetadata("varCopy.kt")
public void testVarCopy() throws Exception {
runTest("idea/testData/inspectionsLocal/unnecessaryVariable/varCopy.kt");
}
@TestMetadata("when.kt")
public void testWhen() throws Exception {
runTest("idea/testData/inspectionsLocal/unnecessaryVariable/when.kt");
}
@TestMetadata("whenSubject.kt")
public void testWhenSubject() throws Exception {
runTest("idea/testData/inspectionsLocal/unnecessaryVariable/whenSubject.kt");