From 29175e96e9c94655c26aa24edcd715e3263d0935 Mon Sep 17 00:00:00 2001 From: Valentin Kipyatkov Date: Wed, 19 Oct 2016 19:18:49 +0300 Subject: [PATCH] Code refactoring --- .idea/modules.xml | 1 + .../InsertExplicitTypeArgumentsIntention.kt | 2 +- .../idea-replacement-engine.iml | 23 +++ .../idea/replacement/ReplacementBuilder.kt | 143 ++++++++++++++++++ .../idea/replacement/ReplacementExpression.kt | 47 ++++++ idea/idea.iml | 1 + .../quickfix/replaceWith/CallKindHandler.kt | 7 +- .../CallableUsageReplacementStrategy.kt | 17 ++- .../ClassUsageReplacementStrategy.kt | 3 +- .../ReplaceWithAnnotationAnalyzer.kt | 113 +------------- 10 files changed, 239 insertions(+), 118 deletions(-) rename idea/{ => idea-core}/src/org/jetbrains/kotlin/idea/intentions/InsertExplicitTypeArgumentsIntention.kt (98%) create mode 100644 idea/idea-replacement-engine/idea-replacement-engine.iml create mode 100644 idea/idea-replacement-engine/src/org/jetbrains/kotlin/idea/replacement/ReplacementBuilder.kt create mode 100644 idea/idea-replacement-engine/src/org/jetbrains/kotlin/idea/replacement/ReplacementExpression.kt diff --git a/.idea/modules.xml b/.idea/modules.xml index 021661f45d5..91ba983ad1f 100644 --- a/.idea/modules.xml +++ b/.idea/modules.xml @@ -50,6 +50,7 @@ + diff --git a/idea/src/org/jetbrains/kotlin/idea/intentions/InsertExplicitTypeArgumentsIntention.kt b/idea/idea-core/src/org/jetbrains/kotlin/idea/intentions/InsertExplicitTypeArgumentsIntention.kt similarity index 98% rename from idea/src/org/jetbrains/kotlin/idea/intentions/InsertExplicitTypeArgumentsIntention.kt rename to idea/idea-core/src/org/jetbrains/kotlin/idea/intentions/InsertExplicitTypeArgumentsIntention.kt index cbb893906da..f46ea0b32f3 100644 --- a/idea/src/org/jetbrains/kotlin/idea/intentions/InsertExplicitTypeArgumentsIntention.kt +++ b/idea/idea-core/src/org/jetbrains/kotlin/idea/intentions/InsertExplicitTypeArgumentsIntention.kt @@ -1,5 +1,5 @@ /* - * Copyright 2010-2015 JetBrains s.r.o. + * Copyright 2010-2016 JetBrains s.r.o. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/idea/idea-replacement-engine/idea-replacement-engine.iml b/idea/idea-replacement-engine/idea-replacement-engine.iml new file mode 100644 index 00000000000..f749c7a75eb --- /dev/null +++ b/idea/idea-replacement-engine/idea-replacement-engine.iml @@ -0,0 +1,23 @@ + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/idea/idea-replacement-engine/src/org/jetbrains/kotlin/idea/replacement/ReplacementBuilder.kt b/idea/idea-replacement-engine/src/org/jetbrains/kotlin/idea/replacement/ReplacementBuilder.kt new file mode 100644 index 00000000000..0eb02387675 --- /dev/null +++ b/idea/idea-replacement-engine/src/org/jetbrains/kotlin/idea/replacement/ReplacementBuilder.kt @@ -0,0 +1,143 @@ +/* + * Copyright 2010-2016 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.jetbrains.kotlin.idea.replacement + +import org.jetbrains.kotlin.descriptors.CallableDescriptor +import org.jetbrains.kotlin.descriptors.TypeParameterDescriptor +import org.jetbrains.kotlin.descriptors.ValueParameterDescriptor +import org.jetbrains.kotlin.idea.core.asExpression +import org.jetbrains.kotlin.idea.core.copied +import org.jetbrains.kotlin.idea.core.replaced +import org.jetbrains.kotlin.idea.imports.importableFqName +import org.jetbrains.kotlin.idea.intentions.InsertExplicitTypeArgumentsIntention +import org.jetbrains.kotlin.idea.references.canBeResolvedViaImport +import org.jetbrains.kotlin.idea.references.mainReference +import org.jetbrains.kotlin.idea.resolve.ResolutionFacade +import org.jetbrains.kotlin.name.FqName +import org.jetbrains.kotlin.psi.* +import org.jetbrains.kotlin.psi.psiUtil.forEachDescendantOfType +import org.jetbrains.kotlin.psi.psiUtil.getReceiverExpression +import org.jetbrains.kotlin.resolve.BindingContext +import org.jetbrains.kotlin.resolve.BindingTraceContext +import org.jetbrains.kotlin.resolve.calls.callUtil.getResolvedCall +import org.jetbrains.kotlin.resolve.calls.model.isReallySuccess +import org.jetbrains.kotlin.resolve.calls.smartcasts.DataFlowInfo +import org.jetbrains.kotlin.resolve.descriptorUtil.isExtension +import org.jetbrains.kotlin.resolve.descriptorUtil.module +import org.jetbrains.kotlin.resolve.scopes.LexicalScope +import org.jetbrains.kotlin.resolve.scopes.receivers.ImplicitReceiver +import org.jetbrains.kotlin.types.TypeUtils +import org.jetbrains.kotlin.types.expressions.ExpressionTypingServices +import org.jetbrains.kotlin.types.expressions.PreliminaryDeclarationVisitor +import java.util.* + +class ReplacementBuilder( + private val targetCallable: CallableDescriptor, + private val resolutionFacade: ResolutionFacade +) { + fun buildReplacementExpression( + expression: KtExpression, + resolutionScope: LexicalScope, + importFqNames: Collection = emptyList(), + copyExpression: Boolean = true + ): ReplacementExpression { + @Suppress("NAME_SHADOWING") + var expression = if (copyExpression) expression.copied() else expression + + var bindingContext = analyzeInContext(expression, resolutionScope) + + val typeArgsToAdd = ArrayList>() + expression.forEachDescendantOfType { + if (InsertExplicitTypeArgumentsIntention.isApplicableTo(it, bindingContext)) { + typeArgsToAdd.add(it to InsertExplicitTypeArgumentsIntention.createTypeArguments(it, bindingContext)!!) + } + } + + if (typeArgsToAdd.isNotEmpty()) { + for ((callExpr, typeArgs) in typeArgsToAdd) { + callExpr.addAfter(typeArgs, callExpr.calleeExpression) + } + + // reanalyze expression - new usages of type parameters may be added + bindingContext = analyzeInContext(expression, resolutionScope) + } + + val receiversToAdd = ArrayList>() + val resultImportFqNames = importFqNames.toMutableSet() + + val psiFactory = KtPsiFactory(expression) + + expression.forEachDescendantOfType { expression -> + val target = bindingContext[BindingContext.REFERENCE_TARGET, expression] ?: return@forEachDescendantOfType + + //TODO: other types of references ('[]' etc) + if (expression.mainReference.canBeResolvedViaImport(target)) { + resultImportFqNames.add(target.importableFqName!!) + } + + if (expression.getReceiverExpression() == null) { + if (target is ValueParameterDescriptor && target.containingDeclaration == targetCallable) { + expression.putCopyableUserData(ReplacementExpression.PARAMETER_USAGE_KEY, target.name) + } + else if (target is TypeParameterDescriptor && target.containingDeclaration == targetCallable) { + expression.putCopyableUserData(ReplacementExpression.TYPE_PARAMETER_USAGE_KEY, target.name) + } + + val resolvedCall = expression.getResolvedCall(bindingContext) + if (resolvedCall != null && resolvedCall.isReallySuccess()) { + val receiver = if (resolvedCall.resultingDescriptor.isExtension) + resolvedCall.extensionReceiver + else + resolvedCall.dispatchReceiver + if (receiver is ImplicitReceiver) { + val receiverExpression = receiver.asExpression(resolutionScope, psiFactory) + if (receiverExpression != null) { + receiversToAdd.add(expression to receiverExpression) + } + } + } + } + } + + // add receivers in reverse order because arguments of a call were processed after the callee's name + for ((expr, receiverExpression) in receiversToAdd.asReversed()) { + val expressionToReplace = expr.parent as? KtCallExpression ?: expr + val newExpr = expressionToReplace.replaced(psiFactory.createExpressionByPattern("$0.$1", receiverExpression, expressionToReplace)) + if (expressionToReplace == expression) { + expression = newExpr + } + } + + return ReplacementExpression(expression, resultImportFqNames) + } + + //TODO: there can be expected type and maybe something else + private fun analyzeInContext(expression: KtExpression, scope: LexicalScope): BindingContext { + val module = scope.ownerDescriptor.module + val traceContext = BindingTraceContext() + val frontendService = if (module.builtIns.builtInsModule == module) { + // TODO: doubtful place, do we require this module or not? Built-ins module doesn't have some necessary components... + resolutionFacade.getFrontendService(ExpressionTypingServices::class.java) + } + else { + resolutionFacade.getFrontendService(module, ExpressionTypingServices::class.java) + } + PreliminaryDeclarationVisitor.createForExpression(expression, traceContext) + frontendService.getTypeInfo(scope, expression, TypeUtils.NO_EXPECTED_TYPE, DataFlowInfo.EMPTY, traceContext, false) + return traceContext.bindingContext + } +} \ No newline at end of file diff --git a/idea/idea-replacement-engine/src/org/jetbrains/kotlin/idea/replacement/ReplacementExpression.kt b/idea/idea-replacement-engine/src/org/jetbrains/kotlin/idea/replacement/ReplacementExpression.kt new file mode 100644 index 00000000000..5253e90e6e0 --- /dev/null +++ b/idea/idea-replacement-engine/src/org/jetbrains/kotlin/idea/replacement/ReplacementExpression.kt @@ -0,0 +1,47 @@ +/* + * Copyright 2010-2016 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.jetbrains.kotlin.idea.replacement + +import com.intellij.openapi.util.Key +import org.jetbrains.kotlin.idea.core.copied +import org.jetbrains.kotlin.idea.replacement.ReplacementExpression.Companion.PARAMETER_USAGE_KEY +import org.jetbrains.kotlin.idea.replacement.ReplacementExpression.Companion.TYPE_PARAMETER_USAGE_KEY +import org.jetbrains.kotlin.name.FqName +import org.jetbrains.kotlin.name.Name +import org.jetbrains.kotlin.psi.KtExpression + +/** + * Represents an expression to replace usages of particular callable. + * The expression should be preprocessed in the following way: + * * Type arguments for all calls should be made explicit + * * All external symbols to be imported should be either referenced via fully-qualified form or included into [fqNamesToImport] + * * All usages of value parameters (of our callable) should be marked with [PARAMETER_USAGE_KEY] copyable user data (holds the name of the corresponding parameter) + * * All usages of type parameters (of our callable) should be marked with [TYPE_PARAMETER_USAGE_KEY] copyable user data (holds the name of the corresponding type parameter) + * Use [ReplacementBuilder.buildReplacementExpression]. + */ +//TODO: should it be data class? +data class ReplacementExpression( + val expression: KtExpression, + val fqNamesToImport: Collection +) { + fun copy() = ReplacementExpression(expression.copied(), fqNamesToImport) + + companion object { + val PARAMETER_USAGE_KEY: Key = Key("PARAMETER_USAGE") + val TYPE_PARAMETER_USAGE_KEY: Key = Key("TYPE_PARAMETER_USAGE") + } +} \ No newline at end of file diff --git a/idea/idea.iml b/idea/idea.iml index ddb7d7af13b..fe8f5620a20 100644 --- a/idea/idea.iml +++ b/idea/idea.iml @@ -55,5 +55,6 @@ + \ No newline at end of file diff --git a/idea/src/org/jetbrains/kotlin/idea/quickfix/replaceWith/CallKindHandler.kt b/idea/src/org/jetbrains/kotlin/idea/quickfix/replaceWith/CallKindHandler.kt index 31451b5e6d8..4b3ab131c9c 100644 --- a/idea/src/org/jetbrains/kotlin/idea/quickfix/replaceWith/CallKindHandler.kt +++ b/idea/src/org/jetbrains/kotlin/idea/quickfix/replaceWith/CallKindHandler.kt @@ -17,13 +17,14 @@ package org.jetbrains.kotlin.idea.quickfix.replaceWith import org.jetbrains.kotlin.idea.core.replaced +import org.jetbrains.kotlin.idea.replacement.ReplacementExpression import org.jetbrains.kotlin.psi.* import org.jetbrains.kotlin.psi.psiUtil.getQualifiedExpressionForSelectorOrThis internal interface CallKindHandler { val elementToReplace: KtElement - fun precheckReplacementPattern(pattern: ReplaceWithAnnotationAnalyzer.ReplacementExpression): Boolean + fun precheckReplacementPattern(pattern: ReplacementExpression): Boolean fun wrapGeneratedExpression(expression: KtExpression): KtElement @@ -33,7 +34,7 @@ internal interface CallKindHandler { internal class CallExpressionHandler(callElement: KtExpression) : CallKindHandler { override val elementToReplace = callElement.getQualifiedExpressionForSelectorOrThis() - override fun precheckReplacementPattern(pattern: ReplaceWithAnnotationAnalyzer.ReplacementExpression) = true + override fun precheckReplacementPattern(pattern: ReplacementExpression) = true override fun wrapGeneratedExpression(expression: KtExpression) = expression @@ -43,7 +44,7 @@ internal class CallExpressionHandler(callElement: KtExpression) : CallKindHandle internal class AnnotationEntryHandler(annotationEntry: KtAnnotationEntry) : CallKindHandler { override val elementToReplace = annotationEntry - override fun precheckReplacementPattern(pattern: ReplaceWithAnnotationAnalyzer.ReplacementExpression): Boolean { + override fun precheckReplacementPattern(pattern: ReplacementExpression): Boolean { //TODO return true } diff --git a/idea/src/org/jetbrains/kotlin/idea/quickfix/replaceWith/CallableUsageReplacementStrategy.kt b/idea/src/org/jetbrains/kotlin/idea/quickfix/replaceWith/CallableUsageReplacementStrategy.kt index a2bd8a55ff9..3cefa68a4f5 100644 --- a/idea/src/org/jetbrains/kotlin/idea/quickfix/replaceWith/CallableUsageReplacementStrategy.kt +++ b/idea/src/org/jetbrains/kotlin/idea/quickfix/replaceWith/CallableUsageReplacementStrategy.kt @@ -30,6 +30,7 @@ import org.jetbrains.kotlin.idea.core.* import org.jetbrains.kotlin.idea.intentions.OperatorToFunctionIntention import org.jetbrains.kotlin.idea.intentions.RemoveExplicitTypeArgumentsIntention import org.jetbrains.kotlin.idea.intentions.setType +import org.jetbrains.kotlin.idea.replacement.ReplacementExpression import org.jetbrains.kotlin.idea.util.CommentSaver import org.jetbrains.kotlin.idea.util.IdeDescriptorRenderers import org.jetbrains.kotlin.idea.util.ImportInsertHelper @@ -55,7 +56,7 @@ import org.jetbrains.kotlin.utils.addIfNotNull import java.util.* class CallableUsageReplacementStrategy( - private val replacement: ReplaceWithAnnotationAnalyzer.ReplacementExpression + private val replacement: ReplacementExpression ) : UsageReplacementStrategy { override fun createReplacer(usage: KtSimpleNameExpression): (() -> KtElement)? { @@ -95,7 +96,7 @@ private fun performCallReplacement( resolvedCall: ResolvedCall, callElement: KtElement, callKindHandler: CallKindHandler, - replacement: ReplaceWithAnnotationAnalyzer.ReplacementExpression + replacement: ReplacementExpression ): KtElement { val project = element.project val psiFactory = KtPsiFactory(project) @@ -196,7 +197,7 @@ private fun ConstructedExpressionWrapper.processValueParameterUsages( val parameterName = parameter.name val usages = expression.collectDescendantsOfType { - it[ReplaceWithAnnotationAnalyzer.PARAMETER_USAGE_KEY] == parameterName + it[ReplacementExpression.PARAMETER_USAGE_KEY] == parameterName } usages.forEach { val usageArgument = it.parent as? KtValueArgument @@ -235,7 +236,7 @@ private fun ConstructedExpressionWrapper.processTypeParameterUsages(resolvedCall for ((index, typeParameter) in typeParameters.withIndex()) { val parameterName = typeParameter.name val usages = expression.collectDescendantsOfType { - it[ReplaceWithAnnotationAnalyzer.TYPE_PARAMETER_USAGE_KEY] == parameterName + it[ReplacementExpression.TYPE_PARAMETER_USAGE_KEY] == parameterName } val factory = KtPsiFactory(callElement) @@ -353,13 +354,13 @@ private fun argumentForParameter( val (expression, parameterUsages) = defaultValue for ((param, usages) in parameterUsages) { - usages.forEach { it.put(ReplaceWithAnnotationAnalyzer.PARAMETER_USAGE_KEY, param.name) } + usages.forEach { it.put(ReplacementExpression.PARAMETER_USAGE_KEY, param.name) } } val expressionCopy = expression.copied() // clean up user data in original - expression.forEachDescendantOfType { it.clear(ReplaceWithAnnotationAnalyzer.PARAMETER_USAGE_KEY) } + expression.forEachDescendantOfType { it.clear(ReplacementExpression.PARAMETER_USAGE_KEY) } return Argument(expressionCopy, null/*TODO*/, isDefaultValue = true) } @@ -430,8 +431,8 @@ private fun postProcessInsertedCode(range: PsiChildRange): PsiChildRange { // clean up user data it.forEachDescendantOfType { it.clear(USER_CODE_KEY) - it.clear(ReplaceWithAnnotationAnalyzer.PARAMETER_USAGE_KEY) - it.clear(ReplaceWithAnnotationAnalyzer.TYPE_PARAMETER_USAGE_KEY) + it.clear(ReplacementExpression.PARAMETER_USAGE_KEY) + it.clear(ReplacementExpression.TYPE_PARAMETER_USAGE_KEY) it.clear(PARAMETER_VALUE_KEY) it.clear(RECEIVER_VALUE_KEY) it.clear(WAS_FUNCTION_LITERAL_ARGUMENT_KEY) diff --git a/idea/src/org/jetbrains/kotlin/idea/quickfix/replaceWith/ClassUsageReplacementStrategy.kt b/idea/src/org/jetbrains/kotlin/idea/quickfix/replaceWith/ClassUsageReplacementStrategy.kt index ac68bc6ba78..d8de59ea5cc 100644 --- a/idea/src/org/jetbrains/kotlin/idea/quickfix/replaceWith/ClassUsageReplacementStrategy.kt +++ b/idea/src/org/jetbrains/kotlin/idea/quickfix/replaceWith/ClassUsageReplacementStrategy.kt @@ -19,13 +19,14 @@ package org.jetbrains.kotlin.idea.quickfix.replaceWith import com.intellij.openapi.project.Project import org.jetbrains.kotlin.idea.core.ShortenReferences import org.jetbrains.kotlin.idea.core.replaced +import org.jetbrains.kotlin.idea.replacement.ReplacementExpression import org.jetbrains.kotlin.psi.* import org.jetbrains.kotlin.psi.psiUtil.getQualifiedExpressionForSelectorOrThis import org.jetbrains.kotlin.utils.addToStdlib.check class ClassUsageReplacementStrategy( typeReplacement: KtUserType?, - constructorReplacement: ReplaceWithAnnotationAnalyzer.ReplacementExpression?, + constructorReplacement: ReplacementExpression?, project: Project ) : UsageReplacementStrategy { diff --git a/idea/src/org/jetbrains/kotlin/idea/quickfix/replaceWith/ReplaceWithAnnotationAnalyzer.kt b/idea/src/org/jetbrains/kotlin/idea/quickfix/replaceWith/ReplaceWithAnnotationAnalyzer.kt index 49002f512f9..1ed3da50e34 100644 --- a/idea/src/org/jetbrains/kotlin/idea/quickfix/replaceWith/ReplaceWithAnnotationAnalyzer.kt +++ b/idea/src/org/jetbrains/kotlin/idea/quickfix/replaceWith/ReplaceWithAnnotationAnalyzer.kt @@ -16,57 +16,34 @@ package org.jetbrains.kotlin.idea.quickfix.replaceWith -import com.intellij.openapi.util.Key import org.jetbrains.kotlin.descriptors.* import org.jetbrains.kotlin.idea.caches.resolve.resolveImportReference -import org.jetbrains.kotlin.idea.core.asExpression -import org.jetbrains.kotlin.idea.core.copied -import org.jetbrains.kotlin.idea.core.replaced -import org.jetbrains.kotlin.idea.imports.importableFqName -import org.jetbrains.kotlin.idea.intentions.InsertExplicitTypeArgumentsIntention import org.jetbrains.kotlin.idea.references.KtSimpleNameReference -import org.jetbrains.kotlin.idea.references.canBeResolvedViaImport import org.jetbrains.kotlin.idea.references.mainReference +import org.jetbrains.kotlin.idea.replacement.ReplacementBuilder +import org.jetbrains.kotlin.idea.replacement.ReplacementExpression import org.jetbrains.kotlin.idea.resolve.ResolutionFacade import org.jetbrains.kotlin.idea.resolve.frontendService import org.jetbrains.kotlin.name.FqName import org.jetbrains.kotlin.name.FqNameUnsafe -import org.jetbrains.kotlin.name.Name -import org.jetbrains.kotlin.psi.* +import org.jetbrains.kotlin.psi.KtNameReferenceExpression +import org.jetbrains.kotlin.psi.KtPsiFactory +import org.jetbrains.kotlin.psi.KtUserType import org.jetbrains.kotlin.psi.psiUtil.forEachDescendantOfType -import org.jetbrains.kotlin.psi.psiUtil.getReceiverExpression import org.jetbrains.kotlin.resolve.* -import org.jetbrains.kotlin.resolve.calls.callUtil.getResolvedCall -import org.jetbrains.kotlin.resolve.calls.model.isReallySuccess -import org.jetbrains.kotlin.resolve.calls.smartcasts.DataFlowInfo import org.jetbrains.kotlin.resolve.descriptorUtil.fqNameUnsafe -import org.jetbrains.kotlin.resolve.descriptorUtil.isExtension import org.jetbrains.kotlin.resolve.descriptorUtil.module import org.jetbrains.kotlin.resolve.lazy.DefaultImportProvider import org.jetbrains.kotlin.resolve.lazy.descriptors.ClassResolutionScopesSupport import org.jetbrains.kotlin.resolve.scopes.* -import org.jetbrains.kotlin.resolve.scopes.receivers.ImplicitReceiver import org.jetbrains.kotlin.resolve.scopes.utils.chainImportingScopes import org.jetbrains.kotlin.resolve.scopes.utils.memberScopeAsImportingScope import org.jetbrains.kotlin.storage.LockBasedStorageManager -import org.jetbrains.kotlin.types.TypeUtils -import org.jetbrains.kotlin.types.expressions.ExpressionTypingServices -import org.jetbrains.kotlin.types.expressions.PreliminaryDeclarationVisitor import java.util.* data class ReplaceWith(val pattern: String, val imports: List) object ReplaceWithAnnotationAnalyzer { - val PARAMETER_USAGE_KEY: Key = Key("PARAMETER_USAGE") - val TYPE_PARAMETER_USAGE_KEY: Key = Key("TYPE_PARAMETER_USAGE") - - data class ReplacementExpression( - val expression: KtExpression, - val fqNamesToImport: Collection - ) { - fun copy() = ReplacementExpression(expression.copied(), fqNamesToImport) - } - fun analyzeCallableReplacement( annotation: ReplaceWith, symbolDescriptor: CallableDescriptor, @@ -85,7 +62,7 @@ object ReplaceWithAnnotationAnalyzer { resolutionFacade: ResolutionFacade ): ReplacementExpression? { val psiFactory = KtPsiFactory(resolutionFacade.project) - var expression = try { + val expression = try { psiFactory.createExpression(annotation.pattern) } catch(t: Throwable) { @@ -98,69 +75,8 @@ object ReplaceWithAnnotationAnalyzer { val scope = getResolutionScope(symbolDescriptor, symbolDescriptor, listOf(explicitImportsScope) + defaultImportsScopes) ?: return null - var bindingContext = analyzeInContext(expression, module, scope, resolutionFacade) - - val typeArgsToAdd = ArrayList>() - expression.forEachDescendantOfType { - if (InsertExplicitTypeArgumentsIntention.isApplicableTo(it, bindingContext)) { - typeArgsToAdd.add(it to InsertExplicitTypeArgumentsIntention.createTypeArguments(it, bindingContext)!!) - } - } - - if (typeArgsToAdd.isNotEmpty()) { - for ((callExpr, typeArgs) in typeArgsToAdd) { - callExpr.addAfter(typeArgs, callExpr.calleeExpression) - } - - // reanalyze expression - new usages of type parameters may be added - bindingContext = analyzeInContext(expression, module, scope, resolutionFacade) - } - - val receiversToAdd = ArrayList>() - val importFqNames = importFqNames(annotation).toMutableSet() - - expression.forEachDescendantOfType { expression -> - val target = bindingContext[BindingContext.REFERENCE_TARGET, expression] ?: return@forEachDescendantOfType - - //TODO: other types of references ('[]' etc) - if (expression.mainReference.canBeResolvedViaImport(target)) { - importFqNames.add(target.importableFqName!!) - } - - if (expression.getReceiverExpression() == null) { - if (target is ValueParameterDescriptor && target.containingDeclaration == symbolDescriptor) { - expression.putCopyableUserData(PARAMETER_USAGE_KEY, target.name) - } - else if (target is TypeParameterDescriptor && target.containingDeclaration == symbolDescriptor) { - expression.putCopyableUserData(TYPE_PARAMETER_USAGE_KEY, target.name) - } - - val resolvedCall = expression.getResolvedCall(bindingContext) - if (resolvedCall != null && resolvedCall.isReallySuccess()) { - val receiver = if (resolvedCall.resultingDescriptor.isExtension) - resolvedCall.extensionReceiver - else - resolvedCall.dispatchReceiver - if (receiver is ImplicitReceiver) { - val receiverExpression = receiver.asExpression(scope, psiFactory) - if (receiverExpression != null) { - receiversToAdd.add(expression to receiverExpression) - } - } - } - } - } - - // add receivers in reverse order because arguments of a call were processed after the callee's name - for ((expr, receiverExpression) in receiversToAdd.asReversed()) { - val expressionToReplace = expr.parent as? KtCallExpression ?: expr - val newExpr = expressionToReplace.replaced(psiFactory.createExpressionByPattern("$0.$1", receiverExpression, expressionToReplace)) - if (expressionToReplace == expression) { - expression = newExpr - } - } - - return ReplacementExpression(expression, importFqNames) + return ReplacementBuilder(symbolDescriptor, resolutionFacade) + .buildReplacementExpression(expression, scope, importFqNames(annotation), copyExpression = false) } fun analyzeClassReplacement( @@ -232,19 +148,6 @@ object ReplaceWithAnnotationAnalyzer { .map(FqNameUnsafe::toSafe) } - private fun analyzeInContext( - expression: KtExpression, - module: ModuleDescriptor, - scope: LexicalScope, - resolutionFacade: ResolutionFacade - ): BindingContext { - val trace = BindingTraceContext() - val expressionTypingServices = resolutionFacade.getFrontendService(module, ExpressionTypingServices::class.java) - PreliminaryDeclarationVisitor.createForExpression(expression, trace) - expressionTypingServices.getTypeInfo(scope, expression, TypeUtils.NO_EXPECTED_TYPE, DataFlowInfo.EMPTY, trace, false) - return trace.bindingContext - } - private fun getResolutionScope(descriptor: DeclarationDescriptor, ownerDescriptor: DeclarationDescriptor, additionalScopes: Collection): LexicalScope? { return when (descriptor) { is PackageFragmentDescriptor -> {