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");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* you may not use this file except in compliance with 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)) }
|
expressionTypeDeclaration?.let { actions.add(LetImplementInterfaceFix(it, expectedType, expressionType)) }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ConvertCollectionFix.getConversionTypeOrNull(expressionType, expectedType)?.let {
|
||||||
|
actions.add(ConvertCollectionFix(diagnosticElement, it))
|
||||||
|
}
|
||||||
|
|
||||||
fun KtExpression.getTopMostQualifiedForSelectorIfAny(): KtExpression {
|
fun KtExpression.getTopMostQualifiedForSelectorIfAny(): KtExpression {
|
||||||
var qualifiedOrThis = this
|
var qualifiedOrThis = this
|
||||||
do {
|
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")
|
@TestMetadata("idea/testData/quickfix/typeMismatch/fixOverloadedOperator")
|
||||||
@TestDataPath("$PROJECT_ROOT")
|
@TestDataPath("$PROJECT_ROOT")
|
||||||
@RunWith(JUnit3RunnerWithInners.class)
|
@RunWith(JUnit3RunnerWithInners.class)
|
||||||
|
|||||||
Reference in New Issue
Block a user