Implement quickfix wrapping elements in collection literal calls
#KT-25238 Fixed
This commit is contained in:
@@ -42,19 +42,22 @@ class ConvertCollectionFix(element: KtExpression, val type: CollectionType) : Ko
|
||||
}
|
||||
|
||||
enum class CollectionType(
|
||||
val functionCall: String,
|
||||
val fqName: FqName,
|
||||
private val nameOverride: String? = null
|
||||
val functionCall: String,
|
||||
val fqName: FqName,
|
||||
val literalFunctionName: String? = null,
|
||||
val emptyCollectionFunction: String? = null,
|
||||
private val nameOverride: String? = null
|
||||
) {
|
||||
List("toList()", FqName("kotlin.collections.List")),
|
||||
Collection("toList()", FqName("kotlin.collections.Collection")),
|
||||
Iterable("toList()", FqName("kotlin.collections.Iterable")),
|
||||
List("toList()", FqName("kotlin.collections.List"), "listOf", "emptyList"),
|
||||
Collection("toList()", FqName("kotlin.collections.Collection"), "listOf", "emptyList"),
|
||||
Iterable("toList()", FqName("kotlin.collections.Iterable"), "listOf", "emptyList"),
|
||||
MutableList("toMutableList()", FqName("kotlin.collections.MutableList")),
|
||||
Array("toTypedArray()", FqName("kotlin.Array")),
|
||||
Sequence("asSequence()", FqName("kotlin.sequences.Sequence")),
|
||||
Array("toTypedArray()", FqName("kotlin.Array"), "arrayOf", "emptyArray"),
|
||||
Sequence("asSequence()", FqName("kotlin.sequences.Sequence"), "sequenceOf", "emptySequence"),
|
||||
Set("toSet()", FqName("kotlin.collections.Set"), "setOf", "emptySet"),
|
||||
|
||||
//specialized types must be last because iteration order is relevant for getCollectionType
|
||||
ArrayViaList("toList().toTypedArray()", FqName("kotlin.Array"), "Array"),
|
||||
ArrayViaList("toList().toTypedArray()", FqName("kotlin.Array"), nameOverride = "Array"),
|
||||
;
|
||||
|
||||
val displayName get() = nameOverride ?: name
|
||||
@@ -80,9 +83,9 @@ class ConvertCollectionFix(element: KtExpression, val type: CollectionType) : Ko
|
||||
return expectedCollectionType.specializeFor(expressionCollectionType)
|
||||
}
|
||||
|
||||
private fun KotlinType.getCollectionType(): CollectionType? {
|
||||
if (isMarkedNullable) return null
|
||||
fun KotlinType.getCollectionType(acceptNullableTypes: Boolean = false): CollectionType? {
|
||||
if (isMarkedNullable && !acceptNullableTypes) return null
|
||||
return TYPES.firstOrNull { KotlinBuiltIns.isConstructedFromGivenClass(this, it.fqName) }
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -19,8 +19,9 @@ package org.jetbrains.kotlin.idea.quickfix
|
||||
import com.intellij.codeInsight.intention.IntentionAction
|
||||
import com.intellij.openapi.diagnostic.Logger
|
||||
import com.intellij.psi.util.PsiTreeUtil
|
||||
import org.jetbrains.kotlin.builtins.*
|
||||
import org.jetbrains.kotlin.builtins.KotlinBuiltIns
|
||||
import org.jetbrains.kotlin.builtins.functions.FunctionClassDescriptor
|
||||
import org.jetbrains.kotlin.builtins.getFunctionalClassKind
|
||||
import org.jetbrains.kotlin.diagnostics.Diagnostic
|
||||
import org.jetbrains.kotlin.diagnostics.Errors
|
||||
import org.jetbrains.kotlin.diagnostics.rendering.DefaultErrorMessages
|
||||
@@ -122,6 +123,9 @@ class QuickFixFactoryForTypeMismatchError : KotlinIntentionActionsFactory() {
|
||||
expressionTypeDeclaration?.let { actions.add(LetImplementInterfaceFix(it, expectedType, expressionType)) }
|
||||
}
|
||||
|
||||
|
||||
actions.addAll(WrapWithCollectionLiteralCallFix.create(expectedType, expressionType, diagnosticElement))
|
||||
|
||||
ConvertCollectionFix.getConversionTypeOrNull(expressionType, expectedType)?.let {
|
||||
actions.add(ConvertCollectionFix(diagnosticElement, it))
|
||||
}
|
||||
|
||||
@@ -0,0 +1,77 @@
|
||||
/*
|
||||
* Copyright 2010-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license
|
||||
* that can be found in the license/LICENSE.txt file.
|
||||
*/
|
||||
|
||||
package org.jetbrains.kotlin.idea.quickfix
|
||||
|
||||
import com.intellij.openapi.editor.Editor
|
||||
import com.intellij.openapi.project.Project
|
||||
import org.jetbrains.kotlin.idea.core.replaced
|
||||
import org.jetbrains.kotlin.idea.intentions.branchedTransformations.isNullExpression
|
||||
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.Variance
|
||||
import org.jetbrains.kotlin.types.typeUtil.isSubtypeOf
|
||||
|
||||
class WrapWithCollectionLiteralCallFix private constructor(
|
||||
element: KtExpression,
|
||||
private val functionName: String,
|
||||
private val wrapInitialElement: Boolean
|
||||
) : KotlinQuickFixAction<KtExpression>(element) {
|
||||
|
||||
override fun invoke(project: Project, editor: Editor?, file: KtFile) {
|
||||
val expression = element ?: return
|
||||
val factory = KtPsiFactory(expression)
|
||||
|
||||
val replaced =
|
||||
if (wrapInitialElement)
|
||||
expression.replaced(factory.createExpressionByPattern("$functionName($0)", expression))
|
||||
else
|
||||
expression.replaced(factory.createExpression("$functionName()"))
|
||||
|
||||
editor?.caretModel?.moveToOffset(replaced.endOffset)
|
||||
}
|
||||
|
||||
override fun getFamilyName(): String = "Wrap with collection literal call"
|
||||
override fun getText() =
|
||||
if (wrapInitialElement)
|
||||
"Wrap element with '$functionName()' call"
|
||||
else
|
||||
"Replace with '$functionName()' call"
|
||||
|
||||
companion object {
|
||||
fun create(expectedType: KotlinType, expressionType: KotlinType, element: KtExpression): List<WrapWithCollectionLiteralCallFix> {
|
||||
val collectionType =
|
||||
with(ConvertCollectionFix) {
|
||||
expectedType.getCollectionType(acceptNullableTypes = true)
|
||||
} ?: return emptyList()
|
||||
|
||||
val expectedArgumentType =
|
||||
expectedType
|
||||
.arguments.singleOrNull()
|
||||
?.takeIf { it.projectionKind != Variance.IN_VARIANCE }
|
||||
?.type
|
||||
?: return emptyList()
|
||||
|
||||
val result = mutableListOf<WrapWithCollectionLiteralCallFix>()
|
||||
|
||||
val isNullExpression = element.isNullExpression()
|
||||
if ((expressionType.isSubtypeOf(expectedArgumentType) || isNullExpression) && collectionType.literalFunctionName != null) {
|
||||
result += WrapWithCollectionLiteralCallFix(element, collectionType.literalFunctionName, wrapInitialElement = true)
|
||||
}
|
||||
|
||||
// Replace "null" with emptyList()
|
||||
if (isNullExpression && collectionType.emptyCollectionFunction != null) {
|
||||
result += WrapWithCollectionLiteralCallFix(element, collectionType.emptyCollectionFunction, wrapInitialElement = false)
|
||||
}
|
||||
|
||||
return result
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
Reference in New Issue
Block a user