Create from Usage: Implement "Create type alias" quickfix
#KT-12904 Fixed
This commit is contained in:
@@ -10,6 +10,7 @@
|
||||
###### New features
|
||||
- [`KT-12903`](https://youtrack.jetbrains.com/issue/KT-12903) Implement "Inline type alias" refactoring
|
||||
- [`KT-12902`](https://youtrack.jetbrains.com/issue/KT-12902) Implement "Introduce type alias" refactoring
|
||||
- [`KT-12904`](https://youtrack.jetbrains.com/issue/KT-12904) Implement "Create type alias from usage" quick fix
|
||||
|
||||
###### Issues fixed
|
||||
- [`KT-13383`](https://youtrack.jetbrains.com/issue/KT-13383), [`KT-13379`](https://youtrack.jetbrains.com/issue/KT-13379) Override/Implement Members: Do not make return type non-nullable if base return type is explicitly nullable
|
||||
|
||||
@@ -106,9 +106,12 @@ class KtPsiFactory(private val project: Project) {
|
||||
}
|
||||
|
||||
fun createTypeAlias(name: String, typeParameters: List<String>, typeElement: KtTypeElement): KtTypeAlias {
|
||||
return createTypeAlias(name, typeParameters, "X").apply { getTypeReference()!!.replace(createType(typeElement)) }
|
||||
}
|
||||
|
||||
fun createTypeAlias(name: String, typeParameters: List<String>, body: String): KtTypeAlias {
|
||||
val typeParametersText = if (typeParameters.isNotEmpty()) typeParameters.joinToString(prefix = "<", postfix = ">") else ""
|
||||
return createDeclaration<KtTypeAlias>("typealias $name$typeParametersText = X")
|
||||
.apply { getTypeReference()!!.replace(createType(typeElement)) }
|
||||
return createDeclaration<KtTypeAlias>("typealias $name$typeParametersText = $body")
|
||||
}
|
||||
|
||||
fun createStar(): PsiElement {
|
||||
|
||||
@@ -95,4 +95,7 @@ fun KtExpression.isUnreachableCode(context: BindingContext): Boolean = context[B
|
||||
fun KtExpression.getReferenceTargets(context: BindingContext): Collection<DeclarationDescriptor> {
|
||||
val targetDescriptor = if (this is KtReferenceExpression) context[BindingContext.REFERENCE_TARGET, this] else null
|
||||
return targetDescriptor?.let { listOf(it) } ?: context[BindingContext.AMBIGUOUS_REFERENCE_TARGET, this].orEmpty()
|
||||
}
|
||||
}
|
||||
|
||||
fun KtTypeReference.getAbbreviatedTypeOrType(context: BindingContext) =
|
||||
context[BindingContext.ABBREVIATED_TYPE, this] ?: context[BindingContext.TYPE, this]
|
||||
@@ -119,7 +119,7 @@ class NewDeclarationNameValidator(
|
||||
if (this is KtCallableDeclaration && receiverTypeReference != null) return false
|
||||
return when(target) {
|
||||
Target.VARIABLES -> this is KtVariableDeclaration
|
||||
Target.FUNCTIONS_AND_CLASSES -> this is KtNamedFunction || this is KtClassOrObject
|
||||
Target.FUNCTIONS_AND_CLASSES -> this is KtNamedFunction || this is KtClassOrObject || this is KtTypeAlias
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+2
-2
@@ -47,7 +47,7 @@ abstract class KotlinSingleIntentionActionFactoryWithDelegate<E : KtElement, D :
|
||||
}
|
||||
|
||||
abstract class KotlinIntentionActionFactoryWithDelegate<E : KtElement, D : Any> : KotlinIntentionActionsFactory() {
|
||||
protected abstract fun getElementOfInterest(diagnostic: Diagnostic): E?
|
||||
abstract fun getElementOfInterest(diagnostic: Diagnostic): E?
|
||||
|
||||
protected abstract fun createFixes(
|
||||
originalElementPointer: SmartPsiElementPointer<E>,
|
||||
@@ -55,7 +55,7 @@ abstract class KotlinIntentionActionFactoryWithDelegate<E : KtElement, D : Any>
|
||||
quickFixDataFactory: () -> D?
|
||||
): List<QuickFixWithDelegateFactory>
|
||||
|
||||
protected abstract fun extractFixData(element: E, diagnostic: Diagnostic): D?
|
||||
abstract fun extractFixData(element: E, diagnostic: Diagnostic): D?
|
||||
|
||||
override final fun doCreateActions(diagnostic: Diagnostic): List<IntentionAction> {
|
||||
val diagnosticMessage = DefaultErrorMessages.render(diagnostic)
|
||||
|
||||
@@ -28,6 +28,7 @@ import org.jetbrains.kotlin.idea.quickfix.createFromUsage.createClass.CreateClas
|
||||
import org.jetbrains.kotlin.idea.quickfix.createFromUsage.createClass.CreateClassFromConstructorCallActionFactory
|
||||
import org.jetbrains.kotlin.idea.quickfix.createFromUsage.createClass.CreateClassFromReferenceExpressionActionFactory
|
||||
import org.jetbrains.kotlin.idea.quickfix.createFromUsage.createClass.CreateClassFromTypeReferenceActionFactory
|
||||
import org.jetbrains.kotlin.idea.quickfix.createFromUsage.createTypeAlias.CreateTypeAliasFromTypeReferenceActionFactory
|
||||
import org.jetbrains.kotlin.idea.quickfix.createFromUsage.createVariable.CreateLocalVariableActionFactory
|
||||
import org.jetbrains.kotlin.idea.quickfix.createFromUsage.createVariable.CreateParameterByNamedArgumentActionFactory
|
||||
import org.jetbrains.kotlin.idea.quickfix.createFromUsage.createVariable.CreateParameterByRefActionFactory
|
||||
@@ -340,8 +341,11 @@ class QuickFixRegistrar : QuickFixContributor {
|
||||
|
||||
UNRESOLVED_REFERENCE.registerFactory(CreateClassFromTypeReferenceActionFactory,
|
||||
CreateClassFromReferenceExpressionActionFactory,
|
||||
CreateClassFromCallWithConstructorCalleeActionFactory,
|
||||
PlatformUnresolvedProvider)
|
||||
CreateClassFromCallWithConstructorCalleeActionFactory)
|
||||
|
||||
UNRESOLVED_REFERENCE.registerFactory(CreateTypeAliasFromTypeReferenceActionFactory)
|
||||
|
||||
UNRESOLVED_REFERENCE.registerFactory(PlatformUnresolvedProvider)
|
||||
|
||||
PRIMARY_CONSTRUCTOR_DELEGATION_CALL_EXPECTED.registerFactory(InsertDelegationCallQuickfix.InsertThisDelegationCallFactory)
|
||||
|
||||
|
||||
+109
-93
@@ -520,86 +520,7 @@ class CallableBuilder(val config: CallableBuilderConfiguration) {
|
||||
return assignmentToReplace.replace(declaration) as KtCallableDeclaration
|
||||
}
|
||||
|
||||
val newLine = psiFactory.createNewLine()
|
||||
|
||||
fun calcNecessaryEmptyLines(decl: KtDeclaration, after: Boolean): Int {
|
||||
var lineBreaksPresent: Int = 0
|
||||
var neighbor: PsiElement? = null
|
||||
|
||||
siblingsLoop@
|
||||
for (sibling in decl.siblings(forward = after, withItself = false)) {
|
||||
when (sibling) {
|
||||
is PsiWhiteSpace -> lineBreaksPresent += (sibling.text ?: "").count { it == '\n' }
|
||||
else -> {
|
||||
neighbor = sibling
|
||||
break@siblingsLoop
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
val neighborType = neighbor?.node?.elementType
|
||||
val lineBreaksNeeded = when {
|
||||
neighborType == KtTokens.LBRACE || neighborType == KtTokens.RBRACE -> 1
|
||||
neighbor is KtDeclaration && (neighbor !is KtProperty || decl !is KtProperty) -> 2
|
||||
else -> 1
|
||||
}
|
||||
|
||||
return Math.max(lineBreaksNeeded - lineBreaksPresent, 0)
|
||||
}
|
||||
|
||||
val actualContainer = (containingElement as? KtClassOrObject)?.getOrCreateBody() ?: containingElement
|
||||
|
||||
fun addNextToOriginalElementContainer(addBefore: Boolean): KtNamedDeclaration {
|
||||
val sibling = config.originalElement.parentsWithSelf.first { it.parent == actualContainer }
|
||||
return if (addBefore) {
|
||||
actualContainer.addBefore(declaration, sibling)
|
||||
}
|
||||
else {
|
||||
actualContainer.addAfter(declaration, sibling)
|
||||
} as KtNamedDeclaration
|
||||
}
|
||||
|
||||
val declarationInPlace = when {
|
||||
actualContainer.isAncestor(config.originalElement, true) -> {
|
||||
val insertToBlock = containingElement is KtBlockExpression
|
||||
if (insertToBlock) {
|
||||
val parent = containingElement.parent
|
||||
if (parent is KtFunctionLiteral) {
|
||||
if (!parent.isMultiLine()) {
|
||||
parent.addBefore(newLine, containingElement)
|
||||
parent.addAfter(newLine, containingElement)
|
||||
}
|
||||
}
|
||||
}
|
||||
addNextToOriginalElementContainer(insertToBlock || (declaration is KtProperty && actualContainer !is KtFile))
|
||||
}
|
||||
|
||||
containingElement is KtFile -> containingElement.add(declaration) as KtNamedDeclaration
|
||||
|
||||
containingElement is PsiClass -> {
|
||||
if (declaration is KtSecondaryConstructor) {
|
||||
val wrappingClass = psiFactory.createClass("class ${containingElement.name} {\n}")
|
||||
addDeclarationToClassOrObject(wrappingClass, declaration)
|
||||
(jetFileToEdit.add(wrappingClass) as KtClass).declarations.first() as KtNamedDeclaration
|
||||
}
|
||||
else {
|
||||
jetFileToEdit.add(declaration) as KtNamedDeclaration
|
||||
}
|
||||
}
|
||||
|
||||
containingElement is KtClassOrObject -> {
|
||||
insertMember(null, containingElement, declaration, containingElement.declarations.lastOrNull())
|
||||
}
|
||||
else -> throw AssertionError("Invalid containing element: ${containingElement.text}")
|
||||
}
|
||||
|
||||
val parent = declarationInPlace.parent
|
||||
calcNecessaryEmptyLines(declarationInPlace, false).let {
|
||||
if (it > 0) parent.addBefore(psiFactory.createNewLine(it), declarationInPlace)
|
||||
}
|
||||
calcNecessaryEmptyLines(declarationInPlace, true).let {
|
||||
if (it > 0) parent.addAfter(psiFactory.createNewLine(it), declarationInPlace)
|
||||
}
|
||||
val declarationInPlace = placeDeclarationInContainer(declaration, containingElement, config.originalElement, jetFileToEdit)
|
||||
|
||||
if (declarationInPlace is KtSecondaryConstructor) {
|
||||
val containingClass = declarationInPlace.containingClassOrObject!!
|
||||
@@ -615,19 +536,6 @@ class CallableBuilder(val config: CallableBuilderConfiguration) {
|
||||
}
|
||||
}
|
||||
|
||||
private fun addDeclarationToClassOrObject(classOrObject: KtClassOrObject,
|
||||
declaration: KtNamedDeclaration): KtNamedDeclaration {
|
||||
val classBody = classOrObject.getOrCreateBody()
|
||||
return if (declaration is KtNamedFunction) {
|
||||
val anchor = PsiTreeUtil.skipSiblingsBackward(
|
||||
classBody.rBrace ?: classBody.lastChild!!,
|
||||
PsiWhiteSpace::class.java
|
||||
)
|
||||
classBody.addAfter(declaration, anchor) as KtNamedDeclaration
|
||||
}
|
||||
else classBody.addAfter(declaration, classBody.lBrace!!) as KtNamedDeclaration
|
||||
}
|
||||
|
||||
private fun getTypeParameterRenames(scope: HierarchicalScope): Map<TypeParameterDescriptor, String> {
|
||||
val allTypeParametersNotInScope = LinkedHashSet<TypeParameterDescriptor>()
|
||||
|
||||
@@ -1047,6 +955,114 @@ class CallableBuilder(val config: CallableBuilderConfiguration) {
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: Simplify and use formatter as much as possible
|
||||
@Suppress("UNCHECKED_CAST")
|
||||
internal fun <D : KtNamedDeclaration> placeDeclarationInContainer(
|
||||
declaration: D,
|
||||
container: PsiElement,
|
||||
anchor: PsiElement,
|
||||
fileToEdit: KtFile = container.containingFile as KtFile
|
||||
): D {
|
||||
val psiFactory = KtPsiFactory(container)
|
||||
val newLine = psiFactory.createNewLine()
|
||||
|
||||
fun calcNecessaryEmptyLines(decl: KtDeclaration, after: Boolean): Int {
|
||||
var lineBreaksPresent: Int = 0
|
||||
var neighbor: PsiElement? = null
|
||||
|
||||
siblingsLoop@
|
||||
for (sibling in decl.siblings(forward = after, withItself = false)) {
|
||||
when (sibling) {
|
||||
is PsiWhiteSpace -> lineBreaksPresent += (sibling.text ?: "").count { it == '\n' }
|
||||
else -> {
|
||||
neighbor = sibling
|
||||
break@siblingsLoop
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
val neighborType = neighbor?.node?.elementType
|
||||
val lineBreaksNeeded = when {
|
||||
neighborType == KtTokens.LBRACE || neighborType == KtTokens.RBRACE -> 1
|
||||
neighbor is KtDeclaration && (neighbor !is KtProperty || decl !is KtProperty) -> 2
|
||||
else -> 1
|
||||
}
|
||||
|
||||
return Math.max(lineBreaksNeeded - lineBreaksPresent, 0)
|
||||
}
|
||||
|
||||
val actualContainer = (container as? KtClassOrObject)?.getOrCreateBody() ?: container
|
||||
|
||||
fun addDeclarationToClassOrObject(classOrObject: KtClassOrObject,
|
||||
declaration: KtNamedDeclaration): KtNamedDeclaration {
|
||||
val classBody = classOrObject.getOrCreateBody()
|
||||
return if (declaration is KtNamedFunction) {
|
||||
val neighbor = PsiTreeUtil.skipSiblingsBackward(
|
||||
classBody.rBrace ?: classBody.lastChild!!,
|
||||
PsiWhiteSpace::class.java
|
||||
)
|
||||
classBody.addAfter(declaration, neighbor) as KtNamedDeclaration
|
||||
}
|
||||
else classBody.addAfter(declaration, classBody.lBrace!!) as KtNamedDeclaration
|
||||
}
|
||||
|
||||
|
||||
fun addNextToOriginalElementContainer(addBefore: Boolean): D {
|
||||
val sibling = anchor.parentsWithSelf.first { it.parent == actualContainer }
|
||||
return if (addBefore) {
|
||||
actualContainer.addBefore(declaration, sibling)
|
||||
}
|
||||
else {
|
||||
actualContainer.addAfter(declaration, sibling)
|
||||
} as D
|
||||
}
|
||||
|
||||
val declarationInPlace = when {
|
||||
actualContainer.isAncestor(anchor, true) -> {
|
||||
val insertToBlock = container is KtBlockExpression
|
||||
if (insertToBlock) {
|
||||
val parent = container.parent
|
||||
if (parent is KtFunctionLiteral) {
|
||||
if (!parent.isMultiLine()) {
|
||||
parent.addBefore(newLine, container)
|
||||
parent.addAfter(newLine, container)
|
||||
}
|
||||
}
|
||||
}
|
||||
addNextToOriginalElementContainer(insertToBlock
|
||||
|| (declaration is KtProperty && actualContainer !is KtFile)
|
||||
|| declaration is KtTypeAlias)
|
||||
}
|
||||
|
||||
container is KtFile -> container.add(declaration) as D
|
||||
|
||||
container is PsiClass -> {
|
||||
if (declaration is KtSecondaryConstructor) {
|
||||
val wrappingClass = psiFactory.createClass("class ${container.name} {\n}")
|
||||
addDeclarationToClassOrObject(wrappingClass, declaration)
|
||||
(fileToEdit.add(wrappingClass) as KtClass).declarations.first() as D
|
||||
}
|
||||
else {
|
||||
fileToEdit.add(declaration) as D
|
||||
}
|
||||
}
|
||||
|
||||
container is KtClassOrObject -> {
|
||||
insertMember(null, container, declaration, container.declarations.lastOrNull())
|
||||
}
|
||||
else -> throw AssertionError("Invalid containing element: ${container.text}")
|
||||
}
|
||||
|
||||
val parent = declarationInPlace.parent
|
||||
calcNecessaryEmptyLines(declarationInPlace, false).let {
|
||||
if (it > 0) parent.addBefore(psiFactory.createNewLine(it), declarationInPlace)
|
||||
}
|
||||
calcNecessaryEmptyLines(declarationInPlace, true).let {
|
||||
if (it > 0) parent.addAfter(psiFactory.createNewLine(it), declarationInPlace)
|
||||
}
|
||||
return declarationInPlace
|
||||
}
|
||||
|
||||
internal fun KtNamedDeclaration.getReturnTypeReference(): KtTypeReference? {
|
||||
return when (this) {
|
||||
is KtCallableDeclaration -> typeReference
|
||||
|
||||
+5
@@ -18,6 +18,7 @@ package org.jetbrains.kotlin.idea.quickfix.createFromUsage.callableBuilder
|
||||
|
||||
import com.intellij.refactoring.psi.SearchUtils
|
||||
import org.jetbrains.kotlin.cfg.pseudocode.*
|
||||
import org.jetbrains.kotlin.descriptors.ClassifierDescriptor
|
||||
import org.jetbrains.kotlin.descriptors.ModuleDescriptor
|
||||
import org.jetbrains.kotlin.descriptors.TypeParameterDescriptor
|
||||
import org.jetbrains.kotlin.descriptors.VariableDescriptor
|
||||
@@ -46,6 +47,10 @@ internal operator fun KotlinType.contains(inner: KotlinType): Boolean {
|
||||
return KotlinTypeChecker.DEFAULT.equalTypes(this, inner) || arguments.any { inner in it.type }
|
||||
}
|
||||
|
||||
internal operator fun KotlinType.contains(descriptor: ClassifierDescriptor): Boolean {
|
||||
return constructor.declarationDescriptor == descriptor || arguments.any { descriptor in it.type }
|
||||
}
|
||||
|
||||
private fun KotlinType.render(typeParameterNameMap: Map<TypeParameterDescriptor, String>, fq: Boolean): String {
|
||||
val substitution = typeParameterNameMap
|
||||
.mapValues {
|
||||
|
||||
+74
@@ -0,0 +1,74 @@
|
||||
/*
|
||||
* Copyright 2010-2016 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.createFromUsage.createTypeAlias
|
||||
|
||||
import org.jetbrains.kotlin.descriptors.TypeParameterDescriptor
|
||||
import org.jetbrains.kotlin.diagnostics.Diagnostic
|
||||
import org.jetbrains.kotlin.idea.caches.resolve.analyze
|
||||
import org.jetbrains.kotlin.idea.core.CollectingNameValidator
|
||||
import org.jetbrains.kotlin.idea.core.KotlinNameSuggester
|
||||
import org.jetbrains.kotlin.idea.core.NewDeclarationNameValidator
|
||||
import org.jetbrains.kotlin.idea.quickfix.IntentionActionPriority
|
||||
import org.jetbrains.kotlin.idea.quickfix.KotlinSingleIntentionActionFactoryWithDelegate
|
||||
import org.jetbrains.kotlin.idea.quickfix.createFromUsage.createClass.CreateClassFromTypeReferenceActionFactory
|
||||
import org.jetbrains.kotlin.psi.*
|
||||
import org.jetbrains.kotlin.psi.psiUtil.getParentOfTypeAndBranch
|
||||
import org.jetbrains.kotlin.resolve.bindingContextUtil.getAbbreviatedTypeOrType
|
||||
import org.jetbrains.kotlin.resolve.lazy.BodyResolveMode
|
||||
import org.jetbrains.kotlin.types.KotlinType
|
||||
import org.jetbrains.kotlin.types.TypeSubstitutor
|
||||
import org.jetbrains.kotlin.types.Variance
|
||||
import org.jetbrains.kotlin.types.substitutions.getTypeSubstitution
|
||||
import org.jetbrains.kotlin.types.typeUtil.containsError
|
||||
|
||||
object CreateTypeAliasFromTypeReferenceActionFactory : KotlinSingleIntentionActionFactoryWithDelegate<KtUserType, TypeAliasInfo>(IntentionActionPriority.LOW) {
|
||||
override fun getElementOfInterest(diagnostic: Diagnostic) = CreateClassFromTypeReferenceActionFactory.getElementOfInterest(diagnostic)
|
||||
|
||||
data class TypeConstraintInfo(val typeParameter: TypeParameterDescriptor, val upperBound: KotlinType)
|
||||
|
||||
private fun getTypeConstraintInfo(element: KtUserType): TypeConstraintInfo? {
|
||||
val context = element.analyze(BodyResolveMode.PARTIAL)
|
||||
val containingTypeArg = (element.parent as? KtTypeReference)?.parent as? KtTypeProjection ?: return null
|
||||
val argumentList = containingTypeArg.parent as? KtTypeArgumentList ?: return null
|
||||
val containingTypeRef = (argumentList.parent as? KtTypeElement)?.parent as? KtTypeReference ?: return null
|
||||
val containingType = containingTypeRef.getAbbreviatedTypeOrType(context) ?: return null
|
||||
val baseType = containingType.constructor.declarationDescriptor?.defaultType ?: return null
|
||||
val typeParameter = containingType.constructor.parameters.getOrNull(argumentList.arguments.indexOf(containingTypeArg))
|
||||
val upperBound = typeParameter?.upperBounds?.singleOrNull() ?: return null
|
||||
val substitution = getTypeSubstitution(baseType, containingType) ?: return null
|
||||
val substitutedUpperBound = TypeSubstitutor.create(substitution).substitute(upperBound, Variance.INVARIANT) ?: return null
|
||||
if (substitutedUpperBound.containsError()) return null
|
||||
return TypeConstraintInfo(typeParameter, substitutedUpperBound)
|
||||
}
|
||||
|
||||
override fun extractFixData(element: KtUserType, diagnostic: Diagnostic): TypeAliasInfo? {
|
||||
if (element.getParentOfTypeAndBranch<KtUserType>(true) { qualifier } != null) return null
|
||||
|
||||
val classInfo = CreateClassFromTypeReferenceActionFactory.extractFixData(element, diagnostic) ?: return null
|
||||
|
||||
val expectedType = getTypeConstraintInfo(element)?.upperBound
|
||||
if (expectedType != null && expectedType.containsError()) return null
|
||||
|
||||
val validator = CollectingNameValidator(
|
||||
filter = NewDeclarationNameValidator(classInfo.targetParent, null, NewDeclarationNameValidator.Target.FUNCTIONS_AND_CLASSES)
|
||||
)
|
||||
val typeParameterNames = KotlinNameSuggester.suggestNamesForTypeParameters(classInfo.typeArguments.size, validator)
|
||||
return TypeAliasInfo(classInfo.name, classInfo.targetParent, typeParameterNames, expectedType)
|
||||
}
|
||||
|
||||
override fun createFix(originalElement: KtUserType, data: TypeAliasInfo) = CreateTypeAliasFromUsageFix(originalElement, data)
|
||||
}
|
||||
+70
@@ -0,0 +1,70 @@
|
||||
/*
|
||||
* Copyright 2010-2016 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.createFromUsage.createTypeAlias
|
||||
|
||||
import com.intellij.codeInsight.intention.LowPriorityAction
|
||||
import com.intellij.codeInsight.template.TemplateBuilderImpl
|
||||
import com.intellij.codeInsight.template.impl.ConstantNode
|
||||
import com.intellij.openapi.application.ApplicationManager
|
||||
import com.intellij.openapi.editor.Editor
|
||||
import com.intellij.openapi.project.Project
|
||||
import com.intellij.psi.PsiDocumentManager
|
||||
import com.intellij.psi.PsiElement
|
||||
import org.jetbrains.kotlin.idea.quickfix.createFromUsage.CreateFromUsageFixBase
|
||||
import org.jetbrains.kotlin.idea.quickfix.createFromUsage.callableBuilder.placeDeclarationInContainer
|
||||
import org.jetbrains.kotlin.idea.util.IdeDescriptorRenderers
|
||||
import org.jetbrains.kotlin.psi.KtElement
|
||||
import org.jetbrains.kotlin.psi.KtFile
|
||||
import org.jetbrains.kotlin.psi.KtPsiFactory
|
||||
import org.jetbrains.kotlin.types.KotlinType
|
||||
|
||||
class TypeAliasInfo(
|
||||
val name: String,
|
||||
val targetParent: PsiElement,
|
||||
val typeParameterNames: List<String>,
|
||||
val expectedType: KotlinType?
|
||||
)
|
||||
|
||||
class CreateTypeAliasFromUsageFix<E : KtElement>(
|
||||
element: E,
|
||||
val aliasInfo: TypeAliasInfo
|
||||
) : CreateFromUsageFixBase<E>(element), LowPriorityAction {
|
||||
override fun getText() = "Create type alias '${aliasInfo.name}'"
|
||||
|
||||
override fun invoke(project: Project, editor: Editor?, file: KtFile) {
|
||||
if (editor == null) return
|
||||
|
||||
val typeAliasProto = KtPsiFactory(project).createTypeAlias(aliasInfo.name, aliasInfo.typeParameterNames, "Dummy")
|
||||
val typeAlias = placeDeclarationInContainer(typeAliasProto, aliasInfo.targetParent, element)
|
||||
|
||||
if (!ApplicationManager.getApplication().isUnitTestMode) {
|
||||
PsiDocumentManager.getInstance(project).doPostponedOperationsAndUnblockDocument(editor.document)
|
||||
|
||||
val aliasBody = typeAlias.getTypeReference()!!
|
||||
|
||||
with(TemplateBuilderImpl(typeAlias)) {
|
||||
for ((typeParameter, typeParameterName) in (typeAlias.typeParameters zip aliasInfo.typeParameterNames)) {
|
||||
replaceElement(typeParameter, ConstantNode(typeParameterName))
|
||||
}
|
||||
val defaultBodyText = aliasInfo.expectedType?.let { IdeDescriptorRenderers.SOURCE_CODE.renderType(it) } ?: "Any"
|
||||
replaceElement(aliasBody, ConstantNode(defaultBodyText))
|
||||
|
||||
run(editor, true)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
+1
@@ -1,5 +1,6 @@
|
||||
// "class org.jetbrains.kotlin.idea.quickfix.ImportFix" "false"
|
||||
// ACTION: Create interface 'SomeTest'
|
||||
// ACTION: Create type alias 'SomeTest'
|
||||
// ERROR: Unresolved reference: SomeTest
|
||||
|
||||
package testing
|
||||
|
||||
idea/testData/quickfix/createFromUsage/createClass/delegationSpecifier/classDelegatorToSuperclass.kt
Vendored
+1
@@ -1,5 +1,6 @@
|
||||
// "Create class 'A'" "false"
|
||||
// ACTION: Create interface 'A'
|
||||
// ACTION: Create type alias 'A'
|
||||
// ACTION: Create test
|
||||
// ERROR: Unresolved reference: A
|
||||
package p
|
||||
|
||||
+1
@@ -1,6 +1,7 @@
|
||||
// "Create annotation 'A'" "false"
|
||||
// ACTION: Create class 'A'
|
||||
// ACTION: Create interface 'A'
|
||||
// ACTION: Create type alias 'A'
|
||||
// ACTION: Convert to block body
|
||||
// ACTION: Remove explicit type specification
|
||||
// ERROR: Unresolved reference: A
|
||||
|
||||
+1
@@ -5,5 +5,6 @@
|
||||
// ACTION: Create class 'A'
|
||||
// ACTION: Create enum 'A'
|
||||
// ACTION: Create interface 'A'
|
||||
// ACTION: Create type alias 'A'
|
||||
// ERROR: Unresolved reference: A
|
||||
internal fun foo(): J.<caret>A = throw Throwable("")
|
||||
Vendored
+1
@@ -3,6 +3,7 @@
|
||||
// ACTION: Create class 'A'
|
||||
// ACTION: Create interface 'A'
|
||||
// ACTION: Create enum 'A'
|
||||
// ACTION: Create type alias 'A'
|
||||
// ACTION: Convert to block body
|
||||
// ACTION: Remove explicit type specification
|
||||
// ERROR: Unresolved reference: A
|
||||
|
||||
Vendored
+1
@@ -1,6 +1,7 @@
|
||||
// "Create enum 'A'" "false"
|
||||
// ACTION: Create class 'A'
|
||||
// ACTION: Create interface 'A'
|
||||
// ACTION: Create type alias 'A'
|
||||
// ACTION: Convert to block body
|
||||
// ACTION: Remove explicit type specification
|
||||
// ERROR: Unresolved reference: A
|
||||
|
||||
Vendored
+1
@@ -1,6 +1,7 @@
|
||||
// "Create annotation 'NotExistent'" "false"
|
||||
// ACTION: Create class 'NotExistent'
|
||||
// ACTION: Create interface 'NotExistent'
|
||||
// ACTION: Create type alias 'NotExistent'
|
||||
// ACTION: Create test
|
||||
// ERROR: Unresolved reference: NotExistent
|
||||
class TPB<X : <caret>NotExistent>
|
||||
Vendored
+1
@@ -1,6 +1,7 @@
|
||||
// "Create annotation 'NotExistent'" "false"
|
||||
// ACTION: Create class 'NotExistent'
|
||||
// ACTION: Create interface 'NotExistent'
|
||||
// ACTION: Create type alias 'NotExistent'
|
||||
// ACTION: Create test
|
||||
// ERROR: Unresolved reference: NotExistent
|
||||
class TPB<X> where X : <caret>NotExistent
|
||||
+1
@@ -1,6 +1,7 @@
|
||||
// "Create enum 'NotExistent'" "false"
|
||||
// ACTION: Create class 'NotExistent'
|
||||
// ACTION: Create interface 'NotExistent'
|
||||
// ACTION: Create type alias 'NotExistent'
|
||||
// ACTION: Create test
|
||||
// ERROR: Unresolved reference: NotExistent
|
||||
class TPB<X : <caret>NotExistent>
|
||||
Vendored
+1
@@ -1,6 +1,7 @@
|
||||
// "Create enum 'NotExistent'" "false"
|
||||
// ACTION: Create class 'NotExistent'
|
||||
// ACTION: Create interface 'NotExistent'
|
||||
// ACTION: Create type alias 'NotExistent'
|
||||
// ACTION: Create test
|
||||
// ERROR: Unresolved reference: NotExistent
|
||||
class TPB<X> where X : <caret>NotExistent
|
||||
+1
@@ -1,6 +1,7 @@
|
||||
// "Create object 'NotExistent'" "false"
|
||||
// ACTION: Create class 'NotExistent'
|
||||
// ACTION: Create interface 'NotExistent'
|
||||
// ACTION: Create type alias 'NotExistent'
|
||||
// ACTION: Create test
|
||||
// ERROR: Unresolved reference: NotExistent
|
||||
class TPB<X : <caret>NotExistent>
|
||||
Vendored
+1
@@ -1,6 +1,7 @@
|
||||
// "Create object 'NotExistent'" "false"
|
||||
// ACTION: Create class 'NotExistent'
|
||||
// ACTION: Create interface 'NotExistent'
|
||||
// ACTION: Create type alias 'NotExistent'
|
||||
// ACTION: Create test
|
||||
// ERROR: Unresolved reference: NotExistent
|
||||
class TPB<X> where X : <caret>NotExistent
|
||||
Vendored
+1
@@ -3,6 +3,7 @@
|
||||
// ACTION: Create class 'A'
|
||||
// ACTION: Create interface 'A'
|
||||
// ACTION: Create enum 'A'
|
||||
// ACTION: Create type alias 'A'
|
||||
// ACTION: Convert to block body
|
||||
// ACTION: Remove explicit type specification
|
||||
// ERROR: Unresolved reference: A
|
||||
|
||||
Vendored
+1
@@ -1,6 +1,7 @@
|
||||
// "Create object 'A'" "false"
|
||||
// ACTION: Create class 'A'
|
||||
// ACTION: Create interface 'A'
|
||||
// ACTION: Create type alias 'A'
|
||||
// ACTION: Convert to block body
|
||||
// ACTION: Remove explicit type specification
|
||||
// ERROR: Unresolved reference: A
|
||||
|
||||
Vendored
+5
@@ -0,0 +1,5 @@
|
||||
// "Create type alias 'A'" "true"
|
||||
// ERROR: Unresolved reference: Dummy
|
||||
package p
|
||||
|
||||
fun foo(): p.<caret>A = throw Throwable("")
|
||||
idea/testData/quickfix/createFromUsage/createTypeAlias/typeReference/currentPackageReceiver.kt.after
Vendored
+7
@@ -0,0 +1,7 @@
|
||||
// "Create type alias 'A'" "true"
|
||||
// ERROR: Unresolved reference: Dummy
|
||||
package p
|
||||
|
||||
typealias A = Dummy
|
||||
|
||||
fun foo(): p.A = throw Throwable("")
|
||||
+7
@@ -0,0 +1,7 @@
|
||||
// "Create type alias 'A'" "false"
|
||||
// ACTION: Convert to block body
|
||||
// ACTION: Remove explicit type specification
|
||||
// ERROR: Unresolved reference: A
|
||||
package p
|
||||
|
||||
internal fun foo(): Int.<caret>A = throw Throwable("")
|
||||
+6
@@ -0,0 +1,6 @@
|
||||
// "Create type alias 'X'" "true"
|
||||
open class A<T>
|
||||
|
||||
class B : A<B.X>() {
|
||||
typealias X = Dummy
|
||||
}
|
||||
+5
@@ -0,0 +1,5 @@
|
||||
// DISABLED: See KT-13596
|
||||
// "Create type alias 'X'" "true"
|
||||
open class A<T>
|
||||
|
||||
class B : A<B.<caret>X>()
|
||||
+7
@@ -0,0 +1,7 @@
|
||||
// "Create type alias 'X'" "true"
|
||||
open class A<T>
|
||||
|
||||
class B : A<B.X>() {
|
||||
typealias X = Dummy
|
||||
|
||||
}
|
||||
+7
@@ -0,0 +1,7 @@
|
||||
// DISABLED: See KT-13596
|
||||
// "Create type alias 'X'" "true"
|
||||
open class A<T>
|
||||
|
||||
class B : A<B.<caret>X>() {
|
||||
|
||||
}
|
||||
Vendored
+5
@@ -0,0 +1,5 @@
|
||||
// "Create type alias 'A'" "true"
|
||||
// ERROR: Unresolved reference: Dummy
|
||||
package p
|
||||
|
||||
fun foo(): <caret>A = throw Throwable("")
|
||||
idea/testData/quickfix/createFromUsage/createTypeAlias/typeReference/notQualifierNoTypeArgs.kt.after
Vendored
+7
@@ -0,0 +1,7 @@
|
||||
// "Create type alias 'A'" "true"
|
||||
// ERROR: Unresolved reference: Dummy
|
||||
package p
|
||||
|
||||
typealias A = Dummy
|
||||
|
||||
fun foo(): A = throw Throwable("")
|
||||
+5
@@ -0,0 +1,5 @@
|
||||
// "Create type alias 'A'" "true"
|
||||
// ERROR: Unresolved reference: Dummy
|
||||
package p
|
||||
|
||||
fun foo(): <caret>A<*, String> = throw Throwable("")
|
||||
+7
@@ -0,0 +1,7 @@
|
||||
// "Create type alias 'A'" "true"
|
||||
// ERROR: Unresolved reference: Dummy
|
||||
package p
|
||||
|
||||
typealias A<T, U> = Dummy
|
||||
|
||||
fun foo(): A<*, String> = throw Throwable("")
|
||||
Vendored
+5
@@ -0,0 +1,5 @@
|
||||
// "Create type alias 'A'" "true"
|
||||
// ERROR: Unresolved reference: Dummy
|
||||
package p
|
||||
|
||||
fun foo(): <caret>A<Int, String> = throw Throwable("")
|
||||
+7
@@ -0,0 +1,7 @@
|
||||
// "Create type alias 'A'" "true"
|
||||
// ERROR: Unresolved reference: Dummy
|
||||
package p
|
||||
|
||||
typealias A<T, U> = Dummy
|
||||
|
||||
fun foo(): A<Int, String> = throw Throwable("")
|
||||
Vendored
+11
@@ -0,0 +1,11 @@
|
||||
// "Create type alias 'A'" "false"
|
||||
// ACTION: Convert to block body
|
||||
// ACTION: Create class 'A'
|
||||
// ACTION: Create enum 'A'
|
||||
// ACTION: Create interface 'A'
|
||||
// ACTION: Create object 'A'
|
||||
// ACTION: Remove explicit type specification
|
||||
// ERROR: Unresolved reference: A
|
||||
package p
|
||||
|
||||
fun foo(): <caret>A.B = throw Throwable("")
|
||||
Vendored
+11
@@ -0,0 +1,11 @@
|
||||
// "Create type alias 'X'" "false"
|
||||
// ACTION: Create class 'X'
|
||||
// ACTION: Create interface 'X'
|
||||
// ERROR: Unresolved reference: X
|
||||
package p
|
||||
|
||||
open class A<T : List<T>>
|
||||
|
||||
fun foo(a: A<<caret>X<Int>>) {
|
||||
|
||||
}
|
||||
Vendored
+11
@@ -0,0 +1,11 @@
|
||||
// "Create type alias 'X'" "false"
|
||||
// ACTION: Create class 'X'
|
||||
// ACTION: Create interface 'X'
|
||||
// ERROR: Unresolved reference: X
|
||||
package p
|
||||
|
||||
open class A<T, U : Map<T, U>>
|
||||
|
||||
fun foo(a: A<List<String>, <caret>X<Int>>) {
|
||||
|
||||
}
|
||||
Vendored
+9
@@ -0,0 +1,9 @@
|
||||
// "Create type alias 'X'" "true"
|
||||
// ERROR: Unresolved reference: Dummy
|
||||
package p
|
||||
|
||||
open class A<T, U : List<T>>
|
||||
|
||||
fun foo(a: A<List<String>, <caret>X<Int>>) {
|
||||
|
||||
}
|
||||
+11
@@ -0,0 +1,11 @@
|
||||
// "Create type alias 'X'" "true"
|
||||
// ERROR: Unresolved reference: Dummy
|
||||
package p
|
||||
|
||||
open class A<T, U : List<T>>
|
||||
|
||||
typealias X<T> = Dummy
|
||||
|
||||
fun foo(a: A<List<String>, X<Int>>) {
|
||||
|
||||
}
|
||||
+9
@@ -0,0 +1,9 @@
|
||||
// "Create type alias 'A'" "true"
|
||||
// ERROR: Unresolved reference: Dummy
|
||||
package p
|
||||
|
||||
class T {
|
||||
|
||||
}
|
||||
|
||||
fun foo(): T.<caret>A = throw Throwable("")
|
||||
Vendored
+10
@@ -0,0 +1,10 @@
|
||||
// "Create type alias 'A'" "true"
|
||||
// ERROR: Unresolved reference: Dummy
|
||||
package p
|
||||
|
||||
class T {
|
||||
typealias A = Dummy
|
||||
|
||||
}
|
||||
|
||||
fun foo(): T.A = throw Throwable("")
|
||||
Vendored
+7
@@ -0,0 +1,7 @@
|
||||
// "Create type alias 'A'" "true"
|
||||
// ERROR: Unresolved reference: Dummy
|
||||
package p
|
||||
|
||||
class T
|
||||
|
||||
fun foo(): T.<caret>A = throw Throwable("")
|
||||
idea/testData/quickfix/createFromUsage/createTypeAlias/typeReference/userTypeReceiverNoBody.kt.after
Vendored
+9
@@ -0,0 +1,9 @@
|
||||
// "Create type alias 'A'" "true"
|
||||
// ERROR: Unresolved reference: Dummy
|
||||
package p
|
||||
|
||||
class T {
|
||||
typealias A = Dummy
|
||||
}
|
||||
|
||||
fun foo(): T.A = throw Throwable("")
|
||||
@@ -3,6 +3,7 @@
|
||||
// ACTION: Create class 'X'
|
||||
// ACTION: Create enum 'X'
|
||||
// ACTION: Create interface 'X'
|
||||
// ACTION: Create type alias 'X'
|
||||
// ERROR: Unresolved reference: X
|
||||
// ERROR: Unresolved reference: X
|
||||
class A {
|
||||
|
||||
+1
@@ -3,6 +3,7 @@
|
||||
// ACTION: Create class 'NoSuchType'
|
||||
// ACTION: Create enum 'NoSuchType'
|
||||
// ACTION: Create interface 'NoSuchType'
|
||||
// ACTION: Create type alias 'NoSuchType'
|
||||
// ACTION: Remove explicit lambda parameter types (may break code)
|
||||
// ERROR: Type mismatch: inferred type is ([ERROR : NoSuchType]) -> Int but Int was expected
|
||||
// ERROR: Unresolved reference: NoSuchType
|
||||
|
||||
@@ -1108,6 +1108,16 @@ public class QuickFixMultiFileTestGenerated extends AbstractQuickFixMultiFileTes
|
||||
}
|
||||
}
|
||||
|
||||
@TestMetadata("idea/testData/quickfix/createFromUsage/createTypeAlias")
|
||||
@TestDataPath("$PROJECT_ROOT")
|
||||
@RunWith(JUnit3RunnerWithInners.class)
|
||||
public static class CreateTypeAlias extends AbstractQuickFixMultiFileTest {
|
||||
public void testAllFilesPresentInCreateTypeAlias() throws Exception {
|
||||
KotlinTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("idea/testData/quickfix/createFromUsage/createTypeAlias"), Pattern.compile("^(\\w+)\\.((before\\.Main\\.\\w+)|(test))$"), true);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@TestMetadata("idea/testData/quickfix/createFromUsage/createVariable")
|
||||
@TestDataPath("$PROJECT_ROOT")
|
||||
@RunWith(JUnit3RunnerWithInners.class)
|
||||
|
||||
@@ -2966,6 +2966,90 @@ public class QuickFixTestGenerated extends AbstractQuickFixTest {
|
||||
}
|
||||
}
|
||||
|
||||
@TestMetadata("idea/testData/quickfix/createFromUsage/createTypeAlias")
|
||||
@TestDataPath("$PROJECT_ROOT")
|
||||
@RunWith(JUnit3RunnerWithInners.class)
|
||||
public static class CreateTypeAlias extends AbstractQuickFixTest {
|
||||
public void testAllFilesPresentInCreateTypeAlias() throws Exception {
|
||||
KotlinTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("idea/testData/quickfix/createFromUsage/createTypeAlias"), Pattern.compile("^([\\w\\-_]+)\\.kt$"), true);
|
||||
}
|
||||
|
||||
@TestMetadata("idea/testData/quickfix/createFromUsage/createTypeAlias/typeReference")
|
||||
@TestDataPath("$PROJECT_ROOT")
|
||||
@RunWith(JUnit3RunnerWithInners.class)
|
||||
public static class TypeReference extends AbstractQuickFixTest {
|
||||
public void testAllFilesPresentInTypeReference() throws Exception {
|
||||
KotlinTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("idea/testData/quickfix/createFromUsage/createTypeAlias/typeReference"), Pattern.compile("^([\\w\\-_]+)\\.kt$"), true);
|
||||
}
|
||||
|
||||
@TestMetadata("currentPackageReceiver.kt")
|
||||
public void testCurrentPackageReceiver() throws Exception {
|
||||
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/quickfix/createFromUsage/createTypeAlias/typeReference/currentPackageReceiver.kt");
|
||||
doTest(fileName);
|
||||
}
|
||||
|
||||
@TestMetadata("libTypeReceiver.kt")
|
||||
public void testLibTypeReceiver() throws Exception {
|
||||
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/quickfix/createFromUsage/createTypeAlias/typeReference/libTypeReceiver.kt");
|
||||
doTest(fileName);
|
||||
}
|
||||
|
||||
@TestMetadata("notQualifierNoTypeArgs.kt")
|
||||
public void testNotQualifierNoTypeArgs() throws Exception {
|
||||
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/quickfix/createFromUsage/createTypeAlias/typeReference/notQualifierNoTypeArgs.kt");
|
||||
doTest(fileName);
|
||||
}
|
||||
|
||||
@TestMetadata("notQualifierWithStarProjection.kt")
|
||||
public void testNotQualifierWithStarProjection() throws Exception {
|
||||
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/quickfix/createFromUsage/createTypeAlias/typeReference/notQualifierWithStarProjection.kt");
|
||||
doTest(fileName);
|
||||
}
|
||||
|
||||
@TestMetadata("notQualifierWithTypeArgs.kt")
|
||||
public void testNotQualifierWithTypeArgs() throws Exception {
|
||||
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/quickfix/createFromUsage/createTypeAlias/typeReference/notQualifierWithTypeArgs.kt");
|
||||
doTest(fileName);
|
||||
}
|
||||
|
||||
@TestMetadata("qualifierNoTypeArgs.kt")
|
||||
public void testQualifierNoTypeArgs() throws Exception {
|
||||
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/quickfix/createFromUsage/createTypeAlias/typeReference/qualifierNoTypeArgs.kt");
|
||||
doTest(fileName);
|
||||
}
|
||||
|
||||
@TestMetadata("recursiveTypeBound1.kt")
|
||||
public void testRecursiveTypeBound1() throws Exception {
|
||||
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/quickfix/createFromUsage/createTypeAlias/typeReference/recursiveTypeBound1.kt");
|
||||
doTest(fileName);
|
||||
}
|
||||
|
||||
@TestMetadata("recursiveTypeBound2.kt")
|
||||
public void testRecursiveTypeBound2() throws Exception {
|
||||
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/quickfix/createFromUsage/createTypeAlias/typeReference/recursiveTypeBound2.kt");
|
||||
doTest(fileName);
|
||||
}
|
||||
|
||||
@TestMetadata("typeParameterDependency.kt")
|
||||
public void testTypeParameterDependency() throws Exception {
|
||||
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/quickfix/createFromUsage/createTypeAlias/typeReference/typeParameterDependency.kt");
|
||||
doTest(fileName);
|
||||
}
|
||||
|
||||
@TestMetadata("userTypeReceiver.kt")
|
||||
public void testUserTypeReceiver() throws Exception {
|
||||
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/quickfix/createFromUsage/createTypeAlias/typeReference/userTypeReceiver.kt");
|
||||
doTest(fileName);
|
||||
}
|
||||
|
||||
@TestMetadata("userTypeReceiverNoBody.kt")
|
||||
public void testUserTypeReceiverNoBody() throws Exception {
|
||||
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/quickfix/createFromUsage/createTypeAlias/typeReference/userTypeReceiverNoBody.kt");
|
||||
doTest(fileName);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@TestMetadata("idea/testData/quickfix/createFromUsage/createVariable")
|
||||
@TestDataPath("$PROJECT_ROOT")
|
||||
@RunWith(JUnit3RunnerWithInners.class)
|
||||
|
||||
Reference in New Issue
Block a user