From 5bc2a4b14ccff168aae459849f2f8a0f343e6792 Mon Sep 17 00:00:00 2001 From: Alexey Sedunov Date: Fri, 24 Jul 2015 21:12:30 +0300 Subject: [PATCH] Create From Usage: Do not keep semantic information in quickfix instances directly as it leads to memory leaks --- .../jetbrains/kotlin/psi/psiUtil/psiUtils.kt | 2 + .../kotlin/idea/quickfix/NullQuickFix.kt | 37 +++ .../kotlin/idea/quickfix/QuickFixRegistrar.kt | 18 +- .../quickfix/QuickFixWithDelegateFactory.kt | 51 +++++ .../createFromUsage/CreateFromUsageFactory.kt | 85 +++++++ .../createFromUsage/CreateFromUsageFixBase.kt | 4 +- .../callableBuilder/typeUtils.kt | 10 + .../CreateBinaryOperationActionFactory.kt | 46 ++-- .../CreateCallableFromCallActionFactory.kt | 215 +++++++++++------- .../CreateCallableFromUsageFix.kt | 69 +++--- .../CreateCallableMemberFromUsageFactory.kt | 63 +++++ .../CreateComponentFunctionActionFactory.kt | 39 ++-- ...structorFromDelegationCallActionFactory.kt | 32 +-- ...orFromDelegatorToSuperCallActionFactory.kt | 31 ++- .../CreateGetFunctionActionFactory.kt | 29 +-- .../CreateHasNextFunctionActionFactory.kt | 27 ++- .../CreateInvokeFunctionActionFactory.kt | 26 +-- .../CreateIteratorFunctionActionFactory.kt | 49 ++-- .../CreateNextFunctionActionFactory.kt | 27 ++- ...ePropertyDelegateAccessorsActionFactory.kt | 59 ++--- .../CreateSetFunctionActionFactory.kt | 51 +++-- .../CreateUnaryOperationActionFactory.kt | 27 ++- ...mCallWithConstructorCalleeActionFactory.kt | 62 ++--- ...teClassFromConstructorCallActionFactory.kt | 68 ++++-- ...assFromReferenceExpressionActionFactory.kt | 154 +++++++------ ...eateClassFromTypeReferenceActionFactory.kt | 88 ++++--- .../CreateClassFromUsageFactory.kt | 59 +++++ .../createClass/CreateClassFromUsageFix.kt | 39 ++-- .../CreateLocalVariableActionFactory.kt | 13 +- .../CreateParameterActionFactory.kt | 147 ------------ ...teParameterByNamedArgumentActionFactory.kt | 49 ++-- .../CreateParameterByRefActionFactory.kt | 140 ++++++++++++ .../CreateParameterFromUsageFactory.kt | 50 ++++ .../CreateParameterFromUsageFix.kt | 10 +- 34 files changed, 1148 insertions(+), 728 deletions(-) create mode 100644 idea/src/org/jetbrains/kotlin/idea/quickfix/NullQuickFix.kt create mode 100644 idea/src/org/jetbrains/kotlin/idea/quickfix/QuickFixWithDelegateFactory.kt create mode 100644 idea/src/org/jetbrains/kotlin/idea/quickfix/createFromUsage/CreateFromUsageFactory.kt create mode 100644 idea/src/org/jetbrains/kotlin/idea/quickfix/createFromUsage/createCallable/CreateCallableMemberFromUsageFactory.kt create mode 100644 idea/src/org/jetbrains/kotlin/idea/quickfix/createFromUsage/createClass/CreateClassFromUsageFactory.kt delete mode 100644 idea/src/org/jetbrains/kotlin/idea/quickfix/createFromUsage/createVariable/CreateParameterActionFactory.kt create mode 100644 idea/src/org/jetbrains/kotlin/idea/quickfix/createFromUsage/createVariable/CreateParameterByRefActionFactory.kt create mode 100644 idea/src/org/jetbrains/kotlin/idea/quickfix/createFromUsage/createVariable/CreateParameterFromUsageFactory.kt diff --git a/compiler/frontend/src/org/jetbrains/kotlin/psi/psiUtil/psiUtils.kt b/compiler/frontend/src/org/jetbrains/kotlin/psi/psiUtil/psiUtils.kt index 5d1332a9105..78e91032d9e 100644 --- a/compiler/frontend/src/org/jetbrains/kotlin/psi/psiUtil/psiUtils.kt +++ b/compiler/frontend/src/org/jetbrains/kotlin/psi/psiUtil/psiUtils.kt @@ -288,3 +288,5 @@ public fun PsiElement.getTextWithLocation(): String = "'${this.getText()}' at ${ public fun SearchScope.contains(element: PsiElement): Boolean = PsiSearchScopeUtil.isInScope(this, element) +public fun E.createSmartPointer(): SmartPsiElementPointer = + SmartPointerManager.getInstance(getProject()).createSmartPsiElementPointer(this) \ No newline at end of file diff --git a/idea/src/org/jetbrains/kotlin/idea/quickfix/NullQuickFix.kt b/idea/src/org/jetbrains/kotlin/idea/quickfix/NullQuickFix.kt new file mode 100644 index 00000000000..2617290b7bf --- /dev/null +++ b/idea/src/org/jetbrains/kotlin/idea/quickfix/NullQuickFix.kt @@ -0,0 +1,37 @@ +/* + * Copyright 2010-2015 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.codeInsight.intention.IntentionAction +import com.intellij.openapi.editor.Editor +import com.intellij.openapi.project.Project +import com.intellij.psi.PsiFile +import org.jetbrains.annotations.NotNull + +public object NullQuickFix : IntentionAction { + override fun getText() = "" + + override fun getFamilyName() = "" + + override fun isAvailable(project: Project, editor: Editor?, file: PsiFile) = false + + override fun startInWriteAction() = false + + override fun invoke(NotNull project: Project, editor: Editor?, file: PsiFile?) { + throw UnsupportedOperationException("invoke() shouldn't be called") + } +} \ No newline at end of file diff --git a/idea/src/org/jetbrains/kotlin/idea/quickfix/QuickFixRegistrar.kt b/idea/src/org/jetbrains/kotlin/idea/quickfix/QuickFixRegistrar.kt index 53baa99fdff..5ec428d76be 100644 --- a/idea/src/org/jetbrains/kotlin/idea/quickfix/QuickFixRegistrar.kt +++ b/idea/src/org/jetbrains/kotlin/idea/quickfix/QuickFixRegistrar.kt @@ -27,7 +27,7 @@ import org.jetbrains.kotlin.idea.quickfix.createFromUsage.createClass.CreateClas 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.createVariable.CreateLocalVariableActionFactory -import org.jetbrains.kotlin.idea.quickfix.createFromUsage.createVariable.CreateParameterActionFactory +import org.jetbrains.kotlin.idea.quickfix.createFromUsage.createVariable.CreateParameterByRefActionFactory import org.jetbrains.kotlin.idea.quickfix.createFromUsage.createVariable.CreateParameterByNamedArgumentActionFactory import org.jetbrains.kotlin.lexer.JetTokens.* import org.jetbrains.kotlin.psi.JetClass @@ -250,11 +250,11 @@ public class QuickFixRegistrar : QuickFixContributor { NO_VALUE_FOR_PARAMETER.registerFactory(CreateBinaryOperationActionFactory) TOO_MANY_ARGUMENTS.registerFactory(CreateBinaryOperationActionFactory) - UNRESOLVED_REFERENCE_WRONG_RECEIVER.registerFactory(CreateCallableFromCallActionFactory) - UNRESOLVED_REFERENCE.registerFactory(CreateCallableFromCallActionFactory) - NO_VALUE_FOR_PARAMETER.registerFactory(CreateCallableFromCallActionFactory) - TOO_MANY_ARGUMENTS.registerFactory(CreateCallableFromCallActionFactory) - EXPRESSION_EXPECTED_PACKAGE_FOUND.registerFactory(CreateCallableFromCallActionFactory) + UNRESOLVED_REFERENCE_WRONG_RECEIVER.registerFactory(*CreateCallableFromCallActionFactory.INSTANCES) + UNRESOLVED_REFERENCE.registerFactory(*CreateCallableFromCallActionFactory.INSTANCES) + NO_VALUE_FOR_PARAMETER.registerFactory(*CreateCallableFromCallActionFactory.INSTANCES) + TOO_MANY_ARGUMENTS.registerFactory(*CreateCallableFromCallActionFactory.INSTANCES) + EXPRESSION_EXPECTED_PACKAGE_FOUND.registerFactory(*CreateCallableFromCallActionFactory.INSTANCES) NO_VALUE_FOR_PARAMETER.registerFactory(CreateConstructorFromDelegationCallActionFactory) TOO_MANY_ARGUMENTS.registerFactory(CreateConstructorFromDelegationCallActionFactory) @@ -269,9 +269,9 @@ public class QuickFixRegistrar : QuickFixContributor { UNRESOLVED_REFERENCE.registerFactory(CreateLocalVariableActionFactory) EXPRESSION_EXPECTED_PACKAGE_FOUND.registerFactory(CreateLocalVariableActionFactory) - UNRESOLVED_REFERENCE.registerFactory(CreateParameterActionFactory) - UNRESOLVED_REFERENCE_WRONG_RECEIVER.registerFactory(CreateParameterActionFactory) - EXPRESSION_EXPECTED_PACKAGE_FOUND.registerFactory(CreateParameterActionFactory) + UNRESOLVED_REFERENCE.registerFactory(CreateParameterByRefActionFactory) + UNRESOLVED_REFERENCE_WRONG_RECEIVER.registerFactory(CreateParameterByRefActionFactory) + EXPRESSION_EXPECTED_PACKAGE_FOUND.registerFactory(CreateParameterByRefActionFactory) NAMED_PARAMETER_NOT_FOUND.registerFactory(CreateParameterByNamedArgumentActionFactory) diff --git a/idea/src/org/jetbrains/kotlin/idea/quickfix/QuickFixWithDelegateFactory.kt b/idea/src/org/jetbrains/kotlin/idea/quickfix/QuickFixWithDelegateFactory.kt new file mode 100644 index 00000000000..4ecde0f8c4c --- /dev/null +++ b/idea/src/org/jetbrains/kotlin/idea/quickfix/QuickFixWithDelegateFactory.kt @@ -0,0 +1,51 @@ +/* + * Copyright 2010-2015 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.codeInsight.intention.IntentionAction +import com.intellij.openapi.editor.Editor +import com.intellij.openapi.project.Project +import com.intellij.psi.PsiFile +import org.jetbrains.annotations.NotNull + +public class QuickFixWithDelegateFactory( + private val delegateFactory: () -> IntentionAction +) : IntentionAction { + private val familyName: String + private val text: String + private val startInWriteAction: Boolean + + init { + val delegate = delegateFactory() + familyName = delegate.familyName + text = delegate.text + startInWriteAction = delegate.startInWriteAction() + } + + override fun getFamilyName() = familyName + + override fun getText() = text + + override fun isAvailable(project: Project, editor: Editor?, file: PsiFile) = + delegateFactory().isAvailable(project, editor, file) + + override fun startInWriteAction() = startInWriteAction + + override fun invoke(NotNull project: Project, editor: Editor?, file: PsiFile?) { + delegateFactory().invoke(project, editor, file) + } +} \ No newline at end of file diff --git a/idea/src/org/jetbrains/kotlin/idea/quickfix/createFromUsage/CreateFromUsageFactory.kt b/idea/src/org/jetbrains/kotlin/idea/quickfix/createFromUsage/CreateFromUsageFactory.kt new file mode 100644 index 00000000000..2e921b36f3b --- /dev/null +++ b/idea/src/org/jetbrains/kotlin/idea/quickfix/createFromUsage/CreateFromUsageFactory.kt @@ -0,0 +1,85 @@ +/* + * Copyright 2010-2015 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 + +import com.intellij.codeInsight.intention.IntentionAction +import com.intellij.openapi.util.Ref +import com.intellij.psi.SmartPsiElementPointer +import org.jetbrains.kotlin.diagnostics.Diagnostic +import org.jetbrains.kotlin.diagnostics.rendering.DefaultErrorMessages +import org.jetbrains.kotlin.idea.caches.resolve.analyze +import org.jetbrains.kotlin.idea.quickfix.JetIntentionActionsFactory +import org.jetbrains.kotlin.idea.quickfix.QuickFixWithDelegateFactory +import org.jetbrains.kotlin.psi.JetElement +import org.jetbrains.kotlin.psi.psiUtil.createSmartPointer +import org.jetbrains.kotlin.resolve.lazy.BodyResolveMode +import org.jetbrains.kotlin.utils.singletonOrEmptyList + +public abstract class CreateFromUsageFactory : JetIntentionActionsFactory() { + protected abstract fun getElementOfInterest(diagnostic: Diagnostic): E? + + protected open fun createQuickFix( + originalElementPointer: SmartPsiElementPointer, + diagnostic: Diagnostic, + quickFixDataFactory: (SmartPsiElementPointer) -> D + ): QuickFixWithDelegateFactory? = null + + protected open fun createQuickFixes( + originalElementPointer: SmartPsiElementPointer, + diagnostic: Diagnostic, + quickFixDataFactory: (SmartPsiElementPointer) -> D + ): List = createQuickFix(originalElementPointer, diagnostic, quickFixDataFactory).singletonOrEmptyList() + + protected abstract fun createQuickFixData(element: E, diagnostic: Diagnostic): D + + override final fun doCreateActions(diagnostic: Diagnostic): List? { + val diagnosticMessage = DefaultErrorMessages.render(diagnostic) + val diagnosticElementPointer = diagnostic.psiElement.createSmartPointer() + val originalElement = getElementOfInterest(diagnostic) ?: return null + val originalElementPointer = originalElement.createSmartPointer() + + val file = originalElement.containingFile + val project = file.project + + // Cache data so that it can be shared between quick fixes bound to the same element & diagnostic + // Cache null values + var cachedData: Ref? = null + val actions: List + try { + actions = createQuickFixes(originalElementPointer, diagnostic) factory@ { + val element = it.element ?: return@factory null + val diagnosticElement = diagnosticElementPointer.element ?: return@factory null + if (!diagnosticElement.isValid || !element.isValid) return@factory null + + val currentDiagnostic = + element.analyze(BodyResolveMode.PARTIAL) + .diagnostics + .forElement(diagnosticElement) + .firstOrNull { DefaultErrorMessages.render(it) == diagnosticMessage } ?: return@factory null + if (cachedData == null) { + cachedData = Ref(createQuickFixData(element, currentDiagnostic)) + } + cachedData!!.get() + }.filter { it.isAvailable(project, null, file) } + } + finally { + cachedData = null // Do not keep cache after all actions are initialized + } + + return actions + } +} diff --git a/idea/src/org/jetbrains/kotlin/idea/quickfix/createFromUsage/CreateFromUsageFixBase.kt b/idea/src/org/jetbrains/kotlin/idea/quickfix/createFromUsage/CreateFromUsageFixBase.kt index feed0c8f9b6..a71bd02bb68 100644 --- a/idea/src/org/jetbrains/kotlin/idea/quickfix/createFromUsage/CreateFromUsageFixBase.kt +++ b/idea/src/org/jetbrains/kotlin/idea/quickfix/createFromUsage/CreateFromUsageFixBase.kt @@ -16,10 +16,10 @@ package org.jetbrains.kotlin.idea.quickfix.createFromUsage -import com.intellij.psi.PsiElement import org.jetbrains.kotlin.idea.JetBundle import org.jetbrains.kotlin.idea.quickfix.JetIntentionAction +import org.jetbrains.kotlin.psi.JetElement -public abstract class CreateFromUsageFixBase(element: PsiElement) : JetIntentionAction(element) { +public abstract class CreateFromUsageFixBase(element: T) : JetIntentionAction(element) { override fun getFamilyName(): String = JetBundle.message("create.from.usage.family") } diff --git a/idea/src/org/jetbrains/kotlin/idea/quickfix/createFromUsage/callableBuilder/typeUtils.kt b/idea/src/org/jetbrains/kotlin/idea/quickfix/createFromUsage/callableBuilder/typeUtils.kt index f5ed472e50c..008ce12f0a7 100644 --- a/idea/src/org/jetbrains/kotlin/idea/quickfix/createFromUsage/callableBuilder/typeUtils.kt +++ b/idea/src/org/jetbrains/kotlin/idea/quickfix/createFromUsage/callableBuilder/typeUtils.kt @@ -234,6 +234,16 @@ fun JetCallElement.getTypeInfoForTypeArguments(): List { return getTypeArguments().map { it.getTypeReference()?.let { TypeInfo(it, Variance.INVARIANT) } }.filterNotNull() } +fun JetCallExpression.getParameterInfos(): List { + val anyType = KotlinBuiltIns.getInstance().nullableAnyType + return valueArguments.map { + ParameterInfo( + it.getArgumentExpression()?.let { TypeInfo(it, Variance.IN_VARIANCE) } ?: TypeInfo(anyType, Variance.IN_VARIANCE), + it.getArgumentName()?.getReferenceExpression()?.getReferencedName() + ) + } +} + private fun TypePredicate.getRepresentativeTypes(): Set { return when (this) { is SingleType -> Collections.singleton(targetType) diff --git a/idea/src/org/jetbrains/kotlin/idea/quickfix/createFromUsage/createCallable/CreateBinaryOperationActionFactory.kt b/idea/src/org/jetbrains/kotlin/idea/quickfix/createFromUsage/createCallable/CreateBinaryOperationActionFactory.kt index 3c18b445657..54e14ac077b 100644 --- a/idea/src/org/jetbrains/kotlin/idea/quickfix/createFromUsage/createCallable/CreateBinaryOperationActionFactory.kt +++ b/idea/src/org/jetbrains/kotlin/idea/quickfix/createFromUsage/createCallable/CreateBinaryOperationActionFactory.kt @@ -16,44 +16,44 @@ package org.jetbrains.kotlin.idea.quickfix.createFromUsage.createCallable -import org.jetbrains.kotlin.diagnostics.Diagnostic -import com.intellij.codeInsight.intention.IntentionAction -import org.jetbrains.kotlin.psi.JetBinaryExpression -import org.jetbrains.kotlin.types.expressions.OperatorConventions -import org.jetbrains.kotlin.lexer.JetToken -import org.jetbrains.kotlin.types.Variance -import java.util.Collections import org.jetbrains.kotlin.builtins.KotlinBuiltIns -import org.jetbrains.kotlin.lexer.JetTokens +import org.jetbrains.kotlin.diagnostics.Diagnostic import org.jetbrains.kotlin.idea.quickfix.createFromUsage.callableBuilder.* -import org.jetbrains.kotlin.idea.quickfix.JetIntentionActionsFactory +import org.jetbrains.kotlin.lexer.JetToken +import org.jetbrains.kotlin.lexer.JetTokens +import org.jetbrains.kotlin.psi.JetBinaryExpression +import org.jetbrains.kotlin.types.Variance +import org.jetbrains.kotlin.types.expressions.OperatorConventions +import java.util.Collections -public object CreateBinaryOperationActionFactory: JetIntentionActionsFactory() { - override fun doCreateActions(diagnostic: Diagnostic): List? { - val callExpr = diagnostic.getPsiElement().getParent() as? JetBinaryExpression ?: return null - val token = callExpr.getOperationToken() as JetToken +public object CreateBinaryOperationActionFactory: CreateCallableMemberFromUsageFactory() { + override fun getElementOfInterest(diagnostic: Diagnostic): JetBinaryExpression? { + return diagnostic.psiElement.parent as? JetBinaryExpression + } + + override fun createCallableInfo(element: JetBinaryExpression, diagnostic: Diagnostic): CallableInfo? { + val token = element.operationToken as JetToken val operationName = when (token) { - JetTokens.IDENTIFIER -> callExpr.getOperationReference().getReferencedName() - else -> OperatorConventions.getNameForOperationSymbol(token)?.asString() - } ?: return null + JetTokens.IDENTIFIER -> element.operationReference.getReferencedName() + else -> OperatorConventions.getNameForOperationSymbol(token)?.asString() + } ?: return null val inOperation = token in OperatorConventions.IN_OPERATIONS val comparisonOperation = token in OperatorConventions.COMPARISON_OPERATIONS - val leftExpr = callExpr.getLeft() ?: return null - val rightExpr = callExpr.getRight() ?: return null + val leftExpr = element.left ?: return null + val rightExpr = element.right ?: return null val receiverExpr = if (inOperation) rightExpr else leftExpr val argumentExpr = if (inOperation) leftExpr else rightExpr val builtIns = KotlinBuiltIns.getInstance() - val receiverType = TypeInfo(receiverExpr, Variance.IN_VARIANCE) val returnType = when { - inOperation -> TypeInfo.ByType(builtIns.getBooleanType(), Variance.INVARIANT).noSubstitutions() - comparisonOperation -> TypeInfo.ByType(builtIns.getIntType(), Variance.INVARIANT).noSubstitutions() - else -> TypeInfo(callExpr, Variance.OUT_VARIANCE) + inOperation -> TypeInfo.ByType(builtIns.booleanType, Variance.INVARIANT).noSubstitutions() + comparisonOperation -> TypeInfo.ByType(builtIns.intType, Variance.INVARIANT).noSubstitutions() + else -> TypeInfo(element, Variance.OUT_VARIANCE) } val parameters = Collections.singletonList(ParameterInfo(TypeInfo(argumentExpr, Variance.IN_VARIANCE))) - return CreateCallableFromUsageFixes(callExpr, FunctionInfo(operationName, receiverType, returnType, Collections.emptyList(), parameters)) + return FunctionInfo(operationName, receiverType, returnType, Collections.emptyList(), parameters) } } diff --git a/idea/src/org/jetbrains/kotlin/idea/quickfix/createFromUsage/createCallable/CreateCallableFromCallActionFactory.kt b/idea/src/org/jetbrains/kotlin/idea/quickfix/createFromUsage/createCallable/CreateCallableFromCallActionFactory.kt index 7b638af15c9..579927e8f80 100644 --- a/idea/src/org/jetbrains/kotlin/idea/quickfix/createFromUsage/createCallable/CreateCallableFromCallActionFactory.kt +++ b/idea/src/org/jetbrains/kotlin/idea/quickfix/createFromUsage/createCallable/CreateCallableFromCallActionFactory.kt @@ -16,124 +16,91 @@ package org.jetbrains.kotlin.idea.quickfix.createFromUsage.createCallable -import org.jetbrains.kotlin.diagnostics.Diagnostic -import com.intellij.codeInsight.intention.IntentionAction -import org.jetbrains.kotlin.types.Variance -import org.jetbrains.kotlin.builtins.KotlinBuiltIns -import org.jetbrains.kotlin.lexer.JetTokens -import org.jetbrains.kotlin.resolve.calls.callUtil.getCall -import org.jetbrains.kotlin.resolve.scopes.receivers.ReceiverValue -import org.jetbrains.kotlin.resolve.scopes.receivers.Qualifier -import org.jetbrains.kotlin.idea.quickfix.createFromUsage.callableBuilder.* -import org.jetbrains.kotlin.diagnostics.Errors -import org.jetbrains.kotlin.psi.psiUtil.getNonStrictParentOfType -import java.util.Collections -import org.jetbrains.kotlin.psi.psiUtil.getAssignmentByLHS -import org.jetbrains.kotlin.resolve.BindingContext -import com.intellij.psi.util.PsiTreeUtil -import org.jetbrains.kotlin.idea.caches.resolve.analyze -import org.jetbrains.kotlin.idea.quickfix.JetIntentionActionsFactory -import org.jetbrains.kotlin.load.java.descriptors.JavaClassDescriptor -import org.jetbrains.kotlin.idea.codeInsight.DescriptorToSourceUtilsIde -import com.intellij.psi.PsiClass import com.intellij.openapi.project.Project +import com.intellij.psi.PsiClass +import com.intellij.psi.util.PsiTreeUtil +import org.jetbrains.kotlin.builtins.KotlinBuiltIns import org.jetbrains.kotlin.descriptors.ClassDescriptor import org.jetbrains.kotlin.descriptors.ConstructorDescriptor -import org.jetbrains.kotlin.idea.caches.resolve.analyzeAndGetResult +import org.jetbrains.kotlin.diagnostics.Diagnostic +import org.jetbrains.kotlin.diagnostics.Errors +import org.jetbrains.kotlin.idea.caches.resolve.analyze +import org.jetbrains.kotlin.idea.codeInsight.DescriptorToSourceUtilsIde import org.jetbrains.kotlin.idea.core.refactoring.canRefactor import org.jetbrains.kotlin.idea.core.refactoring.getExtractionContainers -import org.jetbrains.kotlin.idea.refactoring.* +import org.jetbrains.kotlin.idea.quickfix.createFromUsage.callableBuilder.* +import org.jetbrains.kotlin.lexer.JetTokens +import org.jetbrains.kotlin.load.java.descriptors.JavaClassDescriptor import org.jetbrains.kotlin.psi.* +import org.jetbrains.kotlin.psi.psiUtil.getAssignmentByLHS +import org.jetbrains.kotlin.psi.psiUtil.getNonStrictParentOfType +import org.jetbrains.kotlin.psi.psiUtil.getQualifiedExpressionForSelectorOrThis +import org.jetbrains.kotlin.resolve.BindingContext +import org.jetbrains.kotlin.resolve.calls.callUtil.getCall import org.jetbrains.kotlin.resolve.calls.callUtil.getResolvedCall -import org.jetbrains.kotlin.types.TypeUtils +import org.jetbrains.kotlin.resolve.scopes.receivers.Qualifier +import org.jetbrains.kotlin.resolve.scopes.receivers.ReceiverValue +import org.jetbrains.kotlin.resolve.source.getPsi +import org.jetbrains.kotlin.types.Variance import org.jetbrains.kotlin.types.typeUtil.isSubtypeOf -import java.util.ArrayList +import java.util.Collections -object CreateCallableFromCallActionFactory : JetIntentionActionsFactory() { - override fun doCreateActions(diagnostic: Diagnostic): List? { - val diagElement = diagnostic.getPsiElement() +sealed class CreateCallableFromCallActionFactory( + extensionsEnabled: Boolean = true +) : CreateCallableMemberFromUsageFactory(extensionsEnabled) { + protected abstract fun doCreateCallableInfo( + expression: E, + context: BindingContext, + name: String, + receiverType: TypeInfo, + possibleContainers: List + ): CallableInfo? + + protected fun getExpressionOfInterest(diagnostic: Diagnostic): JetExpression? { + val diagElement = diagnostic.psiElement if (PsiTreeUtil.getParentOfType( diagElement, javaClass(), javaClass(), javaClass() ) != null) return null - val callExpr = when (diagnostic.getFactory()) { - in Errors.UNRESOLVED_REFERENCE_DIAGNOSTICS, Errors.EXPRESSION_EXPECTED_PACKAGE_FOUND -> { - val parent = diagElement.getParent() - if (parent is JetCallExpression && parent.getCalleeExpression() == diagElement) parent else diagElement - } + return when (diagnostic.factory) { + in Errors.UNRESOLVED_REFERENCE_DIAGNOSTICS, Errors.EXPRESSION_EXPECTED_PACKAGE_FOUND -> { + val parent = diagElement.parent + if (parent is JetCallExpression && parent.calleeExpression == diagElement) parent else diagElement + } - Errors.NO_VALUE_FOR_PARAMETER, - Errors.TOO_MANY_ARGUMENTS -> diagElement.getNonStrictParentOfType() + Errors.NO_VALUE_FOR_PARAMETER, + Errors.TOO_MANY_ARGUMENTS -> diagElement.getNonStrictParentOfType() - else -> throw AssertionError("Unexpected diagnostic: ${diagnostic.getFactory()}") - } as? JetExpression ?: return null + else -> throw AssertionError("Unexpected diagnostic: ${diagnostic.factory}") + } as? JetExpression + } - val project = callExpr.getProject() + override fun createCallableInfo(element: E, diagnostic: Diagnostic): CallableInfo? { + val project = element.project - val calleeExpr = when (callExpr) { - is JetCallExpression -> callExpr.getCalleeExpression() - is JetSimpleNameExpression -> callExpr + val calleeExpr = when (element) { + is JetCallExpression -> element.calleeExpression + is JetSimpleNameExpression -> element else -> null } as? JetSimpleNameExpression ?: return null if (calleeExpr.getReferencedNameElementType() != JetTokens.IDENTIFIER) return null - val callParent = callExpr.getParent() - val fullCallExpr = - if (callParent is JetQualifiedExpression && callParent.getSelectorExpression() == callExpr) callParent else callExpr - val context = calleeExpr.analyze() - val receiver = callExpr.getCall(context)?.getExplicitReceiver() ?: ReceiverValue.NO_RECEIVER + val receiver = element.getCall(context)?.explicitReceiver ?: ReceiverValue.NO_RECEIVER val receiverType = getReceiverTypeInfo(context, project, receiver) ?: return null val possibleContainers = if (receiverType is TypeInfo.Empty) { - val containers = with(fullCallExpr.getExtractionContainers()) { - if (callExpr is JetCallExpression) this else filter { it is JetClassBody || it is JetFile } + val containers = with(element.getQualifiedExpressionForSelectorOrThis().getExtractionContainers()) { + if (element is JetCallExpression) this else filter { it is JetClassBody || it is JetFile } } if (containers.isNotEmpty()) containers else return null } else Collections.emptyList() - val name = calleeExpr.getReferencedName() - val anyType = KotlinBuiltIns.getInstance().getNullableAnyType() - - val callableInfos = ArrayList(2) - when (callExpr) { - is JetCallExpression -> { - val parameters = callExpr.getValueArguments().map { - ParameterInfo( - it.getArgumentExpression()?.let { TypeInfo(it, Variance.IN_VARIANCE) } ?: TypeInfo(anyType, Variance.IN_VARIANCE), - it.getArgumentName()?.getReferenceExpression()?.getReferencedName() - ) - } - val typeParameters = callExpr.getTypeInfoForTypeArguments() - val returnType = TypeInfo(fullCallExpr, Variance.OUT_VARIANCE) - callableInfos.add(FunctionInfo(name, receiverType, returnType, possibleContainers, parameters, typeParameters)) - - val expectedType = context[BindingContext.EXPECTED_EXPRESSION_TYPE, fullCallExpr] ?: anyType - val constructorDescriptor = callExpr.getResolvedCall(context)?.getResultingDescriptor() as? ConstructorDescriptor - val classDescriptor = constructorDescriptor?.getContainingDeclaration() as? ClassDescriptor - val klass = classDescriptor?.let { DescriptorToSourceUtilsIde.getAnyDeclaration(project, it) } - if ((klass is JetClass || klass is PsiClass) && klass.canRefactor() - && typeParameters.isEmpty() - && classDescriptor!!.getDefaultType().isSubtypeOf(expectedType)) { - callableInfos.add(SecondaryConstructorInfo(parameters, klass)) - } - } - - is JetSimpleNameExpression -> { - val varExpected = fullCallExpr.getAssignmentByLHS() != null - val returnType = TypeInfo( - fullCallExpr.getExpressionForTypeGuess(), - if (varExpected) Variance.INVARIANT else Variance.OUT_VARIANCE - ) - callableInfos.add(PropertyInfo(name, receiverType, returnType, varExpected, possibleContainers)) - } - } - - return callableInfos.flatMap{ CreateCallableFromUsageFixes(callExpr, it) } + return doCreateCallableInfo(element, context, calleeExpr.getReferencedName(), receiverType, possibleContainers) } private fun getReceiverTypeInfo(context: BindingContext, project: Project, receiver: ReceiverValue): TypeInfo? { @@ -146,9 +113,83 @@ object CreateCallableFromCallActionFactory : JetIntentionActionsFactory() { val classifier = receiver.classifier as? JavaClassDescriptor ?: return null val javaClass = DescriptorToSourceUtilsIde.getAnyDeclaration(project, classifier) as? PsiClass if (javaClass == null || !javaClass.canRefactor()) return null - TypeInfo.StaticContextRequired(TypeInfo(classifier.getDefaultType(), Variance.IN_VARIANCE)) + TypeInfo.StaticContextRequired(TypeInfo(classifier.defaultType, Variance.IN_VARIANCE)) } - else -> TypeInfo(receiver.getType(), Variance.IN_VARIANCE) + else -> TypeInfo(receiver.type, Variance.IN_VARIANCE) } } + + object Property: CreateCallableFromCallActionFactory() { + override fun getElementOfInterest(diagnostic: Diagnostic): JetSimpleNameExpression? { + return getExpressionOfInterest(diagnostic) as? JetSimpleNameExpression + } + + override fun doCreateCallableInfo( + expression: JetSimpleNameExpression, + context: BindingContext, + name: String, + receiverType: TypeInfo, + possibleContainers: List + ): CallableInfo? { + val fullCallExpr = expression.getQualifiedExpressionForSelectorOrThis() + val varExpected = fullCallExpr.getAssignmentByLHS() != null + val returnType = TypeInfo( + fullCallExpr.getExpressionForTypeGuess(), + if (varExpected) Variance.INVARIANT else Variance.OUT_VARIANCE + ) + return PropertyInfo(name, receiverType, returnType, varExpected, possibleContainers) + } + } + + object Function: CreateCallableFromCallActionFactory() { + override fun getElementOfInterest(diagnostic: Diagnostic): JetCallExpression? { + return getExpressionOfInterest(diagnostic) as? JetCallExpression + } + + override fun doCreateCallableInfo( + expression: JetCallExpression, + context: BindingContext, + name: String, + receiverType: TypeInfo, + possibleContainers: List + ): CallableInfo? { + val parameters = expression.getParameterInfos() + val typeParameters = expression.getTypeInfoForTypeArguments() + val returnType = TypeInfo(expression.getQualifiedExpressionForSelectorOrThis(), Variance.OUT_VARIANCE) + return FunctionInfo(name, receiverType, returnType, possibleContainers, parameters, typeParameters) + } + } + + object Constructor: CreateCallableFromCallActionFactory() { + override fun getElementOfInterest(diagnostic: Diagnostic): JetCallExpression? { + return getExpressionOfInterest(diagnostic) as? JetCallExpression + } + + override fun doCreateCallableInfo( + expression: JetCallExpression, + context: BindingContext, + name: String, + receiverType: TypeInfo, + possibleContainers: List + ): CallableInfo? { + if (expression.typeArguments.isNotEmpty()) return null + + val constructorDescriptor = expression.getResolvedCall(context)?.resultingDescriptor as? ConstructorDescriptor + val classDescriptor = constructorDescriptor?.containingDeclaration as? ClassDescriptor + val klass = classDescriptor?.source?.getPsi() + if ((klass !is JetClass && klass !is PsiClass) || !klass.canRefactor()) return null + + val expectedType = context[BindingContext.EXPECTED_EXPRESSION_TYPE, expression.getQualifiedExpressionForSelectorOrThis()] + ?: KotlinBuiltIns.getInstance().nullableAnyType + if (!classDescriptor!!.defaultType.isSubtypeOf(expectedType)) return null + + val parameters = expression.getParameterInfos() + + return SecondaryConstructorInfo(parameters, klass) + } + } + + companion object { + val INSTANCES = arrayOf(Function, Constructor, Property) + } } diff --git a/idea/src/org/jetbrains/kotlin/idea/quickfix/createFromUsage/createCallable/CreateCallableFromUsageFix.kt b/idea/src/org/jetbrains/kotlin/idea/quickfix/createFromUsage/createCallable/CreateCallableFromUsageFix.kt index 2dcbe735809..1832024f23e 100644 --- a/idea/src/org/jetbrains/kotlin/idea/quickfix/createFromUsage/createCallable/CreateCallableFromUsageFix.kt +++ b/idea/src/org/jetbrains/kotlin/idea/quickfix/createFromUsage/createCallable/CreateCallableFromUsageFix.kt @@ -21,35 +21,37 @@ import com.intellij.openapi.editor.Editor import com.intellij.openapi.project.Project import com.intellij.psi.PsiClass import com.intellij.psi.PsiElement +import com.intellij.psi.PsiFile import org.jetbrains.kotlin.idea.codeInsight.DescriptorToSourceUtilsIde import org.jetbrains.kotlin.idea.core.refactoring.canRefactor import org.jetbrains.kotlin.idea.core.refactoring.chooseContainerElementIfNecessary import org.jetbrains.kotlin.idea.quickfix.createFromUsage.CreateFromUsageFixBase import org.jetbrains.kotlin.idea.quickfix.createFromUsage.callableBuilder.* import org.jetbrains.kotlin.idea.util.application.executeCommand -import org.jetbrains.kotlin.psi.* +import org.jetbrains.kotlin.psi.JetClassBody +import org.jetbrains.kotlin.psi.JetClassOrObject +import org.jetbrains.kotlin.psi.JetElement +import org.jetbrains.kotlin.psi.JetFile import org.jetbrains.kotlin.psi.psiUtil.getElementTextWithContext -import java.util.Collections import java.util.HashSet -public class CreateCallableFromUsageFix(originalElement: JetElement, callableInfos: List) -: CreateCallableFromUsageFixBase(originalElement, callableInfos, false) { - constructor(originalExpression: JetElement, callableInfo: CallableInfo) : this(originalExpression, Collections.singletonList(callableInfo)) -} +public class CreateCallableFromUsageFix( + originalExpression: E, + callableInfos: List +) : CreateCallableFromUsageFixBase(originalExpression, callableInfos, false) -public class CreateExtensionCallableFromUsageFix(originalElement: JetElement, callableInfos: List) -: CreateCallableFromUsageFixBase(originalElement, callableInfos, true), LowPriorityAction { - constructor(originalExpression: JetElement, callableInfo: CallableInfo) : this(originalExpression, Collections.singletonList(callableInfo)) -} +public class CreateExtensionCallableFromUsageFix( + originalExpression: E, + callableInfos: List +) : CreateCallableFromUsageFixBase(originalExpression, callableInfos, true), LowPriorityAction -public abstract class CreateCallableFromUsageFixBase( - originalElement: JetElement, +public abstract class CreateCallableFromUsageFixBase( + originalExpression: E, val callableInfos: List, val isExtension: Boolean -) : CreateFromUsageFixBase(originalElement) { - +) : CreateFromUsageFixBase(originalExpression) { init { - assert (callableInfos.isNotEmpty()) { "No CallableInfos: ${originalElement.getElementTextWithContext()}" } + assert (callableInfos.isNotEmpty()) { "No CallableInfos: ${originalExpression.getElementTextWithContext()}" } if (callableInfos.size() > 1) { val receiverSet = callableInfos.mapTo(HashSet()) { it.receiverTypeInfo } if (receiverSet.size() > 1) throw AssertionError("All functions must have common receiver: $receiverSet") @@ -60,7 +62,7 @@ public abstract class CreateCallableFromUsageFixBase( } private fun getDeclarationIfApplicable(project: Project, candidate: TypeCandidate): PsiElement? { - val descriptor = candidate.theType.getConstructor().getDeclarationDescriptor() ?: return null + val descriptor = candidate.theType.constructor.declarationDescriptor ?: return null val declaration = DescriptorToSourceUtilsIde.getAnyDeclaration(project, descriptor) ?: return null if (declaration !is JetClassOrObject && declaration !is PsiClass) return null return if (isExtension || declaration.canRefactor()) declaration else null @@ -92,23 +94,23 @@ public abstract class CreateCallableFromUsageFixBase( }.toString() } - fun isAvailable(): Boolean { + override fun isAvailable(project: Project, editor: Editor?, file: PsiFile): Boolean { + if (!super.isAvailable(project, editor, file)) return false + if (file !is JetFile) return false + val receiverInfo = callableInfos.first().receiverTypeInfo if (receiverInfo is TypeInfo.Empty) return !isExtension // TODO: Remove after companion object extensions are supported if (isExtension && receiverInfo.staticContextRequired) return false - val file = element.getContainingFile() as JetFile - val project = file.getProject() - val callableBuilder = - CallableBuilderConfiguration(callableInfos, element as JetExpression, file, null, isExtension).createBuilder() + val callableBuilder = CallableBuilderConfiguration(callableInfos, element, file, null, isExtension).createBuilder() val receiverTypeCandidates = callableBuilder.computeTypeCandidates(callableInfos.first().receiverTypeInfo) val propertyInfo = callableInfos.firstOrNull { it is PropertyInfo } as PropertyInfo? val isFunction = callableInfos.any { it.kind == CallableKind.FUNCTION } return receiverTypeCandidates.any { val declaration = getDeclarationIfApplicable(project, it) - val insertToJavaInterface = declaration is PsiClass && declaration.isInterface() + val insertToJavaInterface = declaration is PsiClass && declaration.isInterface when { propertyInfo != null && insertToJavaInterface && (!receiverInfo.staticContextRequired || propertyInfo.writable) -> false @@ -128,7 +130,7 @@ public abstract class CreateCallableFromUsageFixBase( fun runBuilder(placement: CallablePlacement) { callableBuilder.placement = placement - project.executeCommand(getText()) { callableBuilder.build() } + project.executeCommand(text) { callableBuilder.build() } } if (callableInfo is SecondaryConstructorInfo) { @@ -149,30 +151,13 @@ public abstract class CreateCallableFromUsageFixBase( } else { assert(callableInfo.receiverTypeInfo is TypeInfo.Empty) { - "No receiver type candidates: ${element.getText()} in ${file.getText()}" + "No receiver type candidates: ${element.text} in ${file.text}" } chooseContainerElementIfNecessary(callableInfo.possibleContainers, editor, popupTitle, true, { it }) { - val container = if (it is JetClassBody) it.getParent() as JetClassOrObject else it + val container = if (it is JetClassBody) it.parent as JetClassOrObject else it runBuilder(CallablePlacement.NoReceiver(container)) } } } } - -public fun CreateCallableFromUsageFixes( - originalExpression: JetExpression, - callableInfos: List -) : List { - return listOf( - CreateCallableFromUsageFix(originalExpression, callableInfos), - CreateExtensionCallableFromUsageFix(originalExpression, callableInfos) - ).filter { it.isAvailable() } -} - -public fun CreateCallableFromUsageFixes( - originalExpression: JetExpression, - callableInfo: CallableInfo -) : List { - return CreateCallableFromUsageFixes(originalExpression, Collections.singletonList(callableInfo)) -} diff --git a/idea/src/org/jetbrains/kotlin/idea/quickfix/createFromUsage/createCallable/CreateCallableMemberFromUsageFactory.kt b/idea/src/org/jetbrains/kotlin/idea/quickfix/createFromUsage/createCallable/CreateCallableMemberFromUsageFactory.kt new file mode 100644 index 00000000000..0b803f3a6ce --- /dev/null +++ b/idea/src/org/jetbrains/kotlin/idea/quickfix/createFromUsage/createCallable/CreateCallableMemberFromUsageFactory.kt @@ -0,0 +1,63 @@ +/* + * Copyright 2010-2015 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.createCallable + +import com.intellij.psi.SmartPsiElementPointer +import org.jetbrains.kotlin.diagnostics.Diagnostic +import org.jetbrains.kotlin.idea.quickfix.NullQuickFix +import org.jetbrains.kotlin.idea.quickfix.QuickFixWithDelegateFactory +import org.jetbrains.kotlin.idea.quickfix.createFromUsage.CreateFromUsageFactory +import org.jetbrains.kotlin.idea.quickfix.createFromUsage.callableBuilder.CallableInfo +import org.jetbrains.kotlin.psi.JetElement +import org.jetbrains.kotlin.utils.addToStdlib.singletonOrEmptyList + +public abstract class CreateCallableMemberFromUsageFactory( + val extensionsSupported: Boolean = true +) : CreateFromUsageFactory>() { + private fun newCallableQuickFix( + originalElementPointer: SmartPsiElementPointer, + quickFixDataFactory: (SmartPsiElementPointer) -> List, + quickFixFactory: (E, List) -> CreateCallableFromUsageFixBase + ): QuickFixWithDelegateFactory { + return QuickFixWithDelegateFactory { + val data = quickFixDataFactory(originalElementPointer).orEmpty() + val originalElement = originalElementPointer.element + if (data.isNotEmpty() && originalElement != null) quickFixFactory(originalElement, data) else NullQuickFix + } + } + + protected open fun createCallableInfo(element: E, diagnostic: Diagnostic): CallableInfo? = null + + override fun createQuickFixData(element: E, diagnostic: Diagnostic): List + = createCallableInfo(element, diagnostic).singletonOrEmptyList() + + override fun createQuickFixes( + originalElementPointer: SmartPsiElementPointer, + diagnostic: Diagnostic, + quickFixDataFactory: (SmartPsiElementPointer) -> List + ): List { + val memberFix = newCallableQuickFix(originalElementPointer, quickFixDataFactory) { element, data -> + CreateCallableFromUsageFix(element, data) + } + if (!extensionsSupported) return listOf(memberFix) + + val extensionFix = newCallableQuickFix(originalElementPointer, quickFixDataFactory) { element, data -> + CreateExtensionCallableFromUsageFix(element, data) + } + return listOf(memberFix, extensionFix) + } +} \ No newline at end of file diff --git a/idea/src/org/jetbrains/kotlin/idea/quickfix/createFromUsage/createCallable/CreateComponentFunctionActionFactory.kt b/idea/src/org/jetbrains/kotlin/idea/quickfix/createFromUsage/createCallable/CreateComponentFunctionActionFactory.kt index af986fde6cc..ab6c3208115 100644 --- a/idea/src/org/jetbrains/kotlin/idea/quickfix/createFromUsage/createCallable/CreateComponentFunctionActionFactory.kt +++ b/idea/src/org/jetbrains/kotlin/idea/quickfix/createFromUsage/createCallable/CreateComponentFunctionActionFactory.kt @@ -17,39 +17,38 @@ package org.jetbrains.kotlin.idea.quickfix.createFromUsage.createCallable import org.jetbrains.kotlin.diagnostics.Diagnostic -import com.intellij.codeInsight.intention.IntentionAction -import org.jetbrains.kotlin.resolve.dataClassUtils.* import org.jetbrains.kotlin.diagnostics.Errors import org.jetbrains.kotlin.idea.core.quickfix.QuickFixUtil -import org.jetbrains.kotlin.psi.JetMultiDeclaration +import org.jetbrains.kotlin.idea.quickfix.createFromUsage.callableBuilder.CallableInfo +import org.jetbrains.kotlin.idea.quickfix.createFromUsage.callableBuilder.FunctionInfo +import org.jetbrains.kotlin.idea.quickfix.createFromUsage.callableBuilder.TypeInfo import org.jetbrains.kotlin.psi.JetForExpression +import org.jetbrains.kotlin.psi.JetMultiDeclaration +import org.jetbrains.kotlin.resolve.dataClassUtils.getComponentIndex +import org.jetbrains.kotlin.resolve.dataClassUtils.isComponentLike import org.jetbrains.kotlin.types.Variance -import org.jetbrains.kotlin.idea.quickfix.createFromUsage.callableBuilder.* -import org.jetbrains.kotlin.idea.quickfix.JetIntentionActionsFactory -object CreateComponentFunctionActionFactory : JetIntentionActionsFactory() { - override fun doCreateActions(diagnostic: Diagnostic): List? { +object CreateComponentFunctionActionFactory : CreateCallableMemberFromUsageFactory() { + override fun getElementOfInterest(diagnostic: Diagnostic): JetMultiDeclaration? { + QuickFixUtil.getParentElementOfType(diagnostic, javaClass())?.let { return it } + return QuickFixUtil.getParentElementOfType(diagnostic, javaClass())?.multiParameter + } + + override fun createCallableInfo(element: JetMultiDeclaration, diagnostic: Diagnostic): CallableInfo? { val diagnosticWithParameters = Errors.COMPONENT_FUNCTION_MISSING.cast(diagnostic) - val name = diagnosticWithParameters.getA() + + val name = diagnosticWithParameters.a if (!isComponentLike(name)) return null val componentNumber = getComponentIndex(name) - 1 - var multiDeclaration = QuickFixUtil.getParentElementOfType(diagnostic, javaClass()) - val ownerType = if (multiDeclaration == null) { - val forExpr = QuickFixUtil.getParentElementOfType(diagnostic, javaClass())!! - multiDeclaration = forExpr.getMultiParameter()!! - TypeInfo(diagnosticWithParameters.getB(), Variance.IN_VARIANCE) - } - else { - val rhs = multiDeclaration.getInitializer() ?: return null - TypeInfo(rhs, Variance.IN_VARIANCE) - } - val entries = multiDeclaration.getEntries() + val ownerType = element.initializer?.let { TypeInfo(it, Variance.IN_VARIANCE) } + ?: TypeInfo(diagnosticWithParameters.b, Variance.IN_VARIANCE) + val entries = element.entries val entry = entries[componentNumber] val returnType = TypeInfo(entry, Variance.OUT_VARIANCE) - return CreateCallableFromUsageFixes(multiDeclaration, FunctionInfo(name.getIdentifier(), ownerType, returnType)) + return FunctionInfo(name.identifier, ownerType, returnType) } } diff --git a/idea/src/org/jetbrains/kotlin/idea/quickfix/createFromUsage/createCallable/CreateConstructorFromDelegationCallActionFactory.kt b/idea/src/org/jetbrains/kotlin/idea/quickfix/createFromUsage/createCallable/CreateConstructorFromDelegationCallActionFactory.kt index 1182d883651..7b2e3c6dcb0 100644 --- a/idea/src/org/jetbrains/kotlin/idea/quickfix/createFromUsage/createCallable/CreateConstructorFromDelegationCallActionFactory.kt +++ b/idea/src/org/jetbrains/kotlin/idea/quickfix/createFromUsage/createCallable/CreateConstructorFromDelegationCallActionFactory.kt @@ -16,7 +16,6 @@ package org.jetbrains.kotlin.idea.quickfix.createFromUsage.createCallable -import com.intellij.codeInsight.intention.IntentionAction import com.intellij.psi.PsiClass import org.jetbrains.kotlin.builtins.KotlinBuiltIns import org.jetbrains.kotlin.descriptors.ClassDescriptor @@ -24,45 +23,48 @@ import org.jetbrains.kotlin.descriptors.ClassKind import org.jetbrains.kotlin.diagnostics.Diagnostic import org.jetbrains.kotlin.idea.caches.resolve.resolveToDescriptor import org.jetbrains.kotlin.idea.codeInsight.DescriptorToSourceUtilsIde -import org.jetbrains.kotlin.idea.quickfix.JetSingleIntentionActionFactory +import org.jetbrains.kotlin.idea.core.refactoring.canRefactor +import org.jetbrains.kotlin.idea.quickfix.createFromUsage.callableBuilder.CallableInfo import org.jetbrains.kotlin.idea.quickfix.createFromUsage.callableBuilder.ParameterInfo import org.jetbrains.kotlin.idea.quickfix.createFromUsage.callableBuilder.SecondaryConstructorInfo import org.jetbrains.kotlin.idea.quickfix.createFromUsage.callableBuilder.TypeInfo -import org.jetbrains.kotlin.idea.core.refactoring.canRefactor import org.jetbrains.kotlin.psi.JetClass import org.jetbrains.kotlin.psi.JetConstructorDelegationCall import org.jetbrains.kotlin.psi.psiUtil.getStrictParentOfType import org.jetbrains.kotlin.resolve.DescriptorUtils import org.jetbrains.kotlin.types.Variance -object CreateConstructorFromDelegationCallActionFactory : JetSingleIntentionActionFactory() { - override fun createAction(diagnostic: Diagnostic): IntentionAction? { - val delegationCall = diagnostic.getPsiElement().getStrictParentOfType() ?: return null - val calleeExpression = delegationCall.getCalleeExpression() ?: return null - val currentClass = delegationCall.getStrictParentOfType() ?: return null +object CreateConstructorFromDelegationCallActionFactory : CreateCallableMemberFromUsageFactory() { + override fun getElementOfInterest(diagnostic: Diagnostic): JetConstructorDelegationCall? { + return diagnostic.psiElement.getStrictParentOfType() + } - val project = currentClass.getProject() + override fun createCallableInfo(element: JetConstructorDelegationCall, diagnostic: Diagnostic): CallableInfo? { + val calleeExpression = element.calleeExpression ?: return null + val currentClass = element.getStrictParentOfType() ?: return null + + val project = currentClass.project val classDescriptor = currentClass.resolveToDescriptor() as? ClassDescriptor ?: return null - val targetClass = if (calleeExpression.isThis()) { + val targetClass = if (calleeExpression.isThis) { currentClass } else { - val superClassDescriptor = DescriptorUtils.getSuperclassDescriptors(classDescriptor) - .singleOrNull { it.getKind() == ClassKind.CLASS } ?: return null + val superClassDescriptor = + DescriptorUtils.getSuperclassDescriptors(classDescriptor).singleOrNull { it.kind == ClassKind.CLASS } ?: return null DescriptorToSourceUtilsIde.getAnyDeclaration(project, superClassDescriptor) ?: return null } if (!(targetClass.canRefactor() && (targetClass is JetClass || targetClass is PsiClass))) return null - val anyType = KotlinBuiltIns.getInstance().getNullableAnyType() - val parameters = delegationCall.getValueArguments().map { + val anyType = KotlinBuiltIns.getInstance().nullableAnyType + val parameters = element.valueArguments.map { ParameterInfo( it.getArgumentExpression()?.let { TypeInfo(it, Variance.IN_VARIANCE) } ?: TypeInfo(anyType, Variance.IN_VARIANCE), it.getArgumentName()?.asName?.asString() ) } - return CreateCallableFromUsageFix(delegationCall, SecondaryConstructorInfo(parameters, targetClass)) + return SecondaryConstructorInfo(parameters, targetClass) } } diff --git a/idea/src/org/jetbrains/kotlin/idea/quickfix/createFromUsage/createCallable/CreateConstructorFromDelegatorToSuperCallActionFactory.kt b/idea/src/org/jetbrains/kotlin/idea/quickfix/createFromUsage/createCallable/CreateConstructorFromDelegatorToSuperCallActionFactory.kt index 512448e3a6c..db42472afcc 100644 --- a/idea/src/org/jetbrains/kotlin/idea/quickfix/createFromUsage/createCallable/CreateConstructorFromDelegatorToSuperCallActionFactory.kt +++ b/idea/src/org/jetbrains/kotlin/idea/quickfix/createFromUsage/createCallable/CreateConstructorFromDelegatorToSuperCallActionFactory.kt @@ -16,49 +16,48 @@ package org.jetbrains.kotlin.idea.quickfix.createFromUsage.createCallable -import com.intellij.codeInsight.intention.IntentionAction import com.intellij.psi.PsiClass import org.jetbrains.kotlin.builtins.KotlinBuiltIns import org.jetbrains.kotlin.descriptors.ClassDescriptor import org.jetbrains.kotlin.descriptors.ClassKind import org.jetbrains.kotlin.diagnostics.Diagnostic import org.jetbrains.kotlin.idea.caches.resolve.analyze -import org.jetbrains.kotlin.idea.caches.resolve.resolveToDescriptor import org.jetbrains.kotlin.idea.codeInsight.DescriptorToSourceUtilsIde -import org.jetbrains.kotlin.idea.quickfix.JetSingleIntentionActionFactory +import org.jetbrains.kotlin.idea.core.refactoring.canRefactor +import org.jetbrains.kotlin.idea.quickfix.createFromUsage.callableBuilder.CallableInfo import org.jetbrains.kotlin.idea.quickfix.createFromUsage.callableBuilder.ParameterInfo import org.jetbrains.kotlin.idea.quickfix.createFromUsage.callableBuilder.SecondaryConstructorInfo import org.jetbrains.kotlin.idea.quickfix.createFromUsage.callableBuilder.TypeInfo -import org.jetbrains.kotlin.idea.core.refactoring.canRefactor import org.jetbrains.kotlin.psi.JetClass -import org.jetbrains.kotlin.psi.JetConstructorDelegationCall import org.jetbrains.kotlin.psi.JetDelegatorToSuperCall import org.jetbrains.kotlin.psi.psiUtil.getStrictParentOfType import org.jetbrains.kotlin.resolve.BindingContext -import org.jetbrains.kotlin.resolve.DescriptorUtils import org.jetbrains.kotlin.types.Variance -object CreateConstructorFromDelegatorToSuperCallActionFactory : JetSingleIntentionActionFactory() { - override fun createAction(diagnostic: Diagnostic): IntentionAction? { - val delegationCall = diagnostic.getPsiElement().getStrictParentOfType() ?: return null - val typeReference = delegationCall.getCalleeExpression().getTypeReference() ?: return null +object CreateConstructorFromDelegatorToSuperCallActionFactory : CreateCallableMemberFromUsageFactory() { + override fun getElementOfInterest(diagnostic: Diagnostic): JetDelegatorToSuperCall? { + return diagnostic.psiElement.getStrictParentOfType() + } - val project = delegationCall.getProject() + override fun createCallableInfo(element: JetDelegatorToSuperCall, diagnostic: Diagnostic): CallableInfo? { + val typeReference = element.calleeExpression.typeReference ?: return null + + val project = element.project val superType = typeReference.analyze()[BindingContext.TYPE, typeReference] ?: return null - val superClassDescriptor = superType.getConstructor().getDeclarationDescriptor() as? ClassDescriptor ?: return null - if (superClassDescriptor.getKind() != ClassKind.CLASS) return null + val superClassDescriptor = superType.constructor.declarationDescriptor as? ClassDescriptor ?: return null + if (superClassDescriptor.kind != ClassKind.CLASS) return null val targetClass = DescriptorToSourceUtilsIde.getAnyDeclaration(project, superClassDescriptor) ?: return null if (!(targetClass.canRefactor() && (targetClass is JetClass || targetClass is PsiClass))) return null - val anyType = KotlinBuiltIns.getInstance().getNullableAnyType() - val parameters = delegationCall.getValueArguments().map { + val anyType = KotlinBuiltIns.getInstance().nullableAnyType + val parameters = element.valueArguments.map { ParameterInfo( it.getArgumentExpression()?.let { TypeInfo(it, Variance.IN_VARIANCE) } ?: TypeInfo(anyType, Variance.IN_VARIANCE), it.getArgumentName()?.asName?.asString() ) } - return CreateCallableFromUsageFix(delegationCall, SecondaryConstructorInfo(parameters, targetClass)) + return SecondaryConstructorInfo(parameters, targetClass) } } diff --git a/idea/src/org/jetbrains/kotlin/idea/quickfix/createFromUsage/createCallable/CreateGetFunctionActionFactory.kt b/idea/src/org/jetbrains/kotlin/idea/quickfix/createFromUsage/createCallable/CreateGetFunctionActionFactory.kt index 4969a3f5725..94342f3084f 100644 --- a/idea/src/org/jetbrains/kotlin/idea/quickfix/createFromUsage/createCallable/CreateGetFunctionActionFactory.kt +++ b/idea/src/org/jetbrains/kotlin/idea/quickfix/createFromUsage/createCallable/CreateGetFunctionActionFactory.kt @@ -17,25 +17,26 @@ package org.jetbrains.kotlin.idea.quickfix.createFromUsage.createCallable import org.jetbrains.kotlin.diagnostics.Diagnostic -import com.intellij.codeInsight.intention.IntentionAction import org.jetbrains.kotlin.idea.core.quickfix.QuickFixUtil +import org.jetbrains.kotlin.idea.quickfix.createFromUsage.callableBuilder.CallableInfo +import org.jetbrains.kotlin.idea.quickfix.createFromUsage.callableBuilder.FunctionInfo +import org.jetbrains.kotlin.idea.quickfix.createFromUsage.callableBuilder.ParameterInfo +import org.jetbrains.kotlin.idea.quickfix.createFromUsage.callableBuilder.TypeInfo import org.jetbrains.kotlin.psi.JetArrayAccessExpression import org.jetbrains.kotlin.types.Variance -import org.jetbrains.kotlin.idea.quickfix.createFromUsage.callableBuilder.* import java.util.Collections -import org.jetbrains.kotlin.idea.quickfix.JetIntentionActionsFactory -object CreateGetFunctionActionFactory : JetIntentionActionsFactory() { - override fun doCreateActions(diagnostic: Diagnostic): List? { - val accessExpr = QuickFixUtil.getParentElementOfType(diagnostic, javaClass()) ?: return null - val arrayExpr = accessExpr.getArrayExpression() ?: return null +object CreateGetFunctionActionFactory : CreateCallableMemberFromUsageFactory() { + override fun getElementOfInterest(diagnostic: Diagnostic): JetArrayAccessExpression? { + return QuickFixUtil.getParentElementOfType(diagnostic, javaClass()) + } + + override fun createCallableInfo(element: JetArrayAccessExpression, diagnostic: Diagnostic): CallableInfo? { + val arrayExpr = element.arrayExpression ?: return null + val arrayType = TypeInfo(arrayExpr, Variance.IN_VARIANCE) - - val parameters = accessExpr.getIndexExpressions().map { - ParameterInfo(TypeInfo(it, Variance.IN_VARIANCE)) - } - - val returnType = TypeInfo(accessExpr, Variance.OUT_VARIANCE) - return CreateCallableFromUsageFixes(accessExpr, FunctionInfo("get", arrayType, returnType, Collections.emptyList(), parameters)) + val parameters = element.indexExpressions.map { ParameterInfo(TypeInfo(it, Variance.IN_VARIANCE)) } + val returnType = TypeInfo(element, Variance.OUT_VARIANCE) + return FunctionInfo("get", arrayType, returnType, Collections.emptyList(), parameters) } } diff --git a/idea/src/org/jetbrains/kotlin/idea/quickfix/createFromUsage/createCallable/CreateHasNextFunctionActionFactory.kt b/idea/src/org/jetbrains/kotlin/idea/quickfix/createFromUsage/createCallable/CreateHasNextFunctionActionFactory.kt index 4821912fa3b..bc317a017b9 100644 --- a/idea/src/org/jetbrains/kotlin/idea/quickfix/createFromUsage/createCallable/CreateHasNextFunctionActionFactory.kt +++ b/idea/src/org/jetbrains/kotlin/idea/quickfix/createFromUsage/createCallable/CreateHasNextFunctionActionFactory.kt @@ -16,24 +16,27 @@ package org.jetbrains.kotlin.idea.quickfix.createFromUsage.createCallable +import org.jetbrains.kotlin.builtins.KotlinBuiltIns import org.jetbrains.kotlin.diagnostics.Diagnostic -import com.intellij.codeInsight.intention.IntentionAction import org.jetbrains.kotlin.diagnostics.DiagnosticFactory import org.jetbrains.kotlin.diagnostics.Errors -import org.jetbrains.kotlin.types.Variance import org.jetbrains.kotlin.idea.core.quickfix.QuickFixUtil +import org.jetbrains.kotlin.idea.quickfix.createFromUsage.callableBuilder.CallableInfo +import org.jetbrains.kotlin.idea.quickfix.createFromUsage.callableBuilder.FunctionInfo +import org.jetbrains.kotlin.idea.quickfix.createFromUsage.callableBuilder.TypeInfo import org.jetbrains.kotlin.psi.JetForExpression -import org.jetbrains.kotlin.builtins.KotlinBuiltIns -import org.jetbrains.kotlin.idea.quickfix.createFromUsage.callableBuilder.* -import org.jetbrains.kotlin.idea.quickfix.JetIntentionActionsFactory +import org.jetbrains.kotlin.types.Variance -object CreateHasNextFunctionActionFactory : JetIntentionActionsFactory() { - override fun doCreateActions(diagnostic: Diagnostic): List? { - val diagnosticWithParameters = DiagnosticFactory.cast(diagnostic, Errors.HAS_NEXT_MISSING, Errors.HAS_NEXT_FUNCTION_NONE_APPLICABLE) - val ownerType = TypeInfo(diagnosticWithParameters.getA(), Variance.IN_VARIANCE) +object CreateHasNextFunctionActionFactory : CreateCallableMemberFromUsageFactory() { + override fun getElementOfInterest(diagnostic: Diagnostic): JetForExpression? { + return QuickFixUtil.getParentElementOfType(diagnostic, javaClass()) + } - val forExpr = QuickFixUtil.getParentElementOfType(diagnostic, javaClass()) ?: return null - val returnType = TypeInfo(KotlinBuiltIns.getInstance().getBooleanType(), Variance.OUT_VARIANCE) - return CreateCallableFromUsageFixes(forExpr, FunctionInfo("hasNext", ownerType, returnType)) + override fun createCallableInfo(element: JetForExpression, diagnostic: Diagnostic): CallableInfo? { + val diagnosticWithParameters = + DiagnosticFactory.cast(diagnostic, Errors.HAS_NEXT_MISSING, Errors.HAS_NEXT_FUNCTION_NONE_APPLICABLE) + val ownerType = TypeInfo(diagnosticWithParameters.a, Variance.IN_VARIANCE) + val returnType = TypeInfo(KotlinBuiltIns.getInstance().booleanType, Variance.OUT_VARIANCE) + return FunctionInfo("hasNext", ownerType, returnType) } } diff --git a/idea/src/org/jetbrains/kotlin/idea/quickfix/createFromUsage/createCallable/CreateInvokeFunctionActionFactory.kt b/idea/src/org/jetbrains/kotlin/idea/quickfix/createFromUsage/createCallable/CreateInvokeFunctionActionFactory.kt index 63f6cec0773..9913ddaed19 100644 --- a/idea/src/org/jetbrains/kotlin/idea/quickfix/createFromUsage/createCallable/CreateInvokeFunctionActionFactory.kt +++ b/idea/src/org/jetbrains/kotlin/idea/quickfix/createFromUsage/createCallable/CreateInvokeFunctionActionFactory.kt @@ -16,11 +16,10 @@ package org.jetbrains.kotlin.idea.quickfix.createFromUsage.createCallable -import com.intellij.codeInsight.intention.IntentionAction import org.jetbrains.kotlin.builtins.KotlinBuiltIns import org.jetbrains.kotlin.diagnostics.Diagnostic import org.jetbrains.kotlin.diagnostics.Errors -import org.jetbrains.kotlin.idea.quickfix.JetIntentionActionsFactory +import org.jetbrains.kotlin.idea.quickfix.createFromUsage.callableBuilder.CallableInfo import org.jetbrains.kotlin.idea.quickfix.createFromUsage.callableBuilder.FunctionInfo import org.jetbrains.kotlin.idea.quickfix.createFromUsage.callableBuilder.ParameterInfo import org.jetbrains.kotlin.idea.quickfix.createFromUsage.callableBuilder.TypeInfo @@ -28,27 +27,26 @@ import org.jetbrains.kotlin.psi.JetCallExpression import org.jetbrains.kotlin.types.Variance import org.jetbrains.kotlin.types.expressions.OperatorConventions -object CreateInvokeFunctionActionFactory : JetIntentionActionsFactory() { - override fun doCreateActions(diagnostic: Diagnostic): List? { - val callExpr = diagnostic.getPsiElement().getParent() as? JetCallExpression ?: return null +object CreateInvokeFunctionActionFactory : CreateCallableMemberFromUsageFactory() { + override fun getElementOfInterest(diagnostic: Diagnostic): JetCallExpression? { + return diagnostic.psiElement.parent as? JetCallExpression + } - val expectedType = Errors.FUNCTION_EXPECTED.cast(diagnostic).getB() - if (expectedType.isError()) return null + override fun createCallableInfo(element: JetCallExpression, diagnostic: Diagnostic): CallableInfo? { + val expectedType = Errors.FUNCTION_EXPECTED.cast(diagnostic).b + if (expectedType.isError) return null val receiverType = TypeInfo(expectedType, Variance.IN_VARIANCE) - val anyType = KotlinBuiltIns.getInstance().getNullableAnyType() - val parameters = callExpr.getValueArguments().map { + val anyType = KotlinBuiltIns.getInstance().nullableAnyType + val parameters = element.valueArguments.map { ParameterInfo( it.getArgumentExpression()?.let { TypeInfo(it, Variance.IN_VARIANCE) } ?: TypeInfo(anyType, Variance.IN_VARIANCE), it.getArgumentName()?.getReferenceExpression()?.getReferencedName() ) } - val returnType = TypeInfo(callExpr, Variance.OUT_VARIANCE) - return CreateCallableFromUsageFixes( - callExpr, - FunctionInfo(OperatorConventions.INVOKE.asString(), receiverType, returnType, emptyList(), parameters) - ) + val returnType = TypeInfo(element, Variance.OUT_VARIANCE) + return FunctionInfo(OperatorConventions.INVOKE.asString(), receiverType, returnType, emptyList(), parameters) } } diff --git a/idea/src/org/jetbrains/kotlin/idea/quickfix/createFromUsage/createCallable/CreateIteratorFunctionActionFactory.kt b/idea/src/org/jetbrains/kotlin/idea/quickfix/createFromUsage/createCallable/CreateIteratorFunctionActionFactory.kt index a06db0f49d4..c565af7553c 100644 --- a/idea/src/org/jetbrains/kotlin/idea/quickfix/createFromUsage/createCallable/CreateIteratorFunctionActionFactory.kt +++ b/idea/src/org/jetbrains/kotlin/idea/quickfix/createFromUsage/createCallable/CreateIteratorFunctionActionFactory.kt @@ -16,30 +16,33 @@ package org.jetbrains.kotlin.idea.quickfix.createFromUsage.createCallable -import org.jetbrains.kotlin.diagnostics.Diagnostic -import com.intellij.codeInsight.intention.IntentionAction -import org.jetbrains.kotlin.psi.JetFile -import org.jetbrains.kotlin.idea.core.quickfix.QuickFixUtil -import org.jetbrains.kotlin.psi.JetForExpression -import org.jetbrains.kotlin.psi.JetExpression -import org.jetbrains.kotlin.types.Variance import org.jetbrains.kotlin.builtins.KotlinBuiltIns -import org.jetbrains.kotlin.idea.caches.resolve.analyzeFully +import org.jetbrains.kotlin.diagnostics.Diagnostic import org.jetbrains.kotlin.idea.caches.resolve.analyzeFullyAndGetResult -import org.jetbrains.kotlin.types.TypeProjectionImpl -import java.util.Collections +import org.jetbrains.kotlin.idea.core.quickfix.QuickFixUtil +import org.jetbrains.kotlin.idea.quickfix.createFromUsage.callableBuilder.CallableInfo +import org.jetbrains.kotlin.idea.quickfix.createFromUsage.callableBuilder.FunctionInfo +import org.jetbrains.kotlin.idea.quickfix.createFromUsage.callableBuilder.TypeInfo +import org.jetbrains.kotlin.idea.quickfix.createFromUsage.callableBuilder.guessTypes +import org.jetbrains.kotlin.psi.JetExpression +import org.jetbrains.kotlin.psi.JetFile +import org.jetbrains.kotlin.psi.JetForExpression import org.jetbrains.kotlin.types.JetTypeImpl -import org.jetbrains.kotlin.idea.quickfix.createFromUsage.callableBuilder.* -import org.jetbrains.kotlin.idea.quickfix.JetIntentionActionsFactory +import org.jetbrains.kotlin.types.TypeProjectionImpl +import org.jetbrains.kotlin.types.Variance +import java.util.Collections -object CreateIteratorFunctionActionFactory : JetIntentionActionsFactory() { - override fun doCreateActions(diagnostic: Diagnostic): List? { - val file = diagnostic.getPsiFile() as? JetFile ?: return null - val forExpr = QuickFixUtil.getParentElementOfType(diagnostic, javaClass()) ?: return null - val iterableExpr = forExpr.getLoopRange() ?: return null - val variableExpr: JetExpression = ((forExpr.getLoopParameter() ?: forExpr.getMultiParameter()) ?: return null) as JetExpression +object CreateIteratorFunctionActionFactory : CreateCallableMemberFromUsageFactory() { + override fun getElementOfInterest(diagnostic: Diagnostic): JetForExpression? { + return QuickFixUtil.getParentElementOfType(diagnostic, javaClass()) + } + + override fun createCallableInfo(element: JetForExpression, diagnostic: Diagnostic): CallableInfo? { + val file = diagnostic.psiFile as? JetFile ?: return null + val iterableExpr = element.loopRange ?: return null + val variableExpr: JetExpression = ((element.loopParameter ?: element.multiParameter) ?: return null) as JetExpression val iterableType = TypeInfo(iterableExpr, Variance.IN_VARIANCE) - val returnJetType = KotlinBuiltIns.getInstance().getIterator().getDefaultType() + val returnJetType = KotlinBuiltIns.getInstance().iterator.defaultType val analysisResult = file.analyzeFullyAndGetResult() val returnJetTypeParameterTypes = variableExpr.guessTypes(analysisResult.bindingContext, analysisResult.moduleDescriptor) @@ -47,8 +50,12 @@ object CreateIteratorFunctionActionFactory : JetIntentionActionsFactory() { val returnJetTypeParameterType = TypeProjectionImpl(returnJetTypeParameterTypes[0]) val returnJetTypeArguments = Collections.singletonList(returnJetTypeParameterType) - val newReturnJetType = JetTypeImpl(returnJetType.getAnnotations(), returnJetType.getConstructor(), returnJetType.isMarkedNullable(), returnJetTypeArguments, returnJetType.getMemberScope()) + val newReturnJetType = JetTypeImpl(returnJetType.annotations, + returnJetType.constructor, + returnJetType.isMarkedNullable, + returnJetTypeArguments, + returnJetType.memberScope) val returnType = TypeInfo(newReturnJetType, Variance.OUT_VARIANCE) - return CreateCallableFromUsageFixes(forExpr, FunctionInfo("iterator", iterableType, returnType)) + return FunctionInfo("iterator", iterableType, returnType) } } diff --git a/idea/src/org/jetbrains/kotlin/idea/quickfix/createFromUsage/createCallable/CreateNextFunctionActionFactory.kt b/idea/src/org/jetbrains/kotlin/idea/quickfix/createFromUsage/createCallable/CreateNextFunctionActionFactory.kt index 7f6cdeab58b..de6e3fb164c 100644 --- a/idea/src/org/jetbrains/kotlin/idea/quickfix/createFromUsage/createCallable/CreateNextFunctionActionFactory.kt +++ b/idea/src/org/jetbrains/kotlin/idea/quickfix/createFromUsage/createCallable/CreateNextFunctionActionFactory.kt @@ -17,24 +17,27 @@ package org.jetbrains.kotlin.idea.quickfix.createFromUsage.createCallable import org.jetbrains.kotlin.diagnostics.Diagnostic -import com.intellij.codeInsight.intention.IntentionAction -import org.jetbrains.kotlin.psi.JetExpression import org.jetbrains.kotlin.diagnostics.DiagnosticFactory import org.jetbrains.kotlin.diagnostics.Errors -import org.jetbrains.kotlin.types.Variance import org.jetbrains.kotlin.idea.core.quickfix.QuickFixUtil +import org.jetbrains.kotlin.idea.quickfix.createFromUsage.callableBuilder.CallableInfo +import org.jetbrains.kotlin.idea.quickfix.createFromUsage.callableBuilder.FunctionInfo +import org.jetbrains.kotlin.idea.quickfix.createFromUsage.callableBuilder.TypeInfo +import org.jetbrains.kotlin.psi.JetExpression import org.jetbrains.kotlin.psi.JetForExpression -import org.jetbrains.kotlin.idea.quickfix.createFromUsage.callableBuilder.* -import org.jetbrains.kotlin.idea.quickfix.JetIntentionActionsFactory +import org.jetbrains.kotlin.types.Variance -object CreateNextFunctionActionFactory : JetIntentionActionsFactory() { - override fun doCreateActions(diagnostic: Diagnostic): List? { +object CreateNextFunctionActionFactory : CreateCallableMemberFromUsageFactory() { + override fun getElementOfInterest(diagnostic: Diagnostic): JetForExpression? { + return QuickFixUtil.getParentElementOfType(diagnostic, javaClass()) + } + + override fun createCallableInfo(element: JetForExpression, diagnostic: Diagnostic): CallableInfo? { val diagnosticWithParameters = DiagnosticFactory.cast(diagnostic, Errors.NEXT_MISSING, Errors.NEXT_NONE_APPLICABLE) - val ownerType = TypeInfo(diagnosticWithParameters.getA(), Variance.IN_VARIANCE) + val ownerType = TypeInfo(diagnosticWithParameters.a, Variance.IN_VARIANCE) - val forExpr = QuickFixUtil.getParentElementOfType(diagnostic, javaClass()) ?: return null - val variableExpr: JetExpression = ((forExpr.getLoopParameter() ?: forExpr.getMultiParameter()) ?: return null) as JetExpression - val returnType = TypeInfo(variableExpr, Variance.OUT_VARIANCE) - return CreateCallableFromUsageFixes(forExpr, FunctionInfo("next", ownerType, returnType)) + val variableExpr = element.loopParameter ?: element.multiParameter ?: return null + val returnType = TypeInfo(variableExpr as JetExpression, Variance.OUT_VARIANCE) + return FunctionInfo("next", ownerType, returnType) } } diff --git a/idea/src/org/jetbrains/kotlin/idea/quickfix/createFromUsage/createCallable/CreatePropertyDelegateAccessorsActionFactory.kt b/idea/src/org/jetbrains/kotlin/idea/quickfix/createFromUsage/createCallable/CreatePropertyDelegateAccessorsActionFactory.kt index 4df232acc5c..3c698a0c9f2 100644 --- a/idea/src/org/jetbrains/kotlin/idea/quickfix/createFromUsage/createCallable/CreatePropertyDelegateAccessorsActionFactory.kt +++ b/idea/src/org/jetbrains/kotlin/idea/quickfix/createFromUsage/createCallable/CreatePropertyDelegateAccessorsActionFactory.kt @@ -16,48 +16,49 @@ package org.jetbrains.kotlin.idea.quickfix.createFromUsage.createCallable -import org.jetbrains.kotlin.diagnostics.Diagnostic -import com.intellij.codeInsight.intention.IntentionAction -import org.jetbrains.kotlin.idea.quickfix.createFromUsage.callableBuilder.TypeInfo -import org.jetbrains.kotlin.types.Variance -import org.jetbrains.kotlin.idea.quickfix.createFromUsage.callableBuilder.ParameterInfo -import org.jetbrains.kotlin.idea.quickfix.createFromUsage.callableBuilder.FunctionInfo -import org.jetbrains.kotlin.psi.JetProperty -import org.jetbrains.kotlin.resolve.BindingContext -import org.jetbrains.kotlin.descriptors.PropertyDescriptor -import org.jetbrains.kotlin.psi.psiUtil.getNonStrictParentOfType -import org.jetbrains.kotlin.psi.JetExpression -import org.jetbrains.kotlin.builtins.KotlinBuiltIns -import org.jetbrains.kotlin.idea.quickfix.createFromUsage.callableBuilder.CallableInfo import com.intellij.util.SmartList +import org.jetbrains.kotlin.builtins.KotlinBuiltIns import org.jetbrains.kotlin.descriptors.PropertyAccessorDescriptor +import org.jetbrains.kotlin.descriptors.PropertyDescriptor +import org.jetbrains.kotlin.diagnostics.Diagnostic import org.jetbrains.kotlin.idea.caches.resolve.analyze -import org.jetbrains.kotlin.idea.quickfix.JetIntentionActionsFactory +import org.jetbrains.kotlin.idea.quickfix.createFromUsage.callableBuilder.CallableInfo +import org.jetbrains.kotlin.idea.quickfix.createFromUsage.callableBuilder.FunctionInfo +import org.jetbrains.kotlin.idea.quickfix.createFromUsage.callableBuilder.ParameterInfo +import org.jetbrains.kotlin.idea.quickfix.createFromUsage.callableBuilder.TypeInfo +import org.jetbrains.kotlin.psi.JetExpression +import org.jetbrains.kotlin.psi.JetProperty +import org.jetbrains.kotlin.psi.psiUtil.getNonStrictParentOfType +import org.jetbrains.kotlin.resolve.BindingContext +import org.jetbrains.kotlin.types.Variance -object CreatePropertyDelegateAccessorsActionFactory : JetIntentionActionsFactory() { - override fun doCreateActions(diagnostic: Diagnostic): List? { - val expression = diagnostic.getPsiElement() as? JetExpression ?: return null - val context = expression.analyze() +object CreatePropertyDelegateAccessorsActionFactory : CreateCallableMemberFromUsageFactory() { + override fun getElementOfInterest(diagnostic: Diagnostic): JetExpression? { + return diagnostic.psiElement as? JetExpression + } + + override fun createQuickFixData(element: JetExpression, diagnostic: Diagnostic): List { + val context = element.analyze() fun isApplicableForAccessor(accessor: PropertyAccessorDescriptor?): Boolean = accessor != null && context[BindingContext.DELEGATED_PROPERTY_RESOLVED_CALL, accessor] == null val builtIns = KotlinBuiltIns.getInstance() - val property = expression.getNonStrictParentOfType() ?: return null + val property = element.getNonStrictParentOfType() ?: return emptyList() val propertyDescriptor = context[BindingContext.DECLARATION_TO_DESCRIPTOR, property] as? PropertyDescriptor - ?: return null + ?: return emptyList() - val propertyReceiver = propertyDescriptor.getExtensionReceiverParameter() ?: propertyDescriptor.getDispatchReceiverParameter() - val propertyType = propertyDescriptor.getType() + val propertyReceiver = propertyDescriptor.extensionReceiverParameter ?: propertyDescriptor.dispatchReceiverParameter + val propertyType = propertyDescriptor.type - val accessorReceiverType = TypeInfo(expression, Variance.IN_VARIANCE) - val thisRefParam = ParameterInfo(TypeInfo(propertyReceiver?.getType() ?: builtIns.getNullableNothingType(), Variance.IN_VARIANCE)) - val metadataParam = ParameterInfo(TypeInfo(builtIns.getPropertyMetadata().getDefaultType(), Variance.IN_VARIANCE)) + val accessorReceiverType = TypeInfo(element, Variance.IN_VARIANCE) + val thisRefParam = ParameterInfo(TypeInfo(propertyReceiver?.type ?: builtIns.nullableNothingType, Variance.IN_VARIANCE)) + val metadataParam = ParameterInfo(TypeInfo(builtIns.propertyMetadata.defaultType, Variance.IN_VARIANCE)) val callableInfos = SmartList() - if (isApplicableForAccessor(propertyDescriptor.getGetter())) { + if (isApplicableForAccessor(propertyDescriptor.getter)) { val getterInfo = FunctionInfo( name = "get", receiverTypeInfo = accessorReceiverType, @@ -67,17 +68,17 @@ object CreatePropertyDelegateAccessorsActionFactory : JetIntentionActionsFactory callableInfos.add(getterInfo) } - if (propertyDescriptor.isVar() && isApplicableForAccessor(propertyDescriptor.getSetter())) { + if (propertyDescriptor.isVar && isApplicableForAccessor(propertyDescriptor.setter)) { val newValueParam = ParameterInfo(TypeInfo(propertyType, Variance.IN_VARIANCE)) val setterInfo = FunctionInfo( name = "set", receiverTypeInfo = accessorReceiverType, - returnTypeInfo = TypeInfo(builtIns.getUnitType(), Variance.OUT_VARIANCE), + returnTypeInfo = TypeInfo(builtIns.unitType, Variance.OUT_VARIANCE), parameterInfos = listOf(thisRefParam, metadataParam, newValueParam) ) callableInfos.add(setterInfo) } - return CreateCallableFromUsageFixes(expression, callableInfos) + return callableInfos } } diff --git a/idea/src/org/jetbrains/kotlin/idea/quickfix/createFromUsage/createCallable/CreateSetFunctionActionFactory.kt b/idea/src/org/jetbrains/kotlin/idea/quickfix/createFromUsage/createCallable/CreateSetFunctionActionFactory.kt index a0ff7ea3c8f..6336ecd598b 100644 --- a/idea/src/org/jetbrains/kotlin/idea/quickfix/createFromUsage/createCallable/CreateSetFunctionActionFactory.kt +++ b/idea/src/org/jetbrains/kotlin/idea/quickfix/createFromUsage/createCallable/CreateSetFunctionActionFactory.kt @@ -16,53 +16,56 @@ package org.jetbrains.kotlin.idea.quickfix.createFromUsage.createCallable -import org.jetbrains.kotlin.diagnostics.Diagnostic -import com.intellij.codeInsight.intention.IntentionAction -import org.jetbrains.kotlin.idea.core.quickfix.QuickFixUtil -import org.jetbrains.kotlin.psi.JetArrayAccessExpression -import org.jetbrains.kotlin.types.Variance -import java.util.ArrayList -import org.jetbrains.kotlin.psi.JetBinaryExpression import org.jetbrains.kotlin.builtins.KotlinBuiltIns -import org.jetbrains.kotlin.idea.quickfix.createFromUsage.callableBuilder.* -import java.util.Collections +import org.jetbrains.kotlin.diagnostics.Diagnostic +import org.jetbrains.kotlin.idea.caches.resolve.analyze +import org.jetbrains.kotlin.idea.core.quickfix.QuickFixUtil +import org.jetbrains.kotlin.idea.quickfix.createFromUsage.callableBuilder.CallableInfo +import org.jetbrains.kotlin.idea.quickfix.createFromUsage.callableBuilder.FunctionInfo +import org.jetbrains.kotlin.idea.quickfix.createFromUsage.callableBuilder.ParameterInfo +import org.jetbrains.kotlin.idea.quickfix.createFromUsage.callableBuilder.TypeInfo +import org.jetbrains.kotlin.psi.JetArrayAccessExpression +import org.jetbrains.kotlin.psi.JetBinaryExpression import org.jetbrains.kotlin.psi.JetOperationExpression import org.jetbrains.kotlin.psi.JetUnaryExpression -import org.jetbrains.kotlin.types.expressions.OperatorConventions import org.jetbrains.kotlin.resolve.calls.callUtil.getResolvedCall import org.jetbrains.kotlin.types.ErrorUtils -import org.jetbrains.kotlin.idea.caches.resolve.analyze -import org.jetbrains.kotlin.idea.quickfix.JetIntentionActionsFactory +import org.jetbrains.kotlin.types.Variance +import org.jetbrains.kotlin.types.expressions.OperatorConventions +import java.util.ArrayList +import java.util.Collections -object CreateSetFunctionActionFactory : JetIntentionActionsFactory() { - override fun doCreateActions(diagnostic: Diagnostic): List? { - val accessExpr = QuickFixUtil.getParentElementOfType(diagnostic, javaClass()) ?: return null - val arrayExpr = accessExpr.getArrayExpression() ?: return null +object CreateSetFunctionActionFactory : CreateCallableMemberFromUsageFactory() { + override fun getElementOfInterest(diagnostic: Diagnostic): JetArrayAccessExpression? { + return QuickFixUtil.getParentElementOfType(diagnostic, javaClass()) + } + + override fun createCallableInfo(element: JetArrayAccessExpression, diagnostic: Diagnostic): CallableInfo? { + val arrayExpr = element.arrayExpression ?: return null val arrayType = TypeInfo(arrayExpr, Variance.IN_VARIANCE) val builtIns = KotlinBuiltIns.getInstance() - val parameters = accessExpr.getIndexExpressions().mapTo(ArrayList()) { + val parameters = element.indexExpressions.mapTo(ArrayList()) { ParameterInfo(TypeInfo(it, Variance.IN_VARIANCE)) } - val assignmentExpr = QuickFixUtil.getParentElementOfType(diagnostic, javaClass()) ?: return null val valType = when (assignmentExpr) { is JetBinaryExpression -> { - TypeInfo(assignmentExpr.getRight() ?: return null, Variance.IN_VARIANCE) + TypeInfo(assignmentExpr.right ?: return null, Variance.IN_VARIANCE) } is JetUnaryExpression -> { - if (assignmentExpr.getOperationToken() !in OperatorConventions.INCREMENT_OPERATIONS) return null + if (assignmentExpr.operationToken !in OperatorConventions.INCREMENT_OPERATIONS) return null val context = assignmentExpr.analyze() - val rhsType = assignmentExpr.getResolvedCall(context)?.getResultingDescriptor()?.getReturnType() - TypeInfo(if (rhsType == null || ErrorUtils.containsErrorType(rhsType)) builtIns.getAnyType() else rhsType, Variance.IN_VARIANCE) + val rhsType = assignmentExpr.getResolvedCall(context)?.resultingDescriptor?.returnType + TypeInfo(if (rhsType == null || ErrorUtils.containsErrorType(rhsType)) builtIns.anyType else rhsType, Variance.IN_VARIANCE) } else -> return null } parameters.add(ParameterInfo(valType, "value")) - val returnType = TypeInfo(builtIns.getUnitType(), Variance.OUT_VARIANCE) - return CreateCallableFromUsageFixes(accessExpr, FunctionInfo("set", arrayType, returnType, Collections.emptyList(), parameters)) + val returnType = TypeInfo(builtIns.unitType, Variance.OUT_VARIANCE) + return FunctionInfo("set", arrayType, returnType, Collections.emptyList(), parameters) } } diff --git a/idea/src/org/jetbrains/kotlin/idea/quickfix/createFromUsage/createCallable/CreateUnaryOperationActionFactory.kt b/idea/src/org/jetbrains/kotlin/idea/quickfix/createFromUsage/createCallable/CreateUnaryOperationActionFactory.kt index 4b323a908e6..1bc1b84fbb7 100644 --- a/idea/src/org/jetbrains/kotlin/idea/quickfix/createFromUsage/createCallable/CreateUnaryOperationActionFactory.kt +++ b/idea/src/org/jetbrains/kotlin/idea/quickfix/createFromUsage/createCallable/CreateUnaryOperationActionFactory.kt @@ -17,25 +17,28 @@ package org.jetbrains.kotlin.idea.quickfix.createFromUsage.createCallable import org.jetbrains.kotlin.diagnostics.Diagnostic -import com.intellij.codeInsight.intention.IntentionAction -import org.jetbrains.kotlin.types.expressions.OperatorConventions +import org.jetbrains.kotlin.idea.quickfix.createFromUsage.callableBuilder.CallableInfo +import org.jetbrains.kotlin.idea.quickfix.createFromUsage.callableBuilder.FunctionInfo +import org.jetbrains.kotlin.idea.quickfix.createFromUsage.callableBuilder.TypeInfo import org.jetbrains.kotlin.lexer.JetToken -import org.jetbrains.kotlin.types.Variance import org.jetbrains.kotlin.psi.JetUnaryExpression -import org.jetbrains.kotlin.idea.quickfix.createFromUsage.callableBuilder.* -import org.jetbrains.kotlin.idea.quickfix.JetIntentionActionsFactory +import org.jetbrains.kotlin.types.Variance +import org.jetbrains.kotlin.types.expressions.OperatorConventions -public object CreateUnaryOperationActionFactory: JetIntentionActionsFactory() { - override fun doCreateActions(diagnostic: Diagnostic): List? { - val callExpr = diagnostic.getPsiElement().getParent() as? JetUnaryExpression ?: return null - val token = callExpr.getOperationToken() as JetToken +public object CreateUnaryOperationActionFactory: CreateCallableMemberFromUsageFactory() { + override fun getElementOfInterest(diagnostic: Diagnostic): JetUnaryExpression? { + return diagnostic.psiElement.parent as? JetUnaryExpression + } + + override fun createCallableInfo(element: JetUnaryExpression, diagnostic: Diagnostic): CallableInfo? { + val token = element.operationToken as JetToken val operationName = OperatorConventions.getNameForOperationSymbol(token) ?: return null val incDec = token in OperatorConventions.INCREMENT_OPERATIONS - val receiverExpr = callExpr.getBaseExpression() ?: return null + val receiverExpr = element.baseExpression ?: return null val receiverType = TypeInfo(receiverExpr, Variance.IN_VARIANCE) - val returnType = if (incDec) TypeInfo.ByReceiverType(Variance.OUT_VARIANCE) else TypeInfo(callExpr, Variance.OUT_VARIANCE) - return CreateCallableFromUsageFixes(callExpr, FunctionInfo(operationName.asString(), receiverType, returnType)) + val returnType = if (incDec) TypeInfo.ByReceiverType(Variance.OUT_VARIANCE) else TypeInfo(element, Variance.OUT_VARIANCE) + return FunctionInfo(operationName.asString(), receiverType, returnType) } } diff --git a/idea/src/org/jetbrains/kotlin/idea/quickfix/createFromUsage/createClass/CreateClassFromCallWithConstructorCalleeActionFactory.kt b/idea/src/org/jetbrains/kotlin/idea/quickfix/createFromUsage/createClass/CreateClassFromCallWithConstructorCalleeActionFactory.kt index edd73429ce3..b54d8735b80 100644 --- a/idea/src/org/jetbrains/kotlin/idea/quickfix/createFromUsage/createClass/CreateClassFromCallWithConstructorCalleeActionFactory.kt +++ b/idea/src/org/jetbrains/kotlin/idea/quickfix/createFromUsage/createClass/CreateClassFromCallWithConstructorCalleeActionFactory.kt @@ -16,28 +16,22 @@ package org.jetbrains.kotlin.idea.quickfix.createFromUsage.createClass +import com.intellij.psi.util.PsiTreeUtil +import org.jetbrains.kotlin.builtins.KotlinBuiltIns import org.jetbrains.kotlin.diagnostics.Diagnostic -import com.intellij.codeInsight.intention.IntentionAction +import org.jetbrains.kotlin.idea.caches.resolve.analyze +import org.jetbrains.kotlin.idea.quickfix.createFromUsage.callableBuilder.ParameterInfo import org.jetbrains.kotlin.idea.quickfix.createFromUsage.callableBuilder.TypeInfo +import org.jetbrains.kotlin.psi.* +import org.jetbrains.kotlin.psi.psiUtil.isAncestor import org.jetbrains.kotlin.resolve.BindingContext import org.jetbrains.kotlin.types.Variance -import org.jetbrains.kotlin.builtins.KotlinBuiltIns -import org.jetbrains.kotlin.idea.quickfix.createFromUsage.callableBuilder.ParameterInfo -import org.jetbrains.kotlin.idea.quickfix.JetSingleIntentionActionFactory -import org.jetbrains.kotlin.psi.JetFile -import org.jetbrains.kotlin.psi.JetAnnotationEntry -import org.jetbrains.kotlin.psi.JetUserType -import com.intellij.psi.util.PsiTreeUtil -import org.jetbrains.kotlin.psi.JetDelegatorToSuperCall -import org.jetbrains.kotlin.psi.JetCallElement -import org.jetbrains.kotlin.psi.psiUtil.isAncestor -import org.jetbrains.kotlin.psi.JetConstructorCalleeExpression +import org.jetbrains.kotlin.utils.addToStdlib.singletonList import java.util.Collections -import org.jetbrains.kotlin.idea.caches.resolve.analyze -public object CreateClassFromCallWithConstructorCalleeActionFactory : JetSingleIntentionActionFactory() { - override fun createAction(diagnostic: Diagnostic): IntentionAction? { - val diagElement = diagnostic.getPsiElement() +public object CreateClassFromCallWithConstructorCalleeActionFactory : CreateClassFromUsageFactory() { + override fun getElementOfInterest(diagnostic: Diagnostic): JetCallElement? { + val diagElement = diagnostic.psiElement val callElement = PsiTreeUtil.getParentOfType( diagElement, @@ -45,26 +39,34 @@ public object CreateClassFromCallWithConstructorCalleeActionFactory : JetSingleI javaClass() ) as? JetCallElement ?: return null - val isAnnotation = callElement is JetAnnotationEntry - - val callee = callElement.getCalleeExpression() as? JetConstructorCalleeExpression ?: return null - val calleeRef = callee.getConstructorReferenceExpression() ?: return null + val callee = callElement.calleeExpression as? JetConstructorCalleeExpression ?: return null + val calleeRef = callee.constructorReferenceExpression ?: return null if (!calleeRef.isAncestor(diagElement)) return null + return callElement + } - val file = callElement.getContainingFile() as? JetFile ?: return null - val typeRef = callee.getTypeReference() ?: return null - val userType = typeRef.getTypeElement() as? JetUserType ?: return null + override fun getPossibleClassKinds(element: JetCallElement, diagnostic: Diagnostic): List { + return (if (element is JetAnnotationEntry) ClassKind.ANNOTATION_CLASS else ClassKind.PLAIN_CLASS).singletonList() + } + + override fun createQuickFixData(element: JetCallElement, diagnostic: Diagnostic): ClassInfo? { + val isAnnotation = element is JetAnnotationEntry + val callee = element.calleeExpression as? JetConstructorCalleeExpression ?: return null + val calleeRef = callee.constructorReferenceExpression ?: return null + val file = element.containingFile as? JetFile ?: return null + val typeRef = callee.typeReference ?: return null + val userType = typeRef.typeElement as? JetUserType ?: return null val context = userType.analyze() - val qualifier = userType.getQualifier()?.getReferenceExpression() + val qualifier = userType.qualifier?.referenceExpression val qualifierDescriptor = qualifier?.let { context[BindingContext.REFERENCE_TARGET, it] } val targetParent = getTargetParentByQualifier(file, qualifier != null, qualifierDescriptor) ?: return null - val anyType = KotlinBuiltIns.getInstance().getNullableAnyType() - val valueArguments = callElement.getValueArguments() + val anyType = KotlinBuiltIns.getInstance().nullableAnyType + val valueArguments = element.valueArguments val defaultParamName = if (valueArguments.size() == 1) "value" else null val parameterInfos = valueArguments.map { ParameterInfo( @@ -75,13 +77,12 @@ public object CreateClassFromCallWithConstructorCalleeActionFactory : JetSingleI val typeArgumentInfos = when { isAnnotation -> Collections.emptyList() - else -> callElement.getTypeArguments() - .map { it.getTypeReference()?.let { TypeInfo(it, Variance.INVARIANT) } } + else -> element.typeArguments + .map { it.typeReference?.let { TypeInfo(it, Variance.INVARIANT) } } .filterNotNull() } - val classInfo = ClassInfo( - kind = if (isAnnotation) ClassKind.ANNOTATION_CLASS else ClassKind.PLAIN_CLASS, + return ClassInfo( name = calleeRef.getReferencedName(), targetParent = targetParent, expectedTypeInfo = TypeInfo.Empty, @@ -89,6 +90,5 @@ public object CreateClassFromCallWithConstructorCalleeActionFactory : JetSingleI open = !isAnnotation, typeArguments = typeArgumentInfos ) - return CreateClassFromUsageFix(callElement, classInfo) } } diff --git a/idea/src/org/jetbrains/kotlin/idea/quickfix/createFromUsage/createClass/CreateClassFromConstructorCallActionFactory.kt b/idea/src/org/jetbrains/kotlin/idea/quickfix/createFromUsage/createClass/CreateClassFromConstructorCallActionFactory.kt index e9315c5ba20..1a61079c1a5 100644 --- a/idea/src/org/jetbrains/kotlin/idea/quickfix/createFromUsage/createClass/CreateClassFromConstructorCallActionFactory.kt +++ b/idea/src/org/jetbrains/kotlin/idea/quickfix/createFromUsage/createClass/CreateClassFromConstructorCallActionFactory.kt @@ -16,43 +16,63 @@ package org.jetbrains.kotlin.idea.quickfix.createFromUsage.createClass +import org.jetbrains.kotlin.builtins.KotlinBuiltIns import org.jetbrains.kotlin.diagnostics.Diagnostic -import com.intellij.codeInsight.intention.IntentionAction +import org.jetbrains.kotlin.idea.caches.resolve.analyzeFullyAndGetResult +import org.jetbrains.kotlin.idea.quickfix.createFromUsage.callableBuilder.ParameterInfo +import org.jetbrains.kotlin.idea.quickfix.createFromUsage.callableBuilder.TypeInfo +import org.jetbrains.kotlin.idea.quickfix.createFromUsage.callableBuilder.getTypeInfoForTypeArguments +import org.jetbrains.kotlin.psi.* import org.jetbrains.kotlin.psi.psiUtil.getNonStrictParentOfType -import org.jetbrains.kotlin.psi.JetTypeReference -import org.jetbrains.kotlin.psi.JetCallExpression -import org.jetbrains.kotlin.psi.JetSimpleNameExpression -import org.jetbrains.kotlin.psi.JetQualifiedExpression +import org.jetbrains.kotlin.psi.psiUtil.getQualifiedExpressionForSelectorOrThis import org.jetbrains.kotlin.resolve.calls.callUtil.getCall import org.jetbrains.kotlin.types.Variance -import org.jetbrains.kotlin.builtins.KotlinBuiltIns -import org.jetbrains.kotlin.idea.quickfix.JetSingleIntentionActionFactory -import org.jetbrains.kotlin.idea.caches.resolve.analyzeFullyAndGetResult -import org.jetbrains.kotlin.psi.JetFile -import org.jetbrains.kotlin.psi.JetAnnotationEntry +import org.jetbrains.kotlin.utils.addToStdlib.singletonList import java.util.Collections -import org.jetbrains.kotlin.idea.quickfix.createFromUsage.callableBuilder.* -public object CreateClassFromConstructorCallActionFactory: JetSingleIntentionActionFactory() { - override fun createAction(diagnostic: Diagnostic): IntentionAction? { - val diagElement = diagnostic.getPsiElement() +public object CreateClassFromConstructorCallActionFactory: CreateClassFromUsageFactory() { + override fun getElementOfInterest(diagnostic: Diagnostic): JetCallExpression? { + val diagElement = diagnostic.psiElement + if (diagElement.getNonStrictParentOfType() != null) return null + + val callExpr = diagElement.parent as? JetCallExpression ?: return null + return if (callExpr.calleeExpression == diagElement) callExpr else null + } + + override fun getPossibleClassKinds(element: JetCallExpression, diagnostic: Diagnostic): List { + val inAnnotationEntry = diagnostic.psiElement.getNonStrictParentOfType() != null + + val (context, moduleDescriptor) = element.analyzeFullyAndGetResult() + val file = element.containingFile as? JetFile ?: return emptyList() + val call = element.getCall(context) ?: return emptyList() + val targetParent = getTargetParentByCall(call, file) ?: return emptyList() + + val classKind = if (inAnnotationEntry) ClassKind.ANNOTATION_CLASS else ClassKind.PLAIN_CLASS + val fullCallExpr = element.getQualifiedExpressionForSelectorOrThis() + if (!fullCallExpr.getInheritableTypeInfo(context, moduleDescriptor, targetParent).second(classKind)) return emptyList() + + return classKind.singletonList() + } + + override fun createQuickFixData(element: JetCallExpression, diagnostic: Diagnostic): ClassInfo? { + val diagElement = diagnostic.psiElement if (diagElement.getNonStrictParentOfType() != null) return null val inAnnotationEntry = diagElement.getNonStrictParentOfType() != null - val callExpr = diagElement.getParent() as? JetCallExpression ?: return null - if (callExpr.getCalleeExpression() != diagElement) return null + val callExpr = diagElement.parent as? JetCallExpression ?: return null + if (callExpr.calleeExpression != diagElement) return null - val calleeExpr = callExpr.getCalleeExpression() as? JetSimpleNameExpression ?: return null + val calleeExpr = callExpr.calleeExpression as? JetSimpleNameExpression ?: return null val name = calleeExpr.getReferencedName() if (!inAnnotationEntry && !name.checkClassName()) return null - val callParent = callExpr.getParent() + val callParent = callExpr.parent val fullCallExpr = - if (callParent is JetQualifiedExpression && callParent.getSelectorExpression() == callExpr) callParent else callExpr + if (callParent is JetQualifiedExpression && callParent.selectorExpression == callExpr) callParent else callExpr - val file = fullCallExpr.getContainingFile() as? JetFile ?: return null + val file = fullCallExpr.containingFile as? JetFile ?: return null val (context, moduleDescriptor) = callExpr.analyzeFullyAndGetResult() @@ -60,9 +80,9 @@ public object CreateClassFromConstructorCallActionFactory: JetSingleIntentionAct val targetParent = getTargetParentByCall(call, file) ?: return null val inner = isInnerClassExpected(call) - val valueArguments = callExpr.getValueArguments() + val valueArguments = callExpr.valueArguments val defaultParamName = if (inAnnotationEntry && valueArguments.size() == 1) "value" else null - val anyType = KotlinBuiltIns.getInstance().getNullableAnyType() + val anyType = KotlinBuiltIns.getInstance().nullableAnyType val parameterInfos = valueArguments.map { ParameterInfo( it.getArgumentExpression()?.let { TypeInfo(it, Variance.IN_VARIANCE) } ?: TypeInfo(anyType, Variance.IN_VARIANCE), @@ -77,8 +97,7 @@ public object CreateClassFromConstructorCallActionFactory: JetSingleIntentionAct val typeArgumentInfos = if (inAnnotationEntry) Collections.emptyList() else callExpr.getTypeInfoForTypeArguments() - val classInfo = ClassInfo( - kind = classKind, + return ClassInfo( name = name, targetParent = targetParent, expectedTypeInfo = expectedTypeInfo, @@ -86,6 +105,5 @@ public object CreateClassFromConstructorCallActionFactory: JetSingleIntentionAct typeArguments = typeArgumentInfos, parameterInfos = parameterInfos ) - return CreateClassFromUsageFix(callExpr, classInfo) } } diff --git a/idea/src/org/jetbrains/kotlin/idea/quickfix/createFromUsage/createClass/CreateClassFromReferenceExpressionActionFactory.kt b/idea/src/org/jetbrains/kotlin/idea/quickfix/createFromUsage/createClass/CreateClassFromReferenceExpressionActionFactory.kt index 693868426cd..be02f933f9a 100644 --- a/idea/src/org/jetbrains/kotlin/idea/quickfix/createFromUsage/createClass/CreateClassFromReferenceExpressionActionFactory.kt +++ b/idea/src/org/jetbrains/kotlin/idea/quickfix/createFromUsage/createClass/CreateClassFromReferenceExpressionActionFactory.kt @@ -16,74 +16,74 @@ package org.jetbrains.kotlin.idea.quickfix.createFromUsage.createClass -import org.jetbrains.kotlin.diagnostics.Diagnostic -import com.intellij.codeInsight.intention.IntentionAction -import org.jetbrains.kotlin.psi.JetSimpleNameExpression -import org.jetbrains.kotlin.psi.JetExpression -import org.jetbrains.kotlin.psi.psiUtil.getNonStrictParentOfType -import org.jetbrains.kotlin.psi.JetTypeReference -import java.util.Collections -import org.jetbrains.kotlin.idea.quickfix.JetIntentionActionsFactory -import org.jetbrains.kotlin.psi.JetClass -import org.jetbrains.kotlin.psi.JetQualifiedExpression -import org.jetbrains.kotlin.psi.psiUtil.getAssignmentByLHS -import org.jetbrains.kotlin.psi.JetCallExpression -import org.jetbrains.kotlin.psi.JetImportDirective -import org.jetbrains.kotlin.idea.caches.resolve.analyzeFullyAndGetResult -import org.jetbrains.kotlin.resolve.calls.callUtil.getCall -import org.jetbrains.kotlin.psi.JetFile -import org.jetbrains.kotlin.resolve.BindingContext -import org.jetbrains.kotlin.psi.psiUtil.getQualifiedElementSelector -import org.jetbrains.kotlin.idea.quickfix.createFromUsage.callableBuilder.TypeInfo -import org.jetbrains.kotlin.psi.JetReferenceExpression -import java.util.Arrays -import org.jetbrains.kotlin.psi.JetDotQualifiedExpression -import org.jetbrains.kotlin.psi.psiUtil.isDotReceiver import com.intellij.psi.PsiClass import com.intellij.psi.PsiElement +import org.jetbrains.kotlin.diagnostics.Diagnostic +import org.jetbrains.kotlin.idea.caches.resolve.analyzeFullyAndGetResult +import org.jetbrains.kotlin.idea.quickfix.createFromUsage.callableBuilder.TypeInfo +import org.jetbrains.kotlin.psi.* +import org.jetbrains.kotlin.psi.psiUtil.getAssignmentByLHS +import org.jetbrains.kotlin.psi.psiUtil.getNonStrictParentOfType +import org.jetbrains.kotlin.psi.psiUtil.getQualifiedElementSelector +import org.jetbrains.kotlin.psi.psiUtil.isDotReceiver +import org.jetbrains.kotlin.resolve.BindingContext +import org.jetbrains.kotlin.resolve.calls.callUtil.getCall +import java.util.Arrays +import java.util.Collections -public object CreateClassFromReferenceExpressionActionFactory : JetIntentionActionsFactory() { - override fun doCreateActions(diagnostic: Diagnostic): List { +public object CreateClassFromReferenceExpressionActionFactory : CreateClassFromUsageFactory(true) { + override fun getElementOfInterest(diagnostic: Diagnostic): JetSimpleNameExpression? { + val refExpr = diagnostic.psiElement as? JetSimpleNameExpression ?: return null + if (refExpr.getNonStrictParentOfType() != null) return null + return refExpr + } + + private fun getFullCallExpression(element: JetSimpleNameExpression): JetExpression? { + return element.parent?.let { + when { + it is JetCallExpression && it.calleeExpression == element -> return null + it is JetQualifiedExpression && it.selectorExpression == element -> it + else -> element + } + } as? JetExpression + } + + private fun isQualifierExpected(element: JetSimpleNameExpression) = element.isDotReceiver() || ((element.parent as? JetDotQualifiedExpression)?.isDotReceiver() ?: false) + + private fun isInsideOfImport(element: JetSimpleNameExpression) = element.getNonStrictParentOfType() != null + + override fun getPossibleClassKinds(element: JetSimpleNameExpression, diagnostic: Diagnostic): List { fun isEnum(element: PsiElement): Boolean { return when (element) { is JetClass -> element.isEnum() - is PsiClass -> element.isEnum() + is PsiClass -> element.isEnum else -> false } } - val refExpr = diagnostic.getPsiElement() as? JetSimpleNameExpression ?: return Collections.emptyList() - if (refExpr.getNonStrictParentOfType() != null) return Collections.emptyList() + val file = element.containingFile as? JetFile ?: return Collections.emptyList() - val file = refExpr.getContainingFile() as? JetFile ?: return Collections.emptyList() + val name = element.getReferencedName() - val name = refExpr.getReferencedName() + val (context, moduleDescriptor) = element.analyzeFullyAndGetResult() - val (context, moduleDescriptor) = refExpr.analyzeFullyAndGetResult() + val fullCallExpr = getFullCallExpression(element) ?: return Collections.emptyList() - val fullCallExpr = refExpr.getParent()?.let { - when { - it is JetCallExpression && it.getCalleeExpression() == refExpr -> return Collections.emptyList() - it is JetQualifiedExpression && it.getSelectorExpression() == refExpr -> it - else -> refExpr - } - } as? JetExpression ?: return Collections.emptyList() - - val inImport = refExpr.getNonStrictParentOfType() != null - val qualifierExpected = refExpr.isDotReceiver() || ((refExpr.getParent() as? JetDotQualifiedExpression)?.isDotReceiver() ?: false) - - if (inImport || qualifierExpected) { - val receiverSelector = (fullCallExpr as? JetQualifiedExpression)?.getReceiverExpression()?.getQualifiedElementSelector() as? JetReferenceExpression + val inImport = element.getNonStrictParentOfType() != null + if (inImport || isQualifierExpected(element)) { + val receiverSelector = (fullCallExpr as? JetQualifiedExpression)?.receiverExpression?.getQualifiedElementSelector() as? JetReferenceExpression val qualifierDescriptor = receiverSelector?.let { context[BindingContext.REFERENCE_TARGET, it] } val targetParent = - getTargetParentByQualifier(refExpr.getContainingJetFile(), receiverSelector != null, qualifierDescriptor) + getTargetParentByQualifier(element.getContainingJetFile(), receiverSelector != null, qualifierDescriptor) ?: return Collections.emptyList() - val createPackageAction = refExpr.getCreatePackageFixIfApplicable(targetParent) - if (createPackageAction != null) return Collections.singletonList(createPackageAction) + element.getCreatePackageFixIfApplicable(targetParent)?.let { return emptyList() } - return (if (name.checkClassName()) ClassKind.values() else arrayOf()) + if (!name.checkClassName()) return emptyList() + + return ClassKind + .values() .filter { when (it) { ClassKind.ANNOTATION_CLASS -> inImport @@ -91,24 +91,15 @@ public object CreateClassFromReferenceExpressionActionFactory : JetIntentionActi else -> true } } - .map { - val classInfo = ClassInfo( - kind = it, - name = name, - targetParent = targetParent, - expectedTypeInfo = TypeInfo.Empty - ) - CreateClassFromUsageFix(refExpr, classInfo) - } } if (fullCallExpr.getAssignmentByLHS() != null) return Collections.emptyList() - val call = refExpr.getCall(context) ?: return Collections.emptyList() + val call = element.getCall(context) ?: return Collections.emptyList() val targetParent = getTargetParentByCall(call, file) ?: return Collections.emptyList() if (isInnerClassExpected(call)) return Collections.emptyList() - val (expectedTypeInfo, filter) = fullCallExpr.getInheritableTypeInfo(context, moduleDescriptor, targetParent) + val filter = fullCallExpr.getInheritableTypeInfo(context, moduleDescriptor, targetParent).second return Arrays.asList(ClassKind.OBJECT, ClassKind.ENUM_ENTRY) .filter { @@ -118,14 +109,41 @@ public object CreateClassFromReferenceExpressionActionFactory : JetIntentionActi else -> false } } - .map { - val classInfo = ClassInfo( - kind = it, - name = name, - targetParent = targetParent, - expectedTypeInfo = expectedTypeInfo - ) - CreateClassFromUsageFix(refExpr, classInfo) - } + } + + override fun createQuickFixData(element: JetSimpleNameExpression, diagnostic: Diagnostic): ClassInfo? { + val file = element.containingFile as? JetFile ?: return null + + val name = element.getReferencedName() + + val (context, moduleDescriptor) = element.analyzeFullyAndGetResult() + + val fullCallExpr = getFullCallExpression(element) ?: return null + + if (isInsideOfImport(element) || isQualifierExpected(element)) { + val receiverSelector = (fullCallExpr as? JetQualifiedExpression)?.receiverExpression?.getQualifiedElementSelector() as? JetReferenceExpression + val qualifierDescriptor = receiverSelector?.let { context[BindingContext.REFERENCE_TARGET, it] } + + val targetParent = + getTargetParentByQualifier(element.getContainingJetFile(), receiverSelector != null, qualifierDescriptor) + ?: return null + + return ClassInfo( + name = name, + targetParent = targetParent, + expectedTypeInfo = TypeInfo.Empty + ) + } + + val call = element.getCall(context) ?: return null + val targetParent = getTargetParentByCall(call, file) ?: return null + + val expectedTypeInfo = fullCallExpr.getInheritableTypeInfo(context, moduleDescriptor, targetParent).first + + return ClassInfo( + name = name, + targetParent = targetParent, + expectedTypeInfo = expectedTypeInfo + ) } } diff --git a/idea/src/org/jetbrains/kotlin/idea/quickfix/createFromUsage/createClass/CreateClassFromTypeReferenceActionFactory.kt b/idea/src/org/jetbrains/kotlin/idea/quickfix/createFromUsage/createClass/CreateClassFromTypeReferenceActionFactory.kt index b7a5e63e05b..484bad63543 100644 --- a/idea/src/org/jetbrains/kotlin/idea/quickfix/createFromUsage/createClass/CreateClassFromTypeReferenceActionFactory.kt +++ b/idea/src/org/jetbrains/kotlin/idea/quickfix/createFromUsage/createClass/CreateClassFromTypeReferenceActionFactory.kt @@ -16,49 +16,36 @@ package org.jetbrains.kotlin.idea.quickfix.createFromUsage.createClass -import org.jetbrains.kotlin.idea.quickfix.JetIntentionActionsFactory +import org.jetbrains.kotlin.builtins.KotlinBuiltIns import org.jetbrains.kotlin.diagnostics.Diagnostic -import com.intellij.codeInsight.intention.IntentionAction -import org.jetbrains.kotlin.idea.core.quickfix.QuickFixUtil -import java.util.Collections -import org.jetbrains.kotlin.psi.JetUserType -import org.jetbrains.kotlin.psi.JetFile -import org.jetbrains.kotlin.resolve.BindingContext -import org.jetbrains.kotlin.idea.quickfix.createFromUsage.callableBuilder.TypeInfo -import org.jetbrains.kotlin.types.Variance -import org.jetbrains.kotlin.psi.JetDelegatorToSuperClass -import org.jetbrains.kotlin.psi.JetConstructorCalleeExpression -import org.jetbrains.kotlin.utils.addToStdlib.singletonOrEmptyList import org.jetbrains.kotlin.idea.caches.resolve.analyze -import org.jetbrains.kotlin.builtins.* +import org.jetbrains.kotlin.idea.core.quickfix.QuickFixUtil +import org.jetbrains.kotlin.idea.quickfix.createFromUsage.callableBuilder.TypeInfo +import org.jetbrains.kotlin.psi.JetConstructorCalleeExpression +import org.jetbrains.kotlin.psi.JetDelegatorToSuperClass +import org.jetbrains.kotlin.psi.JetFile +import org.jetbrains.kotlin.psi.JetUserType +import org.jetbrains.kotlin.resolve.BindingContext +import org.jetbrains.kotlin.types.Variance +import java.util.Collections -public object CreateClassFromTypeReferenceActionFactory: JetIntentionActionsFactory() { - override fun doCreateActions(diagnostic: Diagnostic): List { - val userType = QuickFixUtil.getParentElementOfType(diagnostic, javaClass()) ?: return Collections.emptyList() - val typeArguments = userType.getTypeArgumentsAsTypes() +public object CreateClassFromTypeReferenceActionFactory : CreateClassFromUsageFactory(true) { + override fun getElementOfInterest(diagnostic: Diagnostic): JetUserType? { + return QuickFixUtil.getParentElementOfType(diagnostic, javaClass()) + } - val refExpr = userType.getReferenceExpression() ?: return Collections.emptyList() - val name = refExpr.getReferencedName() - - val typeRefParent = userType.getParent()?.getParent() + override fun getPossibleClassKinds(element: JetUserType, diagnostic: Diagnostic): List { + val typeRefParent = element.parent?.parent if (typeRefParent is JetConstructorCalleeExpression) return Collections.emptyList() val traitExpected = typeRefParent is JetDelegatorToSuperClass - val context = userType.analyze() + val isQualifier = (element.parent as? JetUserType)?.let { it.qualifier == element } ?: false - val file = userType.getContainingFile() as? JetFile ?: return Collections.emptyList() - - val isQualifier = (userType.getParent() as? JetUserType)?.let { it.getQualifier() == userType } ?: false - val qualifier = userType.getQualifier()?.getReferenceExpression() - val qualifierDescriptor = qualifier?.let { context[BindingContext.REFERENCE_TARGET, it] } - - val targetParent = getTargetParentByQualifier(file, qualifier != null, qualifierDescriptor) ?: return Collections.emptyList() - - val possibleKinds = when { + return when { traitExpected -> Collections.singletonList(ClassKind.TRAIT) else -> ClassKind.values().filter { - val noTypeArguments = typeArguments.isEmpty() + val noTypeArguments = element.typeArgumentsAsTypes.isEmpty() when (it) { ClassKind.OBJECT -> noTypeArguments && isQualifier ClassKind.ANNOTATION_CLASS -> noTypeArguments && !isQualifier @@ -68,22 +55,29 @@ public object CreateClassFromTypeReferenceActionFactory: JetIntentionActionsFact } } } + } - val anyType = KotlinBuiltIns.getInstance().getAnyType() + override fun createQuickFixData(element: JetUserType, diagnostic: Diagnostic): ClassInfo? { + val name = element.referenceExpression?.getReferencedName() ?: return null + if (element.parent?.parent is JetConstructorCalleeExpression) return null - val createPackageAction = refExpr.getCreatePackageFixIfApplicable(targetParent) - val createClassActions = possibleKinds.map { - val classInfo = ClassInfo( - kind = it, - name = name, - targetParent = targetParent, - expectedTypeInfo = TypeInfo.Empty, - typeArguments = typeArguments.map { - if (it != null) TypeInfo(it, Variance.INVARIANT) else TypeInfo(anyType, Variance.INVARIANT) - } - ) - CreateClassFromUsageFix(userType, classInfo) - } - return createPackageAction.singletonOrEmptyList() + createClassActions + val file = element.containingFile as? JetFile ?: return null + + val context = element.analyze() + val qualifier = element.qualifier?.referenceExpression + val qualifierDescriptor = qualifier?.let { context[BindingContext.REFERENCE_TARGET, it] } + + val targetParent = getTargetParentByQualifier(file, qualifier != null, qualifierDescriptor) ?: return null + + val anyType = KotlinBuiltIns.getInstance().anyType + + return ClassInfo( + name = name, + targetParent = targetParent, + expectedTypeInfo = TypeInfo.Empty, + typeArguments = element.typeArgumentsAsTypes.map { + if (it != null) TypeInfo(it, Variance.INVARIANT) else TypeInfo(anyType, Variance.INVARIANT) + } + ) } } diff --git a/idea/src/org/jetbrains/kotlin/idea/quickfix/createFromUsage/createClass/CreateClassFromUsageFactory.kt b/idea/src/org/jetbrains/kotlin/idea/quickfix/createFromUsage/createClass/CreateClassFromUsageFactory.kt new file mode 100644 index 00000000000..6c7194d856b --- /dev/null +++ b/idea/src/org/jetbrains/kotlin/idea/quickfix/createFromUsage/createClass/CreateClassFromUsageFactory.kt @@ -0,0 +1,59 @@ +/* + * Copyright 2010-2015 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.createClass + +import com.intellij.psi.SmartPsiElementPointer +import org.jetbrains.kotlin.diagnostics.Diagnostic +import org.jetbrains.kotlin.idea.quickfix.NullQuickFix +import org.jetbrains.kotlin.idea.quickfix.QuickFixWithDelegateFactory +import org.jetbrains.kotlin.idea.quickfix.createFromUsage.CreateFromUsageFactory +import org.jetbrains.kotlin.psi.JetElement +import org.jetbrains.kotlin.psi.JetUserType + +abstract class CreateClassFromUsageFactory( + val createPackageIsAvailable: Boolean = false +) : CreateFromUsageFactory() { + protected abstract fun getPossibleClassKinds(element: E, diagnostic: Diagnostic): List + + override fun createQuickFixes( + originalElementPointer: SmartPsiElementPointer, + diagnostic: Diagnostic, + quickFixDataFactory: (SmartPsiElementPointer) -> ClassInfo? + ): List { + val originalElement = originalElementPointer.element ?: return emptyList() + + val classFixes = getPossibleClassKinds(originalElement, diagnostic).map { classKind -> + QuickFixWithDelegateFactory { + val currentElement = originalElementPointer.element + val data = quickFixDataFactory(originalElementPointer) + if (currentElement != null && data != null) { + CreateClassFromUsageFix(originalElement, data.copy(kind = classKind)) + } else NullQuickFix + } + } + + if (!createPackageIsAvailable) return classFixes + val refExpr = (originalElement as? JetUserType)?.referenceExpression ?: return classFixes + val packageFix = QuickFixWithDelegateFactory { + quickFixDataFactory(originalElementPointer)?.let { + refExpr.getCreatePackageFixIfApplicable(it.targetParent) + } ?: NullQuickFix + } + + return classFixes + packageFix + } +} \ No newline at end of file diff --git a/idea/src/org/jetbrains/kotlin/idea/quickfix/createFromUsage/createClass/CreateClassFromUsageFix.kt b/idea/src/org/jetbrains/kotlin/idea/quickfix/createFromUsage/createClass/CreateClassFromUsageFix.kt index 2949a68f189..d90cebe3cf8 100644 --- a/idea/src/org/jetbrains/kotlin/idea/quickfix/createFromUsage/createClass/CreateClassFromUsageFix.kt +++ b/idea/src/org/jetbrains/kotlin/idea/quickfix/createFromUsage/createClass/CreateClassFromUsageFix.kt @@ -25,17 +25,13 @@ import com.intellij.psi.PsiClass import com.intellij.psi.PsiElement import com.intellij.psi.PsiFile import com.intellij.psi.PsiPackage -import org.jetbrains.kotlin.idea.JetBundle import org.jetbrains.kotlin.idea.JetFileType import org.jetbrains.kotlin.idea.codeInsight.CodeInsightUtils import org.jetbrains.kotlin.idea.core.refactoring.canRefactor import org.jetbrains.kotlin.idea.core.refactoring.getOrCreateKotlinFile import org.jetbrains.kotlin.idea.quickfix.createFromUsage.CreateFromUsageFixBase import org.jetbrains.kotlin.idea.quickfix.createFromUsage.callableBuilder.* -import org.jetbrains.kotlin.idea.quickfix.createFromUsage.createClass.ClassKind.ENUM_ENTRY -import org.jetbrains.kotlin.idea.quickfix.createFromUsage.createClass.ClassKind.OBJECT -import org.jetbrains.kotlin.idea.quickfix.createFromUsage.createClass.ClassKind.PLAIN_CLASS -import org.jetbrains.kotlin.idea.quickfix.createFromUsage.createClass.ClassKind.TRAIT +import org.jetbrains.kotlin.idea.quickfix.createFromUsage.createClass.ClassKind.* import org.jetbrains.kotlin.idea.util.application.executeCommand import org.jetbrains.kotlin.psi.JetElement import org.jetbrains.kotlin.psi.JetFile @@ -48,11 +44,12 @@ enum class ClassKind(val keyword: String, val description: String) { ENUM_ENTRY("", "enum constant"), ANNOTATION_CLASS("annotation class", "annotation"), TRAIT("interface", "interface"), - OBJECT("object", "object") + OBJECT("object", "object"), + DEFAULT("", "") // Used as a placeholder and must be replaced with one of the kinds above } -public class ClassInfo( - val kind: ClassKind, +public data class ClassInfo( + val kind: ClassKind = ClassKind.DEFAULT, val name: String, val targetParent: PsiElement, val expectedTypeInfo: TypeInfo, @@ -62,19 +59,19 @@ public class ClassInfo( val parameterInfos: List = Collections.emptyList() ) -public class CreateClassFromUsageFix( - element: JetElement, +public class CreateClassFromUsageFix( + element: E, val classInfo: ClassInfo -): CreateFromUsageFixBase(element) { - +): CreateFromUsageFixBase(element) { override fun getText() = "Create ${classInfo.kind.description} '${classInfo.name}'" override fun isAvailable(project: Project, editor: Editor?, file: PsiFile): Boolean { if (!super.isAvailable(project, editor, file)) return false with(classInfo) { + if (kind == DEFAULT) return false if (targetParent is PsiClass) { if (kind == OBJECT || kind == ENUM_ENTRY) return false - if (targetParent.isInterface() && inner) return false + if (targetParent.isInterface && inner) return false } } return true @@ -82,27 +79,27 @@ public class CreateClassFromUsageFix( override fun invoke(project: Project, editor: Editor?, file: JetFile) { fun createFileByPackage(psiPackage: PsiPackage): JetFile? { - val directories = psiPackage.getDirectories().filter { it.canRefactor() } - assert (directories.isNotEmpty()) { "Package '${psiPackage.getQualifiedName()}' must be refactorable" } + val directories = psiPackage.directories.filter { it.canRefactor() } + assert (directories.isNotEmpty()) { "Package '${psiPackage.qualifiedName}' must be refactorable" } val currentModule = ModuleUtilCore.findModuleForPsiElement(file) val preferredDirectory = directories.firstOrNull { ModuleUtilCore.findModuleForPsiElement(it) == currentModule } ?: directories.firstOrNull() - val targetDirectory = if (directories.size() > 1 && !ApplicationManager.getApplication().isUnitTestMode()) { + val targetDirectory = if (directories.size() > 1 && !ApplicationManager.getApplication().isUnitTestMode) { DirectoryChooserUtil.chooseDirectory(directories.toTypedArray(), preferredDirectory, project, HashMap()) } else { preferredDirectory } ?: return null - val fileName = "${classInfo.name}.${JetFileType.INSTANCE.getDefaultExtension()}" + val fileName = "${classInfo.name}.${JetFileType.INSTANCE.defaultExtension}" val targetFile = getOrCreateKotlinFile(fileName, targetDirectory) if (targetFile == null) { - val filePath = "${targetDirectory.getVirtualFile().getPath()}/$fileName" + val filePath = "${targetDirectory.virtualFile.path}/$fileName" CodeInsightUtils.showErrorHint( - targetDirectory.getProject(), + targetDirectory.project, editor!!, "File $filePath already exists but does not correspond to Kotlin file", "Create file", @@ -117,7 +114,7 @@ public class CreateClassFromUsageFix( when (targetParent) { is JetElement, is PsiClass -> targetParent is PsiPackage -> createFileByPackage(targetParent) - else -> throw AssertionError("Unexpected element: " + targetParent.getText()) + else -> throw AssertionError("Unexpected element: " + targetParent.text) } ?: return val constructorInfo = PrimaryConstructorInfo(classInfo, expectedTypeInfo) @@ -125,7 +122,7 @@ public class CreateClassFromUsageFix( Collections.singletonList(constructorInfo), element as JetElement, file, editor, false, kind == PLAIN_CLASS || kind == TRAIT ).createBuilder() builder.placement = CallablePlacement.NoReceiver(targetParent) - project.executeCommand(getText()) { builder.build() } + project.executeCommand(text) { builder.build() } } } } diff --git a/idea/src/org/jetbrains/kotlin/idea/quickfix/createFromUsage/createVariable/CreateLocalVariableActionFactory.kt b/idea/src/org/jetbrains/kotlin/idea/quickfix/createFromUsage/createVariable/CreateLocalVariableActionFactory.kt index b714b19697c..9894e5608c4 100644 --- a/idea/src/org/jetbrains/kotlin/idea/quickfix/createFromUsage/createVariable/CreateLocalVariableActionFactory.kt +++ b/idea/src/org/jetbrains/kotlin/idea/quickfix/createFromUsage/createVariable/CreateLocalVariableActionFactory.kt @@ -17,14 +17,13 @@ package org.jetbrains.kotlin.idea.quickfix.createFromUsage.createVariable import com.intellij.codeInsight.intention.IntentionAction -import com.intellij.openapi.command.CommandProcessor import com.intellij.openapi.editor.Editor import com.intellij.openapi.project.Project import org.jetbrains.kotlin.diagnostics.Diagnostic import org.jetbrains.kotlin.idea.JetBundle +import org.jetbrains.kotlin.idea.core.quickfix.QuickFixUtil import org.jetbrains.kotlin.idea.intentions.ConvertToBlockBodyIntention import org.jetbrains.kotlin.idea.quickfix.JetSingleIntentionActionFactory -import org.jetbrains.kotlin.idea.core.quickfix.QuickFixUtil import org.jetbrains.kotlin.idea.quickfix.createFromUsage.CreateFromUsageFixBase import org.jetbrains.kotlin.idea.quickfix.createFromUsage.callableBuilder.* import org.jetbrains.kotlin.idea.util.application.executeCommand @@ -47,7 +46,7 @@ object CreateLocalVariableActionFactory: JetSingleIntentionActionFactory() { .filter { it is JetBlockExpression || it is JetDeclarationWithBody } .firstOrNull() as? JetElement ?: return null - return object: CreateFromUsageFixBase(refExpr) { + return object: CreateFromUsageFixBase(refExpr) { override fun getText(): String = JetBundle.message("create.local.variable.from.usage", propertyName) override fun invoke(project: Project, editor: Editor?, file: JetFile) { @@ -57,12 +56,12 @@ object CreateLocalVariableActionFactory: JetSingleIntentionActionFactory() { val actualContainer = when (container) { is JetBlockExpression -> container - else -> ConvertToBlockBodyIntention.convert(container as JetDeclarationWithBody).getBodyExpression()!! + else -> ConvertToBlockBodyIntention.convert(container as JetDeclarationWithBody).bodyExpression!! } as JetBlockExpression if (actualContainer != container) { - val bodyExpression = actualContainer.getStatements().first() - originalElement = (bodyExpression as? JetReturnExpression)?.getReturnedExpression() ?: bodyExpression + val bodyExpression = actualContainer.statements.first()!! + originalElement = (bodyExpression as? JetReturnExpression)?.returnedExpression ?: bodyExpression } val typeInfo = TypeInfo( @@ -73,7 +72,7 @@ object CreateLocalVariableActionFactory: JetSingleIntentionActionFactory() { with (CallableBuilderConfiguration(propertyInfo.singletonOrEmptyList(), originalElement, file, editor).createBuilder()) { placement = CallablePlacement.NoReceiver(actualContainer) - project.executeCommand(getText()) { build() } + project.executeCommand(text) { build() } } } } diff --git a/idea/src/org/jetbrains/kotlin/idea/quickfix/createFromUsage/createVariable/CreateParameterActionFactory.kt b/idea/src/org/jetbrains/kotlin/idea/quickfix/createFromUsage/createVariable/CreateParameterActionFactory.kt deleted file mode 100644 index b1ba8e9b60b..00000000000 --- a/idea/src/org/jetbrains/kotlin/idea/quickfix/createFromUsage/createVariable/CreateParameterActionFactory.kt +++ /dev/null @@ -1,147 +0,0 @@ -/* - * Copyright 2010-2015 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.createVariable - -import org.jetbrains.kotlin.psi.JetFile -import org.jetbrains.kotlin.idea.quickfix.JetSingleIntentionActionFactory -import org.jetbrains.kotlin.diagnostics.Diagnostic -import com.intellij.codeInsight.intention.IntentionAction -import org.jetbrains.kotlin.idea.core.quickfix.QuickFixUtil -import org.jetbrains.kotlin.psi.JetSimpleNameExpression -import org.jetbrains.kotlin.psi.psiUtil.getQualifiedElement -import org.jetbrains.kotlin.psi.JetClass -import org.jetbrains.kotlin.idea.quickfix.createFromUsage.callableBuilder.guessTypes -import org.jetbrains.kotlin.resolve.BindingContext -import org.jetbrains.kotlin.descriptors.ClassDescriptor -import org.jetbrains.kotlin.descriptors.FunctionDescriptor -import org.jetbrains.kotlin.idea.refactoring.changeSignature.JetParameterInfo -import com.intellij.psi.PsiElement -import org.jetbrains.kotlin.psi.JetPropertyAccessor -import org.jetbrains.kotlin.psi.JetFunction -import org.jetbrains.kotlin.psi.JetClassInitializer -import org.jetbrains.kotlin.psi.JetClassBody -import org.jetbrains.kotlin.builtins.KotlinBuiltIns -import org.jetbrains.kotlin.psi.psiUtil.parents -import org.jetbrains.kotlin.idea.quickfix.createFromUsage.callableBuilder.getTypeParameters -import org.jetbrains.kotlin.descriptors.ClassDescriptorWithResolutionScopes -import java.util.LinkedHashSet -import org.jetbrains.kotlin.types.JetType -import org.jetbrains.kotlin.descriptors.ConstructorDescriptor -import org.jetbrains.kotlin.idea.refactoring.changeSignature.JetValVar -import org.jetbrains.kotlin.psi.JetEnumEntry -import org.jetbrains.kotlin.psi.JetClassOrObject -import org.jetbrains.kotlin.resolve.DescriptorToSourceUtils -import org.jetbrains.kotlin.psi.JetNamedFunction -import org.jetbrains.kotlin.psi.psiUtil.getAssignmentByLHS -import org.jetbrains.kotlin.idea.quickfix.createFromUsage.callableBuilder.getExpressionForTypeGuess -import org.jetbrains.kotlin.idea.caches.resolve.analyzeFullyAndGetResult -import org.jetbrains.kotlin.utils.addToStdlib.firstIsInstanceOrNull -import org.jetbrains.kotlin.psi.JetDelegationSpecifier -import org.jetbrains.kotlin.psi.psiUtil.getStrictParentOfType - -object CreateParameterActionFactory: JetSingleIntentionActionFactory() { - override fun createAction(diagnostic: Diagnostic): IntentionAction? { - val result = (diagnostic.getPsiFile() as? JetFile)?.analyzeFullyAndGetResult() ?: return null - val context = result.bindingContext - - val refExpr = QuickFixUtil.getParentElementOfType(diagnostic, javaClass()) ?: return null - if (refExpr.getQualifiedElement() != refExpr) return null - - val varExpected = refExpr.getAssignmentByLHS() != null - - val paramType = refExpr.getExpressionForTypeGuess().guessTypes(context, result.moduleDescriptor).let { - when (it.size()) { - 0 -> KotlinBuiltIns.getInstance().getAnyType() - 1 -> it.first() - else -> return null - } - } - - var valOrVar: JetValVar = JetValVar.None - - fun chooseContainingClass(it: PsiElement): JetClass? { - valOrVar = if (varExpected) JetValVar.Var else JetValVar.Val - return it.parents.firstIsInstanceOrNull() as? JetClass - } - - // todo: skip lambdas for now because Change Signature doesn't apply to them yet - val container = refExpr.parents - .filter { - it is JetNamedFunction || it is JetPropertyAccessor || it is JetClassBody || it is JetClassInitializer || - it is JetDelegationSpecifier - } - .firstOrNull() - ?.let { - when { - it is JetNamedFunction && varExpected, - it is JetPropertyAccessor -> chooseContainingClass(it) - it is JetClassInitializer -> it.getParent()?.getParent() as? JetClass - it is JetDelegationSpecifier -> { - val klass = it.getStrictParentOfType() - if (klass != null && !klass.isInterface() && klass !is JetEnumEntry) klass else null - } - it is JetClassBody -> { - val klass = it.getParent() as? JetClass - when { - klass is JetEnumEntry -> chooseContainingClass(klass) - klass != null && klass.isInterface() -> null - else -> klass - } - } - else -> it - } - } ?: return null - - val functionDescriptor = context[BindingContext.DECLARATION_TO_DESCRIPTOR, container]?.let { - if (it is ClassDescriptor) it.getUnsubstitutedPrimaryConstructor() else it - } as? FunctionDescriptor ?: return null - - if (paramType.hasTypeParametersToAdd(functionDescriptor, context)) return null - - return CreateParameterFromUsageFix( - functionDescriptor, - context, - JetParameterInfo(callableDescriptor = functionDescriptor, - name = refExpr.getReferencedName(), - type = paramType, - valOrVar = valOrVar), - refExpr - ) - } -} - -fun JetType.hasTypeParametersToAdd(functionDescriptor: FunctionDescriptor, context: BindingContext): Boolean { - val typeParametersToAdd = LinkedHashSet(getTypeParameters()) - typeParametersToAdd.removeAll(functionDescriptor.getTypeParameters()) - if (typeParametersToAdd.isEmpty()) return false - - val scope = when(functionDescriptor) { - is ConstructorDescriptor -> { - val classDescriptor = functionDescriptor.getContainingDeclaration() as? ClassDescriptorWithResolutionScopes - classDescriptor?.getScopeForClassHeaderResolution() - } - - is FunctionDescriptor -> { - val function = DescriptorToSourceUtils.descriptorToDeclaration(functionDescriptor) as? JetFunction - function?.let { context[BindingContext.RESOLUTION_SCOPE, it.getBodyExpression()] } - } - - else -> null - } ?: return true - - return typeParametersToAdd.any { scope.getClassifier(it.getName()) != it } -} diff --git a/idea/src/org/jetbrains/kotlin/idea/quickfix/createFromUsage/createVariable/CreateParameterByNamedArgumentActionFactory.kt b/idea/src/org/jetbrains/kotlin/idea/quickfix/createFromUsage/createVariable/CreateParameterByNamedArgumentActionFactory.kt index 30a354b6600..f075615ea6a 100644 --- a/idea/src/org/jetbrains/kotlin/idea/quickfix/createFromUsage/createVariable/CreateParameterByNamedArgumentActionFactory.kt +++ b/idea/src/org/jetbrains/kotlin/idea/quickfix/createFromUsage/createVariable/CreateParameterByNamedArgumentActionFactory.kt @@ -16,40 +16,38 @@ package org.jetbrains.kotlin.idea.quickfix.createFromUsage.createVariable -import org.jetbrains.kotlin.idea.quickfix.JetSingleIntentionActionFactory -import org.jetbrains.kotlin.diagnostics.Diagnostic -import com.intellij.codeInsight.intention.IntentionAction -import org.jetbrains.kotlin.psi.JetFile -import org.jetbrains.kotlin.idea.caches.resolve.analyzeFullyAndGetResult -import org.jetbrains.kotlin.idea.core.quickfix.QuickFixUtil -import org.jetbrains.kotlin.idea.quickfix.createFromUsage.callableBuilder.guessTypes import org.jetbrains.kotlin.builtins.KotlinBuiltIns +import org.jetbrains.kotlin.descriptors.FunctionDescriptor +import org.jetbrains.kotlin.diagnostics.Diagnostic +import org.jetbrains.kotlin.idea.caches.resolve.analyzeFullyAndGetResult +import org.jetbrains.kotlin.idea.codeInsight.DescriptorToSourceUtilsIde +import org.jetbrains.kotlin.idea.core.quickfix.QuickFixUtil +import org.jetbrains.kotlin.idea.core.refactoring.canRefactor +import org.jetbrains.kotlin.idea.quickfix.createFromUsage.callableBuilder.guessTypes import org.jetbrains.kotlin.idea.refactoring.changeSignature.JetParameterInfo -import org.jetbrains.kotlin.psi.JetValueArgument -import org.jetbrains.kotlin.psi.JetCallElement +import org.jetbrains.kotlin.psi.* import org.jetbrains.kotlin.psi.psiUtil.getStrictParentOfType import org.jetbrains.kotlin.resolve.calls.callUtil.getResolvedCall -import org.jetbrains.kotlin.idea.codeInsight.DescriptorToSourceUtilsIde -import org.jetbrains.kotlin.idea.core.refactoring.canRefactor -import org.jetbrains.kotlin.psi.JetFunction -import org.jetbrains.kotlin.psi.JetClass -import org.jetbrains.kotlin.descriptors.FunctionDescriptor -public object CreateParameterByNamedArgumentActionFactory: JetSingleIntentionActionFactory() { - override fun createAction(diagnostic: Diagnostic): IntentionAction? { - val result = (diagnostic.getPsiFile() as? JetFile)?.analyzeFullyAndGetResult() ?: return null +public object CreateParameterByNamedArgumentActionFactory: CreateParameterFromUsageFactory() { + override fun getElementOfInterest(diagnostic: Diagnostic): JetValueArgument? { + val argument = QuickFixUtil.getParentElementOfType(diagnostic, javaClass()) ?: return null + return if (argument.isNamed()) argument else null + } + + override fun createQuickFixData(element: JetValueArgument, diagnostic: Diagnostic): CreateParameterData? { + val result = (diagnostic.psiFile as? JetFile)?.analyzeFullyAndGetResult() ?: return null val context = result.bindingContext - val argument = QuickFixUtil.getParentElementOfType(diagnostic, javaClass()) ?: return null - val name = argument.getArgumentName()?.getText() ?: return null - val argumentExpression = argument.getArgumentExpression() + val name = element.getArgumentName()?.text ?: return null + val argumentExpression = element.getArgumentExpression() - val callElement = argument.getStrictParentOfType() ?: return null - val functionDescriptor = callElement.getResolvedCall(context)?.getResultingDescriptor() as? FunctionDescriptor ?: return null - val callable = DescriptorToSourceUtilsIde.getAnyDeclaration(callElement.getProject(), functionDescriptor) ?: return null + val callElement = element.getStrictParentOfType() ?: return null + val functionDescriptor = callElement.getResolvedCall(context)?.resultingDescriptor as? FunctionDescriptor ?: return null + val callable = DescriptorToSourceUtilsIde.getAnyDeclaration(callElement.project, functionDescriptor) ?: return null if (!((callable is JetFunction || callable is JetClass) && callable.canRefactor())) return null - val anyType = KotlinBuiltIns.getInstance().getAnyType() + val anyType = KotlinBuiltIns.getInstance().anyType val paramType = argumentExpression?.guessTypes(context, result.moduleDescriptor)?.let { when (it.size()) { 0 -> anyType @@ -65,6 +63,7 @@ public object CreateParameterByNamedArgumentActionFactory: JetSingleIntentionAct type = paramType, defaultValueForCall = argumentExpression ) - return CreateParameterFromUsageFix(functionDescriptor, context, parameterInfo, argument) + + return CreateParameterData(context, parameterInfo, element) } } diff --git a/idea/src/org/jetbrains/kotlin/idea/quickfix/createFromUsage/createVariable/CreateParameterByRefActionFactory.kt b/idea/src/org/jetbrains/kotlin/idea/quickfix/createFromUsage/createVariable/CreateParameterByRefActionFactory.kt new file mode 100644 index 00000000000..decb5d064a6 --- /dev/null +++ b/idea/src/org/jetbrains/kotlin/idea/quickfix/createFromUsage/createVariable/CreateParameterByRefActionFactory.kt @@ -0,0 +1,140 @@ +/* + * Copyright 2010-2015 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.createVariable + +import com.intellij.psi.PsiElement +import org.jetbrains.kotlin.builtins.KotlinBuiltIns +import org.jetbrains.kotlin.descriptors.ClassDescriptor +import org.jetbrains.kotlin.descriptors.ClassDescriptorWithResolutionScopes +import org.jetbrains.kotlin.descriptors.ConstructorDescriptor +import org.jetbrains.kotlin.descriptors.FunctionDescriptor +import org.jetbrains.kotlin.diagnostics.Diagnostic +import org.jetbrains.kotlin.idea.caches.resolve.analyzeFullyAndGetResult +import org.jetbrains.kotlin.idea.core.quickfix.QuickFixUtil +import org.jetbrains.kotlin.idea.quickfix.createFromUsage.callableBuilder.getExpressionForTypeGuess +import org.jetbrains.kotlin.idea.quickfix.createFromUsage.callableBuilder.getTypeParameters +import org.jetbrains.kotlin.idea.quickfix.createFromUsage.callableBuilder.guessTypes +import org.jetbrains.kotlin.idea.refactoring.changeSignature.JetParameterInfo +import org.jetbrains.kotlin.idea.refactoring.changeSignature.JetValVar +import org.jetbrains.kotlin.psi.* +import org.jetbrains.kotlin.psi.psiUtil.getAssignmentByLHS +import org.jetbrains.kotlin.psi.psiUtil.getQualifiedElement +import org.jetbrains.kotlin.psi.psiUtil.getStrictParentOfType +import org.jetbrains.kotlin.psi.psiUtil.parents +import org.jetbrains.kotlin.resolve.BindingContext +import org.jetbrains.kotlin.resolve.source.getPsi +import org.jetbrains.kotlin.types.JetType +import org.jetbrains.kotlin.utils.addToStdlib.firstIsInstanceOrNull +import java.util.LinkedHashSet + +object CreateParameterByRefActionFactory : CreateParameterFromUsageFactory() { + override fun getElementOfInterest(diagnostic: Diagnostic): JetSimpleNameExpression? { + val refExpr = QuickFixUtil.getParentElementOfType(diagnostic, javaClass()) ?: return null + if (refExpr.getQualifiedElement() != refExpr) return null + return refExpr + } + + override fun createQuickFixData( + element: JetSimpleNameExpression, + diagnostic: Diagnostic + ): CreateParameterData? { + val result = (diagnostic.psiFile as? JetFile)?.analyzeFullyAndGetResult() ?: return null + val context = result.bindingContext + + val varExpected = element.getAssignmentByLHS() != null + + val paramType = element.getExpressionForTypeGuess().guessTypes(context, result.moduleDescriptor).let { + when (it.size()) { + 0 -> KotlinBuiltIns.getInstance().anyType + 1 -> it.first() + else -> return null + } + } + + var valOrVar: JetValVar = JetValVar.None + + fun chooseContainingClass(it: PsiElement): JetClass? { + valOrVar = if (varExpected) JetValVar.Var else JetValVar.Val + return it.parents.firstIsInstanceOrNull() as? JetClass + } + + // todo: skip lambdas for now because Change Signature doesn't apply to them yet + val container = element.parents + .filter { + it is JetNamedFunction || it is JetPropertyAccessor || it is JetClassBody || it is JetClassInitializer || + it is JetDelegationSpecifier + } + .firstOrNull() + ?.let { + when { + it is JetNamedFunction && varExpected, + it is JetPropertyAccessor -> chooseContainingClass(it) + it is JetClassInitializer -> it.parent?.parent as? JetClass + it is JetDelegationSpecifier -> { + val klass = it.getStrictParentOfType() + if (klass != null && !klass.isInterface() && klass !is JetEnumEntry) klass else null + } + it is JetClassBody -> { + val klass = it.parent as? JetClass + when { + klass is JetEnumEntry -> chooseContainingClass(klass) + klass != null && klass.isInterface() -> null + else -> klass + } + } + else -> it + } + } ?: return null + + val functionDescriptor = context[BindingContext.DECLARATION_TO_DESCRIPTOR, container]?.let { + if (it is ClassDescriptor) it.unsubstitutedPrimaryConstructor else it + } as? FunctionDescriptor ?: return null + + if (paramType.hasTypeParametersToAdd(functionDescriptor, context)) return null + + return CreateParameterData( + context, + JetParameterInfo(callableDescriptor = functionDescriptor, + name = element.getReferencedName(), + type = paramType, + valOrVar = valOrVar), + element + ) + } +} + +fun JetType.hasTypeParametersToAdd(functionDescriptor: FunctionDescriptor, context: BindingContext): Boolean { + val typeParametersToAdd = LinkedHashSet(getTypeParameters()) + typeParametersToAdd.removeAll(functionDescriptor.typeParameters) + if (typeParametersToAdd.isEmpty()) return false + + val scope = + when (functionDescriptor) { + is ConstructorDescriptor -> { + (functionDescriptor.containingDeclaration as? ClassDescriptorWithResolutionScopes)?.scopeForClassHeaderResolution + } + + is FunctionDescriptor -> { + val function = functionDescriptor.source.getPsi() as? JetFunction + function?.let { context[BindingContext.RESOLUTION_SCOPE, it.bodyExpression] } + } + + else -> null + } ?: return true + + return typeParametersToAdd.any { scope.getClassifier(it.name) != it } +} diff --git a/idea/src/org/jetbrains/kotlin/idea/quickfix/createFromUsage/createVariable/CreateParameterFromUsageFactory.kt b/idea/src/org/jetbrains/kotlin/idea/quickfix/createFromUsage/createVariable/CreateParameterFromUsageFactory.kt new file mode 100644 index 00000000000..56b99adf5cd --- /dev/null +++ b/idea/src/org/jetbrains/kotlin/idea/quickfix/createFromUsage/createVariable/CreateParameterFromUsageFactory.kt @@ -0,0 +1,50 @@ +/* + * Copyright 2010-2015 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.createVariable + +import com.intellij.psi.SmartPsiElementPointer +import org.jetbrains.kotlin.descriptors.FunctionDescriptor +import org.jetbrains.kotlin.diagnostics.Diagnostic +import org.jetbrains.kotlin.idea.quickfix.NullQuickFix +import org.jetbrains.kotlin.idea.quickfix.QuickFixWithDelegateFactory +import org.jetbrains.kotlin.idea.quickfix.createFromUsage.CreateFromUsageFactory +import org.jetbrains.kotlin.idea.refactoring.changeSignature.JetParameterInfo +import org.jetbrains.kotlin.psi.JetElement +import org.jetbrains.kotlin.resolve.BindingContext + +data class CreateParameterData( + val context: BindingContext, + val parameterInfo: JetParameterInfo, + val originalExpression: E +) + +abstract class CreateParameterFromUsageFactory: CreateFromUsageFactory?>() { + override fun createQuickFix( + originalElementPointer: SmartPsiElementPointer, + diagnostic: Diagnostic, + quickFixDataFactory: (SmartPsiElementPointer) -> CreateParameterData? + ): QuickFixWithDelegateFactory? { + return QuickFixWithDelegateFactory { + quickFixDataFactory(originalElementPointer)?.let { data -> + CreateParameterFromUsageFix(data.parameterInfo.callableDescriptor as FunctionDescriptor, + data.context, + data.parameterInfo, + data.originalExpression) + } ?: NullQuickFix + } + } +} \ No newline at end of file diff --git a/idea/src/org/jetbrains/kotlin/idea/quickfix/createFromUsage/createVariable/CreateParameterFromUsageFix.kt b/idea/src/org/jetbrains/kotlin/idea/quickfix/createFromUsage/createVariable/CreateParameterFromUsageFix.kt index 9111b5bf729..4bbfef603a8 100644 --- a/idea/src/org/jetbrains/kotlin/idea/quickfix/createFromUsage/createVariable/CreateParameterFromUsageFix.kt +++ b/idea/src/org/jetbrains/kotlin/idea/quickfix/createFromUsage/createVariable/CreateParameterFromUsageFix.kt @@ -27,14 +27,14 @@ import org.jetbrains.kotlin.psi.JetElement import org.jetbrains.kotlin.psi.JetFile import org.jetbrains.kotlin.resolve.BindingContext -public class CreateParameterFromUsageFix( +public class CreateParameterFromUsageFix( val functionDescriptor: FunctionDescriptor, val bindingContext: BindingContext, val parameterInfo: JetParameterInfo, - val defaultValueContext: JetElement -) : CreateFromUsageFixBase(defaultValueContext) { + val defaultValueContext: E +) : CreateFromUsageFixBase(defaultValueContext) { override fun getText(): String { - return JetBundle.message("create.parameter.from.usage", parameterInfo.getName()) + return JetBundle.message("create.parameter.from.usage", parameterInfo.name) } override fun invoke(project: Project, editor: Editor?, file: JetFile) { @@ -46,6 +46,6 @@ public class CreateParameterFromUsageFix( override fun performSilently(affectedFunctions: Collection): Boolean = false } - runChangeSignature(project, functionDescriptor, config, bindingContext, defaultValueContext, getText()) + runChangeSignature(project, functionDescriptor, config, bindingContext, defaultValueContext, text) } }