For each parameter not used: handle char sequences correctly

An enhancement for KT-27209
This commit is contained in:
Mikhail Glukhikh
2018-12-11 16:40:53 +03:00
parent 833f564f0e
commit b143a1c8f7
8 changed files with 26 additions and 6 deletions
@@ -34,6 +34,7 @@ class ForEachParameterNotUsedInspection : AbstractKotlinInspection() {
private const val FOREACH_NAME = "forEach"
private val COLLECTIONS_FOREACH_FQNAME = FqName("kotlin.collections.$FOREACH_NAME")
private val SEQUENCES_FOREACH_FQNAME = FqName("kotlin.sequences.$FOREACH_NAME")
private val TEXT_FOREACH_FQNAME = FqName("kotlin.text.$FOREACH_NAME")
}
override fun buildVisitor(holder: ProblemsHolder, isOnTheFly: Boolean): PsiElementVisitor {
@@ -44,7 +45,7 @@ class ForEachParameterNotUsedInspection : AbstractKotlinInspection() {
if (lambda == null || lambda.functionLiteral.arrow != null) return
val context = it.analyze()
when (it.getResolvedCall(context)?.resultingDescriptor?.fqNameOrNull()) {
COLLECTIONS_FOREACH_FQNAME, SEQUENCES_FOREACH_FQNAME -> {
COLLECTIONS_FOREACH_FQNAME, SEQUENCES_FOREACH_FQNAME, TEXT_FOREACH_FQNAME -> {
val descriptor = context[BindingContext.FUNCTION, lambda.functionLiteral] ?: return
val iterableParameter = descriptor.valueParameters.singleOrNull() ?: return
@@ -83,7 +84,7 @@ class ForEachParameterNotUsedInspection : AbstractKotlinInspection() {
}
private class ReplaceWithRepeatFix : LocalQuickFix {
override fun getFamilyName() = "Replace with 'repeat(size)'"
override fun getFamilyName() = "Replace with 'repeat()'"
override fun applyFix(project: Project, descriptor: ProblemDescriptor) {
val callExpression = descriptor.psiElement.parent as? KtCallExpression ?: return
@@ -92,8 +93,10 @@ class ForEachParameterNotUsedInspection : AbstractKotlinInspection() {
val receiverClass =
receiverExpression.resolveToCall()?.resultingDescriptor?.returnType?.constructor?.declarationDescriptor as? ClassDescriptor
val collection = callExpression.builtIns.collection
val charSequence = callExpression.builtIns.charSequence
val sizeText = when {
receiverClass != null && DescriptorUtils.isSubclass(receiverClass, collection) -> "size"
receiverClass != null && DescriptorUtils.isSubclass(receiverClass, charSequence) -> "length"
else -> "count()"
}
val lambdaExpression = callExpression.lambdaArguments.singleOrNull()?.getArgumentExpression() ?: return
@@ -1,5 +1,5 @@
// WITH_RUNTIME
// FIX: Replace with 'repeat(size)'
// FIX: Replace with 'repeat()'
fun test(list: List<String>) {
list.for<caret>Each {}
@@ -1,5 +1,5 @@
// WITH_RUNTIME
// FIX: Replace with 'repeat(size)'
// FIX: Replace with 'repeat()'
fun test(list: List<String>) {
repeat(list.size) {}
@@ -1,5 +1,5 @@
// WITH_RUNTIME
// FIX: Replace with 'repeat(size)'
// FIX: Replace with 'repeat()'
fun test(sequence: Sequence<String>) {
sequence.for<caret>Each {}
@@ -1,5 +1,5 @@
// WITH_RUNTIME
// FIX: Replace with 'repeat(size)'
// FIX: Replace with 'repeat()'
fun test(sequence: Sequence<String>) {
repeat(sequence.count()) {}
@@ -0,0 +1,6 @@
// WITH_RUNTIME
// FIX: Replace with 'repeat()'
fun test(s: String) {
s.for<caret>Each {}
}
@@ -0,0 +1,6 @@
// WITH_RUNTIME
// FIX: Replace with 'repeat()'
fun test(s: String) {
repeat(s.length) {}
}
@@ -2557,6 +2557,11 @@ public class LocalInspectionTestGenerated extends AbstractLocalInspectionTest {
public void testSequence() throws Exception {
runTest("idea/testData/inspectionsLocal/forEachParameterNotUsed/sequence.kt");
}
@TestMetadata("string.kt")
public void testString() throws Exception {
runTest("idea/testData/inspectionsLocal/forEachParameterNotUsed/string.kt");
}
}
@TestMetadata("idea/testData/inspectionsLocal/ImplicitNullableNothingType")