Introduce inspection for determining if a forEach parameter is unused
#KT-22068 Fixed
This commit is contained in:
committed by
Mikhail Glukhikh
parent
c3b2d1829f
commit
e81eee4cc1
@@ -0,0 +1,5 @@
|
||||
<html>
|
||||
<body>
|
||||
This inspection guards against useless forEach loops by identifying when the iterable's values are not used.
|
||||
</body>
|
||||
</html>
|
||||
@@ -2924,6 +2924,15 @@ The Kotlin plugin provides language support in IntelliJ IDEA and Android Studio.
|
||||
language="kotlin"
|
||||
/>
|
||||
|
||||
<localInspection implementationClass="org.jetbrains.kotlin.idea.inspections.ForEachParameterNotUsedInspection"
|
||||
displayName="Iterated elements are not used in forEach"
|
||||
groupPath="Kotlin"
|
||||
groupName="Probable bugs"
|
||||
enabledByDefault="true"
|
||||
level="WARNING"
|
||||
language="kotlin"
|
||||
/>
|
||||
|
||||
<localInspection implementationClass="org.jetbrains.kotlin.idea.inspections.NestedLambdaShadowedImplicitParameterInspection"
|
||||
displayName="Nested lambda has shadowed implicit parameter"
|
||||
groupPath="Kotlin"
|
||||
|
||||
@@ -2923,6 +2923,15 @@ The Kotlin plugin provides language support in IntelliJ IDEA and Android Studio.
|
||||
language="kotlin"
|
||||
/>
|
||||
|
||||
<localInspection implementationClass="org.jetbrains.kotlin.idea.inspections.ForEachParameterNotUsedInspection"
|
||||
displayName="Iterated elements are not used in forEach"
|
||||
groupPath="Kotlin"
|
||||
groupName="Probable bugs"
|
||||
enabledByDefault="true"
|
||||
level="WARNING"
|
||||
language="kotlin"
|
||||
/>
|
||||
|
||||
<localInspection implementationClass="org.jetbrains.kotlin.idea.inspections.NestedLambdaShadowedImplicitParameterInspection"
|
||||
displayName="Nested lambda has shadowed implicit parameter"
|
||||
groupPath="Kotlin"
|
||||
|
||||
@@ -2923,6 +2923,15 @@ The Kotlin plugin provides language support in IntelliJ IDEA and Android Studio.
|
||||
language="kotlin"
|
||||
/>
|
||||
|
||||
<localInspection implementationClass="org.jetbrains.kotlin.idea.inspections.ForEachParameterNotUsedInspection"
|
||||
displayName="Iterated elements are not used in forEach"
|
||||
groupPath="Kotlin"
|
||||
groupName="Probable bugs"
|
||||
enabledByDefault="true"
|
||||
level="WARNING"
|
||||
language="kotlin"
|
||||
/>
|
||||
|
||||
<localInspection implementationClass="org.jetbrains.kotlin.idea.inspections.NestedLambdaShadowedImplicitParameterInspection"
|
||||
displayName="Nested lambda has shadowed implicit parameter"
|
||||
groupPath="Kotlin"
|
||||
|
||||
@@ -2924,6 +2924,15 @@ The Kotlin plugin provides language support in IntelliJ IDEA and Android Studio.
|
||||
language="kotlin"
|
||||
/>
|
||||
|
||||
<localInspection implementationClass="org.jetbrains.kotlin.idea.inspections.ForEachParameterNotUsedInspection"
|
||||
displayName="Iterated elements are not used in forEach"
|
||||
groupPath="Kotlin"
|
||||
groupName="Probable bugs"
|
||||
enabledByDefault="true"
|
||||
level="WARNING"
|
||||
language="kotlin"
|
||||
/>
|
||||
|
||||
<localInspection implementationClass="org.jetbrains.kotlin.idea.inspections.NestedLambdaShadowedImplicitParameterInspection"
|
||||
displayName="Nested lambda has shadowed implicit parameter"
|
||||
groupPath="Kotlin"
|
||||
|
||||
@@ -2923,6 +2923,15 @@ The Kotlin plugin provides language support in IntelliJ IDEA and Android Studio.
|
||||
language="kotlin"
|
||||
/>
|
||||
|
||||
<localInspection implementationClass="org.jetbrains.kotlin.idea.inspections.ForEachParameterNotUsedInspection"
|
||||
displayName="Iterated elements are not used in forEach"
|
||||
groupPath="Kotlin"
|
||||
groupName="Probable bugs"
|
||||
enabledByDefault="true"
|
||||
level="WARNING"
|
||||
language="kotlin"
|
||||
/>
|
||||
|
||||
<localInspection implementationClass="org.jetbrains.kotlin.idea.inspections.NestedLambdaShadowedImplicitParameterInspection"
|
||||
displayName="Nested lambda has shadowed implicit parameter"
|
||||
groupPath="Kotlin"
|
||||
|
||||
@@ -2923,6 +2923,15 @@ The Kotlin plugin provides language support in IntelliJ IDEA and Android Studio.
|
||||
language="kotlin"
|
||||
/>
|
||||
|
||||
<localInspection implementationClass="org.jetbrains.kotlin.idea.inspections.ForEachParameterNotUsedInspection"
|
||||
displayName="Iterated elements are not used in forEach"
|
||||
groupPath="Kotlin"
|
||||
groupName="Probable bugs"
|
||||
enabledByDefault="true"
|
||||
level="WARNING"
|
||||
language="kotlin"
|
||||
/>
|
||||
|
||||
<localInspection implementationClass="org.jetbrains.kotlin.idea.inspections.NestedLambdaShadowedImplicitParameterInspection"
|
||||
displayName="Nested lambda has shadowed implicit parameter"
|
||||
groupPath="Kotlin"
|
||||
|
||||
@@ -2924,6 +2924,15 @@ The Kotlin plugin provides language support in IntelliJ IDEA and Android Studio.
|
||||
language="kotlin"
|
||||
/>
|
||||
|
||||
<localInspection implementationClass="org.jetbrains.kotlin.idea.inspections.ForEachParameterNotUsedInspection"
|
||||
displayName="Iterated elements are not used in forEach"
|
||||
groupPath="Kotlin"
|
||||
groupName="Probable bugs"
|
||||
enabledByDefault="true"
|
||||
level="WARNING"
|
||||
language="kotlin"
|
||||
/>
|
||||
|
||||
<localInspection implementationClass="org.jetbrains.kotlin.idea.inspections.NestedLambdaShadowedImplicitParameterInspection"
|
||||
displayName="Nested lambda has shadowed implicit parameter"
|
||||
groupPath="Kotlin"
|
||||
|
||||
@@ -0,0 +1,67 @@
|
||||
/*
|
||||
* Copyright 2010-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license
|
||||
* that can be found in the license/LICENSE.txt file.
|
||||
*/
|
||||
|
||||
package org.jetbrains.kotlin.idea.inspections
|
||||
|
||||
import com.intellij.codeInspection.ProblemHighlightType
|
||||
import com.intellij.codeInspection.ProblemsHolder
|
||||
import com.intellij.psi.PsiElementVisitor
|
||||
import org.jetbrains.kotlin.descriptors.VariableDescriptor
|
||||
import org.jetbrains.kotlin.idea.caches.resolve.analyze
|
||||
import org.jetbrains.kotlin.idea.intentions.getCallableDescriptor
|
||||
import org.jetbrains.kotlin.idea.refactoring.getThisLabelName
|
||||
import org.jetbrains.kotlin.name.FqName
|
||||
import org.jetbrains.kotlin.psi.*
|
||||
import org.jetbrains.kotlin.resolve.BindingContext
|
||||
import org.jetbrains.kotlin.resolve.calls.callUtil.getResolvedCall
|
||||
import org.jetbrains.kotlin.resolve.descriptorUtil.fqNameOrNull
|
||||
|
||||
class ForEachParameterNotUsedInspection : AbstractKotlinInspection() {
|
||||
companion object {
|
||||
private val COLLECTIONS_FOREACH_FQNAME = FqName("kotlin.collections.forEach")
|
||||
private val SEQUENCES_FOREACH_FQNAME = FqName("kotlin.sequences.forEach")
|
||||
}
|
||||
|
||||
override fun buildVisitor(holder: ProblemsHolder, isOnTheFly: Boolean): PsiElementVisitor {
|
||||
return callExpressionVisitor {
|
||||
val calleeExpression = it.calleeExpression as? KtNameReferenceExpression ?: return@callExpressionVisitor
|
||||
if (calleeExpression.getReferencedName() != "forEach") return@callExpressionVisitor
|
||||
when (it.getCallableDescriptor()?.fqNameOrNull()) {
|
||||
COLLECTIONS_FOREACH_FQNAME, SEQUENCES_FOREACH_FQNAME -> {
|
||||
val lambda = it.lambdaArguments.singleOrNull()?.getLambdaExpression() ?: return@callExpressionVisitor
|
||||
val descriptor = lambda.analyze()[BindingContext.FUNCTION, lambda.functionLiteral] ?: return@callExpressionVisitor
|
||||
val iterableParameter = descriptor.valueParameters.singleOrNull() ?: return@callExpressionVisitor
|
||||
|
||||
if (lambda.bodyExpression?.usesDescriptor(iterableParameter) != true && it.calleeExpression != null) {
|
||||
holder.registerProblem(
|
||||
it.calleeExpression!!,
|
||||
"Loop parameter '${iterableParameter.getThisLabelName()}' is unused",
|
||||
ProblemHighlightType.GENERIC_ERROR_OR_WARNING
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun KtBlockExpression.usesDescriptor(descriptor: VariableDescriptor): Boolean {
|
||||
var used = false
|
||||
acceptChildren(object : KtVisitorVoid() {
|
||||
override fun visitKtElement(element: KtElement) {
|
||||
if (!used) {
|
||||
if (element.children.isNotEmpty()) {
|
||||
element.acceptChildren(this)
|
||||
} else {
|
||||
val bindingContext = element.analyze()
|
||||
val resolvedCall = element.getResolvedCall(bindingContext) ?: return
|
||||
|
||||
used = resolvedCall.candidateDescriptor == descriptor
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
return used
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
fun test() {
|
||||
val items = sequenceOf<Any>()
|
||||
items.forEach {
|
||||
ForEachable().forEach {}
|
||||
}
|
||||
}
|
||||
|
||||
class ForEachable {
|
||||
fun forEach(action: (ForEachable) -> Unit) {}
|
||||
}
|
||||
+34
@@ -0,0 +1,34 @@
|
||||
<problems>
|
||||
<problem>
|
||||
<file>test.kt</file>
|
||||
<line>3</line>
|
||||
<module>light_idea_test_case</module>
|
||||
<entry_point TYPE="file" FQNAME="temp:///src/test.kt" />
|
||||
<problem_class severity="WARNING" attribute_key="WARNING_ATTRIBUTES">Iterated elements are not used in forEach</problem_class>
|
||||
<description>Loop parameter 'it' is unused</description>
|
||||
</problem>
|
||||
<problem>
|
||||
<file>test.kt</file>
|
||||
<line>4</line>
|
||||
<module>light_idea_test_case</module>
|
||||
<entry_point TYPE="file" FQNAME="temp:///src/test.kt" />
|
||||
<problem_class severity="WARNING" attribute_key="WARNING_ATTRIBUTES">Iterated elements are not used in forEach</problem_class>
|
||||
<description>Loop parameter 'item' is unused</description>
|
||||
</problem>
|
||||
<problem>
|
||||
<file>test.kt</file>
|
||||
<line>7</line>
|
||||
<module>light_idea_test_case</module>
|
||||
<entry_point TYPE="file" FQNAME="temp:///src/test.kt" />
|
||||
<problem_class severity="WARNING" attribute_key="WARNING_ATTRIBUTES">Iterated elements are not used in forEach</problem_class>
|
||||
<description>Loop parameter 'it' is unused</description>
|
||||
</problem>
|
||||
<problem>
|
||||
<file>correctReference.kt</file>
|
||||
<line>3</line>
|
||||
<module>light_idea_test_case</module>
|
||||
<entry_point TYPE="file" FQNAME="temp:///src/correctReference.kt" />
|
||||
<problem_class severity="WARNING" attribute_key="WARNING_ATTRIBUTES">Iterated elements are not used in forEach</problem_class>
|
||||
<description>Loop parameter 'it' is unused</description>
|
||||
</problem>
|
||||
</problems>
|
||||
+2
@@ -0,0 +1,2 @@
|
||||
// INSPECTION_CLASS: org.jetbrains.kotlin.idea.inspections.ForEachParameterNotUsedInspection
|
||||
// WITH_RUNTIME
|
||||
@@ -0,0 +1,11 @@
|
||||
fun test() {
|
||||
val items = listOf<Any>()
|
||||
items.forEach { }
|
||||
items.forEach { item -> }
|
||||
items.forEach { doSomething(it) }
|
||||
items.forEach { item -> doSomething(item) }
|
||||
items.forEach { items.forEach { doSomething(it) } }
|
||||
items.forEach { items.forEach { thing -> doSomething(it); doSomething(thing) } }
|
||||
}
|
||||
|
||||
fun doSomething(item: Any) {}
|
||||
+5
@@ -169,6 +169,11 @@ public class InspectionTestGenerated extends AbstractInspectionTest {
|
||||
runTest("idea/testData/inspections/equalsAndHashCode/inspectionData/inspections.test");
|
||||
}
|
||||
|
||||
@TestMetadata("forEachParameterNotUsed/inspectionData/inspections.test")
|
||||
public void testForEachParameterNotUsed_inspectionData_Inspections_test() throws Exception {
|
||||
runTest("idea/testData/inspections/forEachParameterNotUsed/inspectionData/inspections.test");
|
||||
}
|
||||
|
||||
@TestMetadata("gradleWrongPluginVersion/inspectionData/inspections.test")
|
||||
public void testGradleWrongPluginVersion_inspectionData_Inspections_test() throws Exception {
|
||||
runTest("idea/testData/inspections/gradleWrongPluginVersion/inspectionData/inspections.test");
|
||||
|
||||
Reference in New Issue
Block a user