For each parameter not used: handle char sequences correctly
An enhancement for KT-27209
This commit is contained in:
@@ -34,6 +34,7 @@ class ForEachParameterNotUsedInspection : AbstractKotlinInspection() {
|
|||||||
private const val FOREACH_NAME = "forEach"
|
private const val FOREACH_NAME = "forEach"
|
||||||
private val COLLECTIONS_FOREACH_FQNAME = FqName("kotlin.collections.$FOREACH_NAME")
|
private val COLLECTIONS_FOREACH_FQNAME = FqName("kotlin.collections.$FOREACH_NAME")
|
||||||
private val SEQUENCES_FOREACH_FQNAME = FqName("kotlin.sequences.$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 {
|
override fun buildVisitor(holder: ProblemsHolder, isOnTheFly: Boolean): PsiElementVisitor {
|
||||||
@@ -44,7 +45,7 @@ class ForEachParameterNotUsedInspection : AbstractKotlinInspection() {
|
|||||||
if (lambda == null || lambda.functionLiteral.arrow != null) return
|
if (lambda == null || lambda.functionLiteral.arrow != null) return
|
||||||
val context = it.analyze()
|
val context = it.analyze()
|
||||||
when (it.getResolvedCall(context)?.resultingDescriptor?.fqNameOrNull()) {
|
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 descriptor = context[BindingContext.FUNCTION, lambda.functionLiteral] ?: return
|
||||||
val iterableParameter = descriptor.valueParameters.singleOrNull() ?: return
|
val iterableParameter = descriptor.valueParameters.singleOrNull() ?: return
|
||||||
|
|
||||||
@@ -83,7 +84,7 @@ class ForEachParameterNotUsedInspection : AbstractKotlinInspection() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private class ReplaceWithRepeatFix : LocalQuickFix {
|
private class ReplaceWithRepeatFix : LocalQuickFix {
|
||||||
override fun getFamilyName() = "Replace with 'repeat(size)'"
|
override fun getFamilyName() = "Replace with 'repeat()'"
|
||||||
|
|
||||||
override fun applyFix(project: Project, descriptor: ProblemDescriptor) {
|
override fun applyFix(project: Project, descriptor: ProblemDescriptor) {
|
||||||
val callExpression = descriptor.psiElement.parent as? KtCallExpression ?: return
|
val callExpression = descriptor.psiElement.parent as? KtCallExpression ?: return
|
||||||
@@ -92,8 +93,10 @@ class ForEachParameterNotUsedInspection : AbstractKotlinInspection() {
|
|||||||
val receiverClass =
|
val receiverClass =
|
||||||
receiverExpression.resolveToCall()?.resultingDescriptor?.returnType?.constructor?.declarationDescriptor as? ClassDescriptor
|
receiverExpression.resolveToCall()?.resultingDescriptor?.returnType?.constructor?.declarationDescriptor as? ClassDescriptor
|
||||||
val collection = callExpression.builtIns.collection
|
val collection = callExpression.builtIns.collection
|
||||||
|
val charSequence = callExpression.builtIns.charSequence
|
||||||
val sizeText = when {
|
val sizeText = when {
|
||||||
receiverClass != null && DescriptorUtils.isSubclass(receiverClass, collection) -> "size"
|
receiverClass != null && DescriptorUtils.isSubclass(receiverClass, collection) -> "size"
|
||||||
|
receiverClass != null && DescriptorUtils.isSubclass(receiverClass, charSequence) -> "length"
|
||||||
else -> "count()"
|
else -> "count()"
|
||||||
}
|
}
|
||||||
val lambdaExpression = callExpression.lambdaArguments.singleOrNull()?.getArgumentExpression() ?: return
|
val lambdaExpression = callExpression.lambdaArguments.singleOrNull()?.getArgumentExpression() ?: return
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
// WITH_RUNTIME
|
// WITH_RUNTIME
|
||||||
// FIX: Replace with 'repeat(size)'
|
// FIX: Replace with 'repeat()'
|
||||||
|
|
||||||
fun test(list: List<String>) {
|
fun test(list: List<String>) {
|
||||||
list.for<caret>Each {}
|
list.for<caret>Each {}
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
// WITH_RUNTIME
|
// WITH_RUNTIME
|
||||||
// FIX: Replace with 'repeat(size)'
|
// FIX: Replace with 'repeat()'
|
||||||
|
|
||||||
fun test(list: List<String>) {
|
fun test(list: List<String>) {
|
||||||
repeat(list.size) {}
|
repeat(list.size) {}
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
// WITH_RUNTIME
|
// WITH_RUNTIME
|
||||||
// FIX: Replace with 'repeat(size)'
|
// FIX: Replace with 'repeat()'
|
||||||
|
|
||||||
fun test(sequence: Sequence<String>) {
|
fun test(sequence: Sequence<String>) {
|
||||||
sequence.for<caret>Each {}
|
sequence.for<caret>Each {}
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
// WITH_RUNTIME
|
// WITH_RUNTIME
|
||||||
// FIX: Replace with 'repeat(size)'
|
// FIX: Replace with 'repeat()'
|
||||||
|
|
||||||
fun test(sequence: Sequence<String>) {
|
fun test(sequence: Sequence<String>) {
|
||||||
repeat(sequence.count()) {}
|
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) {}
|
||||||
|
}
|
||||||
+5
@@ -2557,6 +2557,11 @@ public class LocalInspectionTestGenerated extends AbstractLocalInspectionTest {
|
|||||||
public void testSequence() throws Exception {
|
public void testSequence() throws Exception {
|
||||||
runTest("idea/testData/inspectionsLocal/forEachParameterNotUsed/sequence.kt");
|
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")
|
@TestMetadata("idea/testData/inspectionsLocal/ImplicitNullableNothingType")
|
||||||
|
|||||||
Reference in New Issue
Block a user