Check that add() method is in fact MutableCollection.add()
#KT-18881 Fixed
This commit is contained in:
+21
-2
@@ -17,18 +17,23 @@
|
|||||||
package org.jetbrains.kotlin.idea.intentions.loopToCallChain.result
|
package org.jetbrains.kotlin.idea.intentions.loopToCallChain.result
|
||||||
|
|
||||||
import org.jetbrains.kotlin.builtins.KotlinBuiltIns
|
import org.jetbrains.kotlin.builtins.KotlinBuiltIns
|
||||||
|
import org.jetbrains.kotlin.descriptors.CallableDescriptor
|
||||||
import org.jetbrains.kotlin.descriptors.ClassDescriptor
|
import org.jetbrains.kotlin.descriptors.ClassDescriptor
|
||||||
|
import org.jetbrains.kotlin.descriptors.DeclarationDescriptor
|
||||||
import org.jetbrains.kotlin.descriptors.VariableDescriptor
|
import org.jetbrains.kotlin.descriptors.VariableDescriptor
|
||||||
import org.jetbrains.kotlin.idea.caches.resolve.unsafeResolveToDescriptor
|
import org.jetbrains.kotlin.idea.caches.resolve.unsafeResolveToDescriptor
|
||||||
import org.jetbrains.kotlin.idea.imports.importableFqName
|
import org.jetbrains.kotlin.idea.imports.importableFqName
|
||||||
import org.jetbrains.kotlin.idea.intentions.loopToCallChain.*
|
import org.jetbrains.kotlin.idea.intentions.loopToCallChain.*
|
||||||
import org.jetbrains.kotlin.idea.intentions.loopToCallChain.sequence.*
|
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.idea.util.IdeDescriptorRenderers
|
||||||
import org.jetbrains.kotlin.lexer.KtTokens
|
import org.jetbrains.kotlin.lexer.KtTokens
|
||||||
import org.jetbrains.kotlin.name.FqName
|
import org.jetbrains.kotlin.name.FqName
|
||||||
import org.jetbrains.kotlin.psi.*
|
import org.jetbrains.kotlin.psi.*
|
||||||
import org.jetbrains.kotlin.psi.psiUtil.getCallNameExpression
|
import org.jetbrains.kotlin.psi.psiUtil.getCallNameExpression
|
||||||
import org.jetbrains.kotlin.renderer.render
|
import org.jetbrains.kotlin.renderer.render
|
||||||
|
import org.jetbrains.kotlin.resolve.descriptorUtil.fqNameSafe
|
||||||
|
import org.jetbrains.kotlin.resolve.descriptorUtil.overriddenTreeUniqueAsSequence
|
||||||
|
|
||||||
class AddToCollectionTransformation(
|
class AddToCollectionTransformation(
|
||||||
loop: KtForExpression,
|
loop: KtForExpression,
|
||||||
@@ -96,8 +101,11 @@ class AddToCollectionTransformation(
|
|||||||
val qualifiedExpression = statement as? KtDotQualifiedExpression ?: return null
|
val qualifiedExpression = statement as? KtDotQualifiedExpression ?: return null
|
||||||
val targetCollection = qualifiedExpression.receiverExpression
|
val targetCollection = qualifiedExpression.receiverExpression
|
||||||
val callExpression = qualifiedExpression.selectorExpression as? KtCallExpression ?: return null
|
val callExpression = qualifiedExpression.selectorExpression as? KtCallExpression ?: return null
|
||||||
if (callExpression.getCallNameExpression()?.getReferencedName() != "add") return null
|
val callNameExpression = callExpression.getCallNameExpression() ?: return null
|
||||||
//TODO: check that it's MutableCollection's add
|
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 argument = callExpression.valueArguments.singleOrNull() ?: return null
|
||||||
val argumentValue = argument.getArgumentExpression() ?: 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 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(
|
class FilterToTransformation private constructor(
|
||||||
loop: KtForExpression,
|
loop: KtForExpression,
|
||||||
private val inputVariable: KtCallableDeclaration,
|
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);
|
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")
|
@TestMetadata("resultCollectionUsedInsideLoop.kt")
|
||||||
public void testResultCollectionUsedInsideLoop() throws Exception {
|
public void testResultCollectionUsedInsideLoop() throws Exception {
|
||||||
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/intentions/loopToCallChain/toCollection/resultCollectionUsedInsideLoop.kt");
|
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/intentions/loopToCallChain/toCollection/resultCollectionUsedInsideLoop.kt");
|
||||||
|
|||||||
@@ -11461,6 +11461,12 @@ public class IntentionTestGenerated extends AbstractIntentionTest {
|
|||||||
doTest(fileName);
|
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")
|
@TestMetadata("resultCollectionUsedInsideLoop.kt")
|
||||||
public void testResultCollectionUsedInsideLoop() throws Exception {
|
public void testResultCollectionUsedInsideLoop() throws Exception {
|
||||||
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/intentions/loopToCallChain/toCollection/resultCollectionUsedInsideLoop.kt");
|
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/intentions/loopToCallChain/toCollection/resultCollectionUsedInsideLoop.kt");
|
||||||
|
|||||||
Reference in New Issue
Block a user