Introduce inspection for determining if a forEach parameter is unused

#KT-22068 Fixed
This commit is contained in:
Dennis Cornwell
2018-07-29 23:35:23 +03:00
committed by Mikhail Glukhikh
parent c3b2d1829f
commit e81eee4cc1
14 changed files with 197 additions and 0 deletions
@@ -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>
+9
View File
@@ -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"
+9
View File
@@ -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"
+9
View File
@@ -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"
+9
View File
@@ -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"
+9
View File
@@ -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"
+9
View File
@@ -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"
+9
View File
@@ -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) {}
}
@@ -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>
@@ -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) {}
@@ -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");