Check that add() method is in fact MutableCollection.add()

#KT-18881 Fixed
This commit is contained in:
Dmitry Jemerov
2017-12-14 18:25:25 +01:00
parent 94b8614fb8
commit 64f01e53af
4 changed files with 47 additions and 2 deletions
@@ -17,18 +17,23 @@
package org.jetbrains.kotlin.idea.intentions.loopToCallChain.result
import org.jetbrains.kotlin.builtins.KotlinBuiltIns
import org.jetbrains.kotlin.descriptors.CallableDescriptor
import org.jetbrains.kotlin.descriptors.ClassDescriptor
import org.jetbrains.kotlin.descriptors.DeclarationDescriptor
import org.jetbrains.kotlin.descriptors.VariableDescriptor
import org.jetbrains.kotlin.idea.caches.resolve.unsafeResolveToDescriptor
import org.jetbrains.kotlin.idea.imports.importableFqName
import org.jetbrains.kotlin.idea.intentions.loopToCallChain.*
import org.jetbrains.kotlin.idea.intentions.loopToCallChain.sequence.*
import org.jetbrains.kotlin.idea.references.resolveMainReferenceToDescriptors
import org.jetbrains.kotlin.idea.util.IdeDescriptorRenderers
import org.jetbrains.kotlin.lexer.KtTokens
import org.jetbrains.kotlin.name.FqName
import org.jetbrains.kotlin.psi.*
import org.jetbrains.kotlin.psi.psiUtil.getCallNameExpression
import org.jetbrains.kotlin.renderer.render
import org.jetbrains.kotlin.resolve.descriptorUtil.fqNameSafe
import org.jetbrains.kotlin.resolve.descriptorUtil.overriddenTreeUniqueAsSequence
class AddToCollectionTransformation(
loop: KtForExpression,
@@ -96,8 +101,11 @@ class AddToCollectionTransformation(
val qualifiedExpression = statement as? KtDotQualifiedExpression ?: return null
val targetCollection = qualifiedExpression.receiverExpression
val callExpression = qualifiedExpression.selectorExpression as? KtCallExpression ?: return null
if (callExpression.getCallNameExpression()?.getReferencedName() != "add") return null
//TODO: check that it's MutableCollection's add
val callNameExpression = callExpression.getCallNameExpression() ?: return null
if (callNameExpression.getReferencedName() != "add") return null
val calleeMethodDescriptors = callNameExpression.resolveMainReferenceToDescriptors()
if (calleeMethodDescriptors.none { it.isCollectionAdd() }) return null
val argument = callExpression.valueArguments.singleOrNull() ?: return null
val argumentValue = argument.getArgumentExpression() ?: return null
@@ -204,6 +212,17 @@ class AddToCollectionTransformation(
private fun KtExpression?.isRangeLiteral() = this is KtBinaryExpression && operationToken == KtTokens.RANGE
private fun DeclarationDescriptor.isCollectionAdd(): Boolean {
if ((containingDeclaration as? ClassDescriptor)?.fqNameSafe == KotlinBuiltIns.FQ_NAMES.mutableCollection) {
return true
}
val overrides = (this as? CallableDescriptor)?.overriddenTreeUniqueAsSequence(true) ?: return false
return overrides.any {
(it.containingDeclaration as? ClassDescriptor)?.fqNameSafe == KotlinBuiltIns.FQ_NAMES.mutableCollection
}
}
class FilterToTransformation private constructor(
loop: KtForExpression,
private val inputVariable: KtCallableDeclaration,
@@ -0,0 +1,14 @@
// WITH_RUNTIME
// IS_APPLICABLE: false
// IS_APPLICABLE_2: false
data class Node(private val data: MutableList<String>) {
fun add(word: String) {
data.add(word)
}
}
fun foo(node: Node, words: List<String>) {
<caret>for (word in words) {
node.add(word)
}
}
@@ -1550,6 +1550,12 @@ public class IntentionTest2Generated extends AbstractIntentionTest2 {
doTest(fileName);
}
@TestMetadata("KT18881.kt")
public void testKT18881() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/intentions/loopToCallChain/toCollection/KT18881.kt");
doTest(fileName);
}
@TestMetadata("resultCollectionUsedInsideLoop.kt")
public void testResultCollectionUsedInsideLoop() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/intentions/loopToCallChain/toCollection/resultCollectionUsedInsideLoop.kt");
@@ -11461,6 +11461,12 @@ public class IntentionTestGenerated extends AbstractIntentionTest {
doTest(fileName);
}
@TestMetadata("KT18881.kt")
public void testKT18881() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/intentions/loopToCallChain/toCollection/KT18881.kt");
doTest(fileName);
}
@TestMetadata("resultCollectionUsedInsideLoop.kt")
public void testResultCollectionUsedInsideLoop() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/intentions/loopToCallChain/toCollection/resultCollectionUsedInsideLoop.kt");