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 -> {