Introduce additional quick-fix for (collection) type mismatch
This fixed inserts conversion .toSequence/Array/Iterable/Collection/List So #KT-19735 Fixed
This commit is contained in:
committed by
Mikhail Glukhikh
parent
7f5b9b1760
commit
93252926ba
@@ -0,0 +1,88 @@
|
||||
/*
|
||||
* Copyright 2010-2017 JetBrains s.r.o.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.jetbrains.kotlin.idea.quickfix
|
||||
|
||||
import com.intellij.openapi.editor.Editor
|
||||
import com.intellij.openapi.project.Project
|
||||
import org.jetbrains.kotlin.builtins.KotlinBuiltIns
|
||||
import org.jetbrains.kotlin.idea.core.replaced
|
||||
import org.jetbrains.kotlin.name.FqName
|
||||
import org.jetbrains.kotlin.psi.KtExpression
|
||||
import org.jetbrains.kotlin.psi.KtFile
|
||||
import org.jetbrains.kotlin.psi.KtPsiFactory
|
||||
import org.jetbrains.kotlin.psi.createExpressionByPattern
|
||||
import org.jetbrains.kotlin.psi.psiUtil.endOffset
|
||||
import org.jetbrains.kotlin.types.KotlinType
|
||||
import org.jetbrains.kotlin.types.typeUtil.isSubtypeOf
|
||||
|
||||
class ConvertCollectionFix(element: KtExpression, val type: CollectionType) : KotlinQuickFixAction<KtExpression>(element) {
|
||||
override fun getFamilyName(): String = "Convert to ${type.displayName}"
|
||||
override fun getText() = "Convert expression to '${type.displayName}' by inserting '.${type.functionCall}'"
|
||||
|
||||
override fun invoke(project: Project, editor: Editor?, file: KtFile) {
|
||||
val expression = element ?: return
|
||||
val factory = KtPsiFactory(expression)
|
||||
|
||||
val replaced = expression.replaced(factory.createExpressionByPattern("$0.$1", expression, type.functionCall))
|
||||
editor?.caretModel?.moveToOffset(replaced.endOffset)
|
||||
}
|
||||
|
||||
enum class CollectionType(
|
||||
val functionCall: String,
|
||||
val fqName: FqName,
|
||||
private val nameOverride: String? = null
|
||||
) {
|
||||
List("toList()", FqName("kotlin.collections.List")),
|
||||
Collection("toList()", FqName("kotlin.collections.Collection")),
|
||||
Iterable("toList()", FqName("kotlin.collections.Iterable")),
|
||||
MutableList("toMutableList()", FqName("kotlin.collections.MutableList")),
|
||||
Array("toTypedArray()", FqName("kotlin.Array")),
|
||||
Sequence("asSequence()", FqName("kotlin.sequences.Sequence")),
|
||||
|
||||
//specialized types must be last because iteration order is relevant for getCollectionType
|
||||
ArrayViaList("toList().toTypedArray()", FqName("kotlin.Array"), "Array"),
|
||||
;
|
||||
|
||||
val displayName get() = nameOverride ?: name
|
||||
|
||||
fun specializeFor(sourceType: CollectionType) = when {
|
||||
this == Array && sourceType == Sequence -> ArrayViaList
|
||||
this == Array && sourceType == Iterable -> ArrayViaList
|
||||
else -> this
|
||||
}
|
||||
}
|
||||
|
||||
companion object {
|
||||
private val TYPES = CollectionType.values()
|
||||
|
||||
fun getConversionTypeOrNull(expressionType: KotlinType, expectedType: KotlinType): CollectionType? {
|
||||
val expressionCollectionType = expressionType.getCollectionType() ?: return null
|
||||
val expectedCollectionType = expectedType.getCollectionType() ?: return null
|
||||
|
||||
val expressionTypeArg = expressionType.arguments.singleOrNull()?.type ?: return null
|
||||
val expectedTypeArg = expectedType.arguments.singleOrNull()?.type ?: return null
|
||||
if (!expressionTypeArg.isSubtypeOf(expectedTypeArg)) return null
|
||||
|
||||
return expectedCollectionType.specializeFor(expressionCollectionType)
|
||||
}
|
||||
|
||||
private fun KotlinType.getCollectionType(): CollectionType? {
|
||||
if (isMarkedNullable) return null
|
||||
return TYPES.firstOrNull { KotlinBuiltIns.isConstructedFromGivenClass(this, it.fqName) }
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2010-2015 JetBrains s.r.o.
|
||||
* Copyright 2010-2017 JetBrains s.r.o.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -116,6 +116,10 @@ class QuickFixFactoryForTypeMismatchError : KotlinIntentionActionsFactory() {
|
||||
expressionTypeDeclaration?.let { actions.add(LetImplementInterfaceFix(it, expectedType, expressionType)) }
|
||||
}
|
||||
|
||||
ConvertCollectionFix.getConversionTypeOrNull(expressionType, expectedType)?.let {
|
||||
actions.add(ConvertCollectionFix(diagnosticElement, it))
|
||||
}
|
||||
|
||||
fun KtExpression.getTopMostQualifiedForSelectorIfAny(): KtExpression {
|
||||
var qualifiedOrThis = this
|
||||
do {
|
||||
@@ -245,3 +249,4 @@ class QuickFixFactoryForTypeMismatchError : KotlinIntentionActionsFactory() {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,8 @@
|
||||
// "Convert expression to 'Collection' by inserting '.toList()'" "true"
|
||||
// WITH_RUNTIME
|
||||
|
||||
fun foo(a: Array<String>) {
|
||||
bar(a<caret>)
|
||||
}
|
||||
|
||||
fun bar(a: Collection<String>) {}
|
||||
+8
@@ -0,0 +1,8 @@
|
||||
// "Convert expression to 'Collection' by inserting '.toList()'" "true"
|
||||
// WITH_RUNTIME
|
||||
|
||||
fun foo(a: Array<String>) {
|
||||
bar(a.toList()<caret>)
|
||||
}
|
||||
|
||||
fun bar(a: Collection<String>) {}
|
||||
@@ -0,0 +1,8 @@
|
||||
// "Convert expression to 'Iterable' by inserting '.toList()'" "true"
|
||||
// WITH_RUNTIME
|
||||
|
||||
fun foo(a: Array<String>) {
|
||||
bar(a<caret>)
|
||||
}
|
||||
|
||||
fun bar(a: Iterable<String>) {}
|
||||
+8
@@ -0,0 +1,8 @@
|
||||
// "Convert expression to 'Iterable' by inserting '.toList()'" "true"
|
||||
// WITH_RUNTIME
|
||||
|
||||
fun foo(a: Array<String>) {
|
||||
bar(a.toList()<caret>)
|
||||
}
|
||||
|
||||
fun bar(a: Iterable<String>) {}
|
||||
@@ -0,0 +1,8 @@
|
||||
// "Convert expression to 'List' by inserting '.toList()'" "true"
|
||||
// WITH_RUNTIME
|
||||
|
||||
fun foo(a: Array<String>) {
|
||||
bar(a<caret>)
|
||||
}
|
||||
|
||||
fun bar(a: List<String>) {}
|
||||
@@ -0,0 +1,8 @@
|
||||
// "Convert expression to 'List' by inserting '.toList()'" "true"
|
||||
// WITH_RUNTIME
|
||||
|
||||
fun foo(a: Array<String>) {
|
||||
bar(a.toList()<caret>)
|
||||
}
|
||||
|
||||
fun bar(a: List<String>) {}
|
||||
@@ -0,0 +1,8 @@
|
||||
// "Convert expression to 'Sequence' by inserting '.asSequence()'" "true"
|
||||
// WITH_RUNTIME
|
||||
|
||||
fun foo(a: Array<String>) {
|
||||
bar(a<caret>)
|
||||
}
|
||||
|
||||
fun bar(a: Sequence<String>) {}
|
||||
+8
@@ -0,0 +1,8 @@
|
||||
// "Convert expression to 'Sequence' by inserting '.asSequence()'" "true"
|
||||
// WITH_RUNTIME
|
||||
|
||||
fun foo(a: Array<String>) {
|
||||
bar(a.asSequence()<caret>)
|
||||
}
|
||||
|
||||
fun bar(a: Sequence<String>) {}
|
||||
@@ -0,0 +1,8 @@
|
||||
// "Convert expression to 'Array' by inserting '.toList().toTypedArray()'" "true"
|
||||
// WITH_RUNTIME
|
||||
|
||||
fun foo(a: Iterable<String>) {
|
||||
bar(a<caret>)
|
||||
}
|
||||
|
||||
fun bar(a: Array<String>) {}
|
||||
+8
@@ -0,0 +1,8 @@
|
||||
// "Convert expression to 'Array' by inserting '.toList().toTypedArray()'" "true"
|
||||
// WITH_RUNTIME
|
||||
|
||||
fun foo(a: Iterable<String>) {
|
||||
bar(a.toList().toTypedArray()<caret>)
|
||||
}
|
||||
|
||||
fun bar(a: Array<String>) {}
|
||||
@@ -0,0 +1,8 @@
|
||||
// "Convert expression to 'Array' by inserting '.toTypedArray()'" "true"
|
||||
// WITH_RUNTIME
|
||||
|
||||
fun foo(a: List<String>) {
|
||||
bar(a<caret>)
|
||||
}
|
||||
|
||||
fun bar(a: Array<String>) {}
|
||||
@@ -0,0 +1,8 @@
|
||||
// "Convert expression to 'Array' by inserting '.toTypedArray()'" "true"
|
||||
// WITH_RUNTIME
|
||||
|
||||
fun foo(a: List<String>) {
|
||||
bar(a.toTypedArray()<caret>)
|
||||
}
|
||||
|
||||
fun bar(a: Array<String>) {}
|
||||
@@ -0,0 +1,8 @@
|
||||
// "Convert expression to 'Array' by inserting '.toTypedArray()'" "true"
|
||||
// WITH_RUNTIME
|
||||
|
||||
fun foo(a: List<String>, b: List<String>) {
|
||||
bar(a<caret> + b)
|
||||
}
|
||||
|
||||
fun bar(a: Array<String>) {}
|
||||
+8
@@ -0,0 +1,8 @@
|
||||
// "Convert expression to 'Array' by inserting '.toTypedArray()'" "true"
|
||||
// WITH_RUNTIME
|
||||
|
||||
fun foo(a: List<String>, b: List<String>) {
|
||||
bar((a + b).toTypedArray()<caret>)
|
||||
}
|
||||
|
||||
fun bar(a: Array<String>) {}
|
||||
@@ -0,0 +1,8 @@
|
||||
// "Convert expression to 'MutableList' by inserting '.toMutableList()'" "true"
|
||||
// WITH_RUNTIME
|
||||
|
||||
fun foo(a: List<String>) {
|
||||
bar(a<caret>)
|
||||
}
|
||||
|
||||
fun bar(a: MutableList<String>) {}
|
||||
+8
@@ -0,0 +1,8 @@
|
||||
// "Convert expression to 'MutableList' by inserting '.toMutableList()'" "true"
|
||||
// WITH_RUNTIME
|
||||
|
||||
fun foo(a: List<String>) {
|
||||
bar(a.toMutableList()<caret>)
|
||||
}
|
||||
|
||||
fun bar(a: MutableList<String>) {}
|
||||
@@ -0,0 +1,8 @@
|
||||
// "Convert expression to 'Sequence' by inserting '.asSequence()'" "true"
|
||||
// WITH_RUNTIME
|
||||
|
||||
fun foo(a: List<String>) {
|
||||
bar(a<caret>)
|
||||
}
|
||||
|
||||
fun bar(a: Sequence<String>) {}
|
||||
@@ -0,0 +1,8 @@
|
||||
// "Convert expression to 'Sequence' by inserting '.asSequence()'" "true"
|
||||
// WITH_RUNTIME
|
||||
|
||||
fun foo(a: List<String>) {
|
||||
bar(a.asSequence()<caret>)
|
||||
}
|
||||
|
||||
fun bar(a: Sequence<String>) {}
|
||||
@@ -0,0 +1,8 @@
|
||||
// "Convert expression to 'Array' by inserting '.toList().toTypedArray()'" "true"
|
||||
// WITH_RUNTIME
|
||||
|
||||
fun foo(a: Sequence<String>) {
|
||||
bar(a<caret>)
|
||||
}
|
||||
|
||||
fun bar(a: Array<String>) {}
|
||||
+8
@@ -0,0 +1,8 @@
|
||||
// "Convert expression to 'Array' by inserting '.toList().toTypedArray()'" "true"
|
||||
// WITH_RUNTIME
|
||||
|
||||
fun foo(a: Sequence<String>) {
|
||||
bar(a.toList().toTypedArray()<caret>)
|
||||
}
|
||||
|
||||
fun bar(a: Array<String>) {}
|
||||
@@ -0,0 +1,8 @@
|
||||
// "Convert expression to 'List' by inserting '.toList()'" "true"
|
||||
// WITH_RUNTIME
|
||||
|
||||
fun foo(a: Sequence<String>) {
|
||||
bar(a<caret>)
|
||||
}
|
||||
|
||||
fun bar(a: List<String>) {}
|
||||
@@ -0,0 +1,8 @@
|
||||
// "Convert expression to 'List' by inserting '.toList()'" "true"
|
||||
// WITH_RUNTIME
|
||||
|
||||
fun foo(a: Sequence<String>) {
|
||||
bar(a.toList()<caret>)
|
||||
}
|
||||
|
||||
fun bar(a: List<String>) {}
|
||||
@@ -11138,6 +11138,81 @@ public class QuickFixTestGenerated extends AbstractQuickFixTest {
|
||||
}
|
||||
}
|
||||
|
||||
@TestMetadata("idea/testData/quickfix/typeMismatch/convertCollection")
|
||||
@TestDataPath("$PROJECT_ROOT")
|
||||
@RunWith(JUnit3RunnerWithInners.class)
|
||||
public static class ConvertCollection extends AbstractQuickFixTest {
|
||||
public void testAllFilesPresentInConvertCollection() throws Exception {
|
||||
KotlinTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("idea/testData/quickfix/typeMismatch/convertCollection"), Pattern.compile("^([\\w\\-_]+)\\.kt$"), TargetBackend.ANY, true);
|
||||
}
|
||||
|
||||
@TestMetadata("arrayToCollection.kt")
|
||||
public void testArrayToCollection() throws Exception {
|
||||
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/quickfix/typeMismatch/convertCollection/arrayToCollection.kt");
|
||||
doTest(fileName);
|
||||
}
|
||||
|
||||
@TestMetadata("arrayToIterable.kt")
|
||||
public void testArrayToIterable() throws Exception {
|
||||
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/quickfix/typeMismatch/convertCollection/arrayToIterable.kt");
|
||||
doTest(fileName);
|
||||
}
|
||||
|
||||
@TestMetadata("arrayToList.kt")
|
||||
public void testArrayToList() throws Exception {
|
||||
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/quickfix/typeMismatch/convertCollection/arrayToList.kt");
|
||||
doTest(fileName);
|
||||
}
|
||||
|
||||
@TestMetadata("arrayToSequence.kt")
|
||||
public void testArrayToSequence() throws Exception {
|
||||
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/quickfix/typeMismatch/convertCollection/arrayToSequence.kt");
|
||||
doTest(fileName);
|
||||
}
|
||||
|
||||
@TestMetadata("iterableToArray.kt")
|
||||
public void testIterableToArray() throws Exception {
|
||||
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/quickfix/typeMismatch/convertCollection/iterableToArray.kt");
|
||||
doTest(fileName);
|
||||
}
|
||||
|
||||
@TestMetadata("listToArray.kt")
|
||||
public void testListToArray() throws Exception {
|
||||
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/quickfix/typeMismatch/convertCollection/listToArray.kt");
|
||||
doTest(fileName);
|
||||
}
|
||||
|
||||
@TestMetadata("listToArrayBinary.kt")
|
||||
public void testListToArrayBinary() throws Exception {
|
||||
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/quickfix/typeMismatch/convertCollection/listToArrayBinary.kt");
|
||||
doTest(fileName);
|
||||
}
|
||||
|
||||
@TestMetadata("listToMutableList.kt")
|
||||
public void testListToMutableList() throws Exception {
|
||||
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/quickfix/typeMismatch/convertCollection/listToMutableList.kt");
|
||||
doTest(fileName);
|
||||
}
|
||||
|
||||
@TestMetadata("listToSequence.kt")
|
||||
public void testListToSequence() throws Exception {
|
||||
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/quickfix/typeMismatch/convertCollection/listToSequence.kt");
|
||||
doTest(fileName);
|
||||
}
|
||||
|
||||
@TestMetadata("sequenceToArray.kt")
|
||||
public void testSequenceToArray() throws Exception {
|
||||
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/quickfix/typeMismatch/convertCollection/sequenceToArray.kt");
|
||||
doTest(fileName);
|
||||
}
|
||||
|
||||
@TestMetadata("sequenceToList.kt")
|
||||
public void testSequenceToList() throws Exception {
|
||||
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/quickfix/typeMismatch/convertCollection/sequenceToList.kt");
|
||||
doTest(fileName);
|
||||
}
|
||||
}
|
||||
|
||||
@TestMetadata("idea/testData/quickfix/typeMismatch/fixOverloadedOperator")
|
||||
@TestDataPath("$PROJECT_ROOT")
|
||||
@RunWith(JUnit3RunnerWithInners.class)
|
||||
|
||||
Reference in New Issue
Block a user