Use "unnecessary variable" inspection in J2K
Unused variables are no more treated as unnecessary Related to KT-15958
This commit is contained in:
@@ -22,14 +22,13 @@ import com.intellij.codeInspection.ProblemHighlightType
|
|||||||
import com.intellij.codeInspection.ProblemsHolder
|
import com.intellij.codeInspection.ProblemsHolder
|
||||||
import com.intellij.openapi.project.Project
|
import com.intellij.openapi.project.Project
|
||||||
import com.intellij.psi.PsiElementVisitor
|
import com.intellij.psi.PsiElementVisitor
|
||||||
|
import com.intellij.psi.search.LocalSearchScope
|
||||||
|
import com.intellij.psi.search.searches.ReferencesSearch
|
||||||
import org.jetbrains.kotlin.descriptors.FunctionDescriptor
|
import org.jetbrains.kotlin.descriptors.FunctionDescriptor
|
||||||
import org.jetbrains.kotlin.descriptors.VariableDescriptor
|
import org.jetbrains.kotlin.descriptors.VariableDescriptor
|
||||||
import org.jetbrains.kotlin.idea.caches.resolve.analyze
|
import org.jetbrains.kotlin.idea.caches.resolve.analyze
|
||||||
import org.jetbrains.kotlin.idea.refactoring.inline.KotlinInlineValHandler
|
import org.jetbrains.kotlin.idea.refactoring.inline.KotlinInlineValHandler
|
||||||
import org.jetbrains.kotlin.psi.KtNameReferenceExpression
|
import org.jetbrains.kotlin.psi.*
|
||||||
import org.jetbrains.kotlin.psi.KtProperty
|
|
||||||
import org.jetbrains.kotlin.psi.KtReturnExpression
|
|
||||||
import org.jetbrains.kotlin.psi.KtVisitorVoid
|
|
||||||
import org.jetbrains.kotlin.psi.psiUtil.getNextSiblingIgnoringWhitespaceAndComments
|
import org.jetbrains.kotlin.psi.psiUtil.getNextSiblingIgnoringWhitespaceAndComments
|
||||||
import org.jetbrains.kotlin.psi.psiUtil.getParentOfType
|
import org.jetbrains.kotlin.psi.psiUtil.getParentOfType
|
||||||
import org.jetbrains.kotlin.resolve.BindingContext.DECLARATION_TO_DESCRIPTOR
|
import org.jetbrains.kotlin.resolve.BindingContext.DECLARATION_TO_DESCRIPTOR
|
||||||
@@ -42,45 +41,61 @@ class UnnecessaryVariableInspection : AbstractKotlinInspection() {
|
|||||||
override fun visitProperty(property: KtProperty) {
|
override fun visitProperty(property: KtProperty) {
|
||||||
super.visitProperty(property)
|
super.visitProperty(property)
|
||||||
|
|
||||||
if (!property.isLocal) return
|
|
||||||
val initializer = property.initializer ?: return
|
|
||||||
val nameIdentifier = property.nameIdentifier ?: return
|
val nameIdentifier = property.nameIdentifier ?: return
|
||||||
|
val status = statusFor(property) ?: return
|
||||||
|
holder.registerProblem(
|
||||||
|
nameIdentifier,
|
||||||
|
when (status) {
|
||||||
|
UnnecessaryVariableInspection.Companion.Status.RETURN_ONLY ->
|
||||||
|
"Variable used only in following return and can be inlined"
|
||||||
|
UnnecessaryVariableInspection.Companion.Status.EXACT_COPY ->
|
||||||
|
"Variable is an exact copy of another variable and can be inlined"
|
||||||
|
},
|
||||||
|
ProblemHighlightType.GENERIC_ERROR_OR_WARNING,
|
||||||
|
InlineVariableFix()
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (!property.isVar && initializer is KtNameReferenceExpression && property.typeReference == null) {
|
companion object {
|
||||||
val context = property.analyze()
|
private enum class Status {
|
||||||
val initializerDescriptor = context[REFERENCE_TARGET, initializer]
|
RETURN_ONLY,
|
||||||
if (initializerDescriptor is VariableDescriptor) {
|
EXACT_COPY
|
||||||
if (!initializerDescriptor.isVar &&
|
}
|
||||||
initializerDescriptor.containingDeclaration is FunctionDescriptor) {
|
|
||||||
holder.registerProblem(
|
|
||||||
nameIdentifier,
|
|
||||||
"Variable is an exact copy of another variable and can be inlined",
|
|
||||||
ProblemHighlightType.GENERIC_ERROR_OR_WARNING,
|
|
||||||
InlineVariableFix()
|
|
||||||
)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
val nextStatement = property.getNextSiblingIgnoringWhitespaceAndComments()
|
private fun statusFor(property: KtProperty): Status? {
|
||||||
if (nextStatement is KtReturnExpression) {
|
val enclosingElement = KtPsiUtil.getEnclosingElementForLocalDeclaration(property) ?: return null
|
||||||
val returned = nextStatement.returnedExpression
|
val initializer = property.initializer ?: return null
|
||||||
if (returned is KtNameReferenceExpression) {
|
|
||||||
val context = nextStatement.analyze()
|
if (!property.isVar && initializer is KtNameReferenceExpression && property.typeReference == null) {
|
||||||
if (context[REFERENCE_TARGET, returned] == context[DECLARATION_TO_DESCRIPTOR, property]) {
|
val context = property.analyze()
|
||||||
holder.registerProblem(
|
val initializerDescriptor = context[REFERENCE_TARGET, initializer]
|
||||||
nameIdentifier,
|
if (initializerDescriptor is VariableDescriptor) {
|
||||||
"Variable used only in following return and can be inlined",
|
if (!initializerDescriptor.isVar && initializerDescriptor.containingDeclaration is FunctionDescriptor) {
|
||||||
ProblemHighlightType.GENERIC_ERROR_OR_WARNING,
|
if (ReferencesSearch.search(property, LocalSearchScope(enclosingElement)).findFirst() != null) {
|
||||||
InlineVariableFix()
|
return Status.EXACT_COPY
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
val nextStatement = property.getNextSiblingIgnoringWhitespaceAndComments()
|
||||||
|
if (nextStatement is KtReturnExpression) {
|
||||||
|
val returned = nextStatement.returnedExpression
|
||||||
|
if (returned is KtNameReferenceExpression) {
|
||||||
|
val context = nextStatement.analyze()
|
||||||
|
if (context[REFERENCE_TARGET, returned] == context[DECLARATION_TO_DESCRIPTOR, property]) {
|
||||||
|
return Status.RETURN_ONLY
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
|
||||||
|
fun isActiveFor(property: KtProperty) = statusFor(property) != null
|
||||||
|
}
|
||||||
|
|
||||||
class InlineVariableFix : LocalQuickFix {
|
class InlineVariableFix : LocalQuickFix {
|
||||||
|
|
||||||
override fun getName() = "Inline variable"
|
override fun getName() = "Inline variable"
|
||||||
|
|||||||
@@ -26,7 +26,9 @@ import org.jetbrains.kotlin.idea.caches.resolve.analyze
|
|||||||
import org.jetbrains.kotlin.idea.core.replaced
|
import org.jetbrains.kotlin.idea.core.replaced
|
||||||
import org.jetbrains.kotlin.idea.core.setVisibility
|
import org.jetbrains.kotlin.idea.core.setVisibility
|
||||||
import org.jetbrains.kotlin.idea.inspections.RedundantSamConstructorInspection
|
import org.jetbrains.kotlin.idea.inspections.RedundantSamConstructorInspection
|
||||||
|
import org.jetbrains.kotlin.idea.inspections.UnnecessaryVariableInspection
|
||||||
import org.jetbrains.kotlin.idea.inspections.UseExpressionBodyInspection
|
import org.jetbrains.kotlin.idea.inspections.UseExpressionBodyInspection
|
||||||
|
import org.jetbrains.kotlin.idea.inspections.findExistingEditor
|
||||||
import org.jetbrains.kotlin.idea.intentions.*
|
import org.jetbrains.kotlin.idea.intentions.*
|
||||||
import org.jetbrains.kotlin.idea.intentions.branchedTransformations.intentions.FoldIfToReturnAsymmetricallyIntention
|
import org.jetbrains.kotlin.idea.intentions.branchedTransformations.intentions.FoldIfToReturnAsymmetricallyIntention
|
||||||
import org.jetbrains.kotlin.idea.intentions.branchedTransformations.intentions.FoldIfToReturnIntention
|
import org.jetbrains.kotlin.idea.intentions.branchedTransformations.intentions.FoldIfToReturnIntention
|
||||||
@@ -37,6 +39,7 @@ import org.jetbrains.kotlin.idea.intentions.conventionNameCalls.ReplaceGetOrSetI
|
|||||||
import org.jetbrains.kotlin.idea.intentions.conventionNameCalls.ReplaceGetOrSetIntention
|
import org.jetbrains.kotlin.idea.intentions.conventionNameCalls.ReplaceGetOrSetIntention
|
||||||
import org.jetbrains.kotlin.idea.quickfix.RemoveModifierFix
|
import org.jetbrains.kotlin.idea.quickfix.RemoveModifierFix
|
||||||
import org.jetbrains.kotlin.idea.quickfix.RemoveUselessCastFix
|
import org.jetbrains.kotlin.idea.quickfix.RemoveUselessCastFix
|
||||||
|
import org.jetbrains.kotlin.idea.refactoring.inline.KotlinInlineValHandler
|
||||||
import org.jetbrains.kotlin.idea.references.mainReference
|
import org.jetbrains.kotlin.idea.references.mainReference
|
||||||
import org.jetbrains.kotlin.idea.util.application.runWriteAction
|
import org.jetbrains.kotlin.idea.util.application.runWriteAction
|
||||||
import org.jetbrains.kotlin.lexer.KtTokens
|
import org.jetbrains.kotlin.lexer.KtTokens
|
||||||
@@ -80,6 +83,7 @@ object J2KPostProcessingRegistrar {
|
|||||||
_processings.add(RemoveRedundantSamAdaptersProcessing())
|
_processings.add(RemoveRedundantSamAdaptersProcessing())
|
||||||
_processings.add(RemoveRedundantCastToNullableProcessing())
|
_processings.add(RemoveRedundantCastToNullableProcessing())
|
||||||
_processings.add(UseExpressionBodyProcessing())
|
_processings.add(UseExpressionBodyProcessing())
|
||||||
|
_processings.add(UnnecessaryVariableProcessing())
|
||||||
|
|
||||||
registerIntentionBasedProcessing(FoldInitializerAndIfToElvisIntention())
|
registerIntentionBasedProcessing(FoldInitializerAndIfToElvisIntention())
|
||||||
|
|
||||||
@@ -269,6 +273,18 @@ object J2KPostProcessingRegistrar {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private class UnnecessaryVariableProcessing : J2kPostProcessing {
|
||||||
|
override fun createAction(element: KtElement, diagnostics: Diagnostics): (() -> Unit)? {
|
||||||
|
if (element !is KtProperty) return null
|
||||||
|
|
||||||
|
if (!UnnecessaryVariableInspection.isActiveFor(element)) return null
|
||||||
|
|
||||||
|
return {
|
||||||
|
KotlinInlineValHandler().inlineElement(element.project, element.findExistingEditor(), element)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private class RemoveRedundantCastToNullableProcessing : J2kPostProcessing {
|
private class RemoveRedundantCastToNullableProcessing : J2kPostProcessing {
|
||||||
override fun createAction(element: KtElement, diagnostics: Diagnostics): (() -> Unit)? {
|
override fun createAction(element: KtElement, diagnostics: Diagnostics): (() -> Unit)? {
|
||||||
if (element !is KtBinaryExpressionWithTypeRHS) return null
|
if (element !is KtBinaryExpressionWithTypeRHS) return null
|
||||||
|
|||||||
@@ -0,0 +1,6 @@
|
|||||||
|
// PROBLEM: none
|
||||||
|
|
||||||
|
fun test() {
|
||||||
|
val x = 1
|
||||||
|
val <caret>y = x
|
||||||
|
}
|
||||||
@@ -1472,6 +1472,12 @@ public class LocalInspectionTestGenerated extends AbstractLocalInspectionTest {
|
|||||||
doTest(fileName);
|
doTest(fileName);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@TestMetadata("copyOfValUnused.kt")
|
||||||
|
public void testCopyOfValUnused() throws Exception {
|
||||||
|
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/inspectionsLocal/unnecessaryVariable/copyOfValUnused.kt");
|
||||||
|
doTest(fileName);
|
||||||
|
}
|
||||||
|
|
||||||
@TestMetadata("copyOfValWithExplicitType.kt")
|
@TestMetadata("copyOfValWithExplicitType.kt")
|
||||||
public void testCopyOfValWithExplicitType() throws Exception {
|
public void testCopyOfValWithExplicitType() throws Exception {
|
||||||
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/inspectionsLocal/unnecessaryVariable/copyOfValWithExplicitType.kt");
|
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/inspectionsLocal/unnecessaryVariable/copyOfValWithExplicitType.kt");
|
||||||
|
|||||||
@@ -2,8 +2,7 @@ import java.util.HashSet
|
|||||||
|
|
||||||
internal class Foo {
|
internal class Foo {
|
||||||
fun foo(o: HashSet<*>) {
|
fun foo(o: HashSet<*>) {
|
||||||
val o2 = o
|
|
||||||
var foo = 0
|
var foo = 0
|
||||||
foo = o2.size
|
foo = o.size
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
+3
-4
@@ -1,6 +1,5 @@
|
|||||||
if (1 > 0) {
|
return if (1 > 0) {
|
||||||
val n = 1
|
1
|
||||||
return n
|
|
||||||
} else {
|
} else {
|
||||||
return 0
|
0
|
||||||
}
|
}
|
||||||
+1
-2
@@ -1,4 +1,3 @@
|
|||||||
if (1 > 0) {
|
if (1 > 0) {
|
||||||
val n = 1
|
return 1
|
||||||
return n
|
|
||||||
}
|
}
|
||||||
@@ -1,9 +1,8 @@
|
|||||||
internal class C {
|
internal class C {
|
||||||
fun foo(o: Any) {
|
fun foo(o: Any) {
|
||||||
if (o is String) {
|
if (o is String) {
|
||||||
val s = o
|
val l = o.length
|
||||||
val l = s.length
|
val substring = o.substring(l - 2)
|
||||||
val substring = s.substring(l - 2)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -8,8 +8,7 @@ internal class Test : Iterable<String> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fun push(i: Iterator<String>): Iterator<String> {
|
fun push(i: Iterator<String>): Iterator<String> {
|
||||||
val j = i
|
return i
|
||||||
return j
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -19,7 +18,6 @@ internal class FullTest : Iterable<String> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fun push(i: Iterator<String>): Iterator<String> {
|
fun push(i: Iterator<String>): Iterator<String> {
|
||||||
val j = i
|
return i
|
||||||
return j
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -10,8 +10,7 @@ internal class Test : Iterable<String> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fun push(i: Iterator<String>): Iterator<String> {
|
fun push(i: Iterator<String>): Iterator<String> {
|
||||||
val j = i
|
return i
|
||||||
return j
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -21,7 +20,6 @@ internal class FullTest : Iterable<String> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fun push(i: Iterator<String>): Iterator<String> {
|
fun push(i: Iterator<String>): Iterator<String> {
|
||||||
val j = i
|
return i
|
||||||
return j
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -7,7 +7,6 @@ internal class Test : Iterable<String> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fun push(i: Iterator<String>): Iterator<String> {
|
fun push(i: Iterator<String>): Iterator<String> {
|
||||||
val j = i
|
return i
|
||||||
return j
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -5,8 +5,7 @@ class C {
|
|||||||
try {
|
try {
|
||||||
ByteArrayInputStream(ByteArray(10)).use { stream ->
|
ByteArrayInputStream(ByteArray(10)).use { stream ->
|
||||||
// reading something
|
// reading something
|
||||||
val c = stream.read()
|
return stream.read()
|
||||||
return c
|
|
||||||
}
|
}
|
||||||
} catch (e: IOException) {
|
} catch (e: IOException) {
|
||||||
println(e)
|
println(e)
|
||||||
|
|||||||
Reference in New Issue
Block a user