Code refactoring
This commit is contained in:
Generated
+1
@@ -50,6 +50,7 @@
|
||||
<module fileurl="file://$PROJECT_DIR$/idea/idea-live-templates/idea-live-templates.iml" filepath="$PROJECT_DIR$/idea/idea-live-templates/idea-live-templates.iml" group="ide" />
|
||||
<module fileurl="file://$PROJECT_DIR$/idea/idea-maven/idea-maven.iml" filepath="$PROJECT_DIR$/idea/idea-maven/idea-maven.iml" group="ide" />
|
||||
<module fileurl="file://$PROJECT_DIR$/idea/idea-repl/idea-repl.iml" filepath="$PROJECT_DIR$/idea/idea-repl/idea-repl.iml" group="ide" />
|
||||
<module fileurl="file://$PROJECT_DIR$/idea/idea-replacement-engine/idea-replacement-engine.iml" filepath="$PROJECT_DIR$/idea/idea-replacement-engine/idea-replacement-engine.iml" group="ide" />
|
||||
<module fileurl="file://$PROJECT_DIR$/idea-runner/idea-runner.iml" filepath="$PROJECT_DIR$/idea-runner/idea-runner.iml" group="ide" />
|
||||
<module fileurl="file://$PROJECT_DIR$/idea/idea-test-framework/idea-test-framework.iml" filepath="$PROJECT_DIR$/idea/idea-test-framework/idea-test-framework.iml" group="ide" />
|
||||
<module fileurl="file://$PROJECT_DIR$/compiler/preloader/instrumentation/instrumentation.iml" filepath="$PROJECT_DIR$/compiler/preloader/instrumentation/instrumentation.iml" group="compiler/cli" />
|
||||
|
||||
+1
-1
@@ -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.
|
||||
@@ -0,0 +1,23 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<module type="JAVA_MODULE" version="4">
|
||||
<component name="NewModuleRootManager" inherit-compiler-output="true">
|
||||
<exclude-output />
|
||||
<content url="file://$MODULE_DIR$">
|
||||
<sourceFolder url="file://$MODULE_DIR$/src" isTestSource="false" />
|
||||
</content>
|
||||
<orderEntry type="jdk" jdkName="1.8" jdkType="JavaSDK" />
|
||||
<orderEntry type="sourceFolder" forTests="false" />
|
||||
<orderEntry type="module" module-name="descriptors" />
|
||||
<orderEntry type="module" module-name="idea-analysis" />
|
||||
<orderEntry type="library" name="idea-full" level="project" />
|
||||
<orderEntry type="module" module-name="descriptor.loader.java" />
|
||||
<orderEntry type="module" module-name="light-classes" />
|
||||
<orderEntry type="module" module-name="frontend" />
|
||||
<orderEntry type="module" module-name="idea-core" />
|
||||
<orderEntry type="module" module-name="frontend.java" />
|
||||
<orderEntry type="module" module-name="util" />
|
||||
<orderEntry type="library" scope="PROVIDED" name="properties" level="project" />
|
||||
<orderEntry type="module" module-name="idea" scope="RUNTIME" />
|
||||
<orderEntry type="module" module-name="formatter" />
|
||||
</component>
|
||||
</module>
|
||||
+143
@@ -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<FqName> = emptyList(),
|
||||
copyExpression: Boolean = true
|
||||
): ReplacementExpression {
|
||||
@Suppress("NAME_SHADOWING")
|
||||
var expression = if (copyExpression) expression.copied() else expression
|
||||
|
||||
var bindingContext = analyzeInContext(expression, resolutionScope)
|
||||
|
||||
val typeArgsToAdd = ArrayList<Pair<KtCallExpression, KtTypeArgumentList>>()
|
||||
expression.forEachDescendantOfType<KtCallExpression> {
|
||||
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<Pair<KtExpression, KtExpression>>()
|
||||
val resultImportFqNames = importFqNames.toMutableSet()
|
||||
|
||||
val psiFactory = KtPsiFactory(expression)
|
||||
|
||||
expression.forEachDescendantOfType<KtSimpleNameExpression> { 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
|
||||
}
|
||||
}
|
||||
+47
@@ -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<FqName>
|
||||
) {
|
||||
fun copy() = ReplacementExpression(expression.copied(), fqNamesToImport)
|
||||
|
||||
companion object {
|
||||
val PARAMETER_USAGE_KEY: Key<Name> = Key("PARAMETER_USAGE")
|
||||
val TYPE_PARAMETER_USAGE_KEY: Key<Name> = Key("TYPE_PARAMETER_USAGE")
|
||||
}
|
||||
}
|
||||
@@ -55,5 +55,6 @@
|
||||
<orderEntry type="module" module-name="formatter" />
|
||||
<orderEntry type="module" module-name="idea-maven" />
|
||||
<orderEntry type="library" scope="TEST" name="native-platform-uberjar" level="project" />
|
||||
<orderEntry type="module" module-name="idea-replacement-engine" />
|
||||
</component>
|
||||
</module>
|
||||
@@ -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
|
||||
}
|
||||
|
||||
+9
-8
@@ -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<out CallableDescriptor>,
|
||||
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<KtExpression> {
|
||||
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<KtExpression> {
|
||||
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<KtExpression> { it.clear(ReplaceWithAnnotationAnalyzer.PARAMETER_USAGE_KEY) }
|
||||
expression.forEachDescendantOfType<KtExpression> { 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<KtExpression> {
|
||||
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)
|
||||
|
||||
+2
-1
@@ -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 {
|
||||
|
||||
|
||||
+8
-105
@@ -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<String>)
|
||||
|
||||
object ReplaceWithAnnotationAnalyzer {
|
||||
val PARAMETER_USAGE_KEY: Key<Name> = Key("PARAMETER_USAGE")
|
||||
val TYPE_PARAMETER_USAGE_KEY: Key<Name> = Key("TYPE_PARAMETER_USAGE")
|
||||
|
||||
data class ReplacementExpression(
|
||||
val expression: KtExpression,
|
||||
val fqNamesToImport: Collection<FqName>
|
||||
) {
|
||||
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<Pair<KtCallExpression, KtTypeArgumentList>>()
|
||||
expression.forEachDescendantOfType<KtCallExpression> {
|
||||
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<Pair<KtExpression, KtExpression>>()
|
||||
val importFqNames = importFqNames(annotation).toMutableSet()
|
||||
|
||||
expression.forEachDescendantOfType<KtSimpleNameExpression> { 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<ImportingScope>): LexicalScope? {
|
||||
return when (descriptor) {
|
||||
is PackageFragmentDescriptor -> {
|
||||
|
||||
Reference in New Issue
Block a user