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-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-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-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-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$/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" />
|
<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");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* you may not use this file except in compliance with 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="formatter" />
|
||||||
<orderEntry type="module" module-name="idea-maven" />
|
<orderEntry type="module" module-name="idea-maven" />
|
||||||
<orderEntry type="library" scope="TEST" name="native-platform-uberjar" level="project" />
|
<orderEntry type="library" scope="TEST" name="native-platform-uberjar" level="project" />
|
||||||
|
<orderEntry type="module" module-name="idea-replacement-engine" />
|
||||||
</component>
|
</component>
|
||||||
</module>
|
</module>
|
||||||
@@ -17,13 +17,14 @@
|
|||||||
package org.jetbrains.kotlin.idea.quickfix.replaceWith
|
package org.jetbrains.kotlin.idea.quickfix.replaceWith
|
||||||
|
|
||||||
import org.jetbrains.kotlin.idea.core.replaced
|
import org.jetbrains.kotlin.idea.core.replaced
|
||||||
|
import org.jetbrains.kotlin.idea.replacement.ReplacementExpression
|
||||||
import org.jetbrains.kotlin.psi.*
|
import org.jetbrains.kotlin.psi.*
|
||||||
import org.jetbrains.kotlin.psi.psiUtil.getQualifiedExpressionForSelectorOrThis
|
import org.jetbrains.kotlin.psi.psiUtil.getQualifiedExpressionForSelectorOrThis
|
||||||
|
|
||||||
internal interface CallKindHandler {
|
internal interface CallKindHandler {
|
||||||
val elementToReplace: KtElement
|
val elementToReplace: KtElement
|
||||||
|
|
||||||
fun precheckReplacementPattern(pattern: ReplaceWithAnnotationAnalyzer.ReplacementExpression): Boolean
|
fun precheckReplacementPattern(pattern: ReplacementExpression): Boolean
|
||||||
|
|
||||||
fun wrapGeneratedExpression(expression: KtExpression): KtElement
|
fun wrapGeneratedExpression(expression: KtExpression): KtElement
|
||||||
|
|
||||||
@@ -33,7 +34,7 @@ internal interface CallKindHandler {
|
|||||||
internal class CallExpressionHandler(callElement: KtExpression) : CallKindHandler {
|
internal class CallExpressionHandler(callElement: KtExpression) : CallKindHandler {
|
||||||
override val elementToReplace = callElement.getQualifiedExpressionForSelectorOrThis()
|
override val elementToReplace = callElement.getQualifiedExpressionForSelectorOrThis()
|
||||||
|
|
||||||
override fun precheckReplacementPattern(pattern: ReplaceWithAnnotationAnalyzer.ReplacementExpression) = true
|
override fun precheckReplacementPattern(pattern: ReplacementExpression) = true
|
||||||
|
|
||||||
override fun wrapGeneratedExpression(expression: KtExpression) = expression
|
override fun wrapGeneratedExpression(expression: KtExpression) = expression
|
||||||
|
|
||||||
@@ -43,7 +44,7 @@ internal class CallExpressionHandler(callElement: KtExpression) : CallKindHandle
|
|||||||
internal class AnnotationEntryHandler(annotationEntry: KtAnnotationEntry) : CallKindHandler {
|
internal class AnnotationEntryHandler(annotationEntry: KtAnnotationEntry) : CallKindHandler {
|
||||||
override val elementToReplace = annotationEntry
|
override val elementToReplace = annotationEntry
|
||||||
|
|
||||||
override fun precheckReplacementPattern(pattern: ReplaceWithAnnotationAnalyzer.ReplacementExpression): Boolean {
|
override fun precheckReplacementPattern(pattern: ReplacementExpression): Boolean {
|
||||||
//TODO
|
//TODO
|
||||||
return true
|
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.OperatorToFunctionIntention
|
||||||
import org.jetbrains.kotlin.idea.intentions.RemoveExplicitTypeArgumentsIntention
|
import org.jetbrains.kotlin.idea.intentions.RemoveExplicitTypeArgumentsIntention
|
||||||
import org.jetbrains.kotlin.idea.intentions.setType
|
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.CommentSaver
|
||||||
import org.jetbrains.kotlin.idea.util.IdeDescriptorRenderers
|
import org.jetbrains.kotlin.idea.util.IdeDescriptorRenderers
|
||||||
import org.jetbrains.kotlin.idea.util.ImportInsertHelper
|
import org.jetbrains.kotlin.idea.util.ImportInsertHelper
|
||||||
@@ -55,7 +56,7 @@ import org.jetbrains.kotlin.utils.addIfNotNull
|
|||||||
import java.util.*
|
import java.util.*
|
||||||
|
|
||||||
class CallableUsageReplacementStrategy(
|
class CallableUsageReplacementStrategy(
|
||||||
private val replacement: ReplaceWithAnnotationAnalyzer.ReplacementExpression
|
private val replacement: ReplacementExpression
|
||||||
) : UsageReplacementStrategy {
|
) : UsageReplacementStrategy {
|
||||||
|
|
||||||
override fun createReplacer(usage: KtSimpleNameExpression): (() -> KtElement)? {
|
override fun createReplacer(usage: KtSimpleNameExpression): (() -> KtElement)? {
|
||||||
@@ -95,7 +96,7 @@ private fun performCallReplacement(
|
|||||||
resolvedCall: ResolvedCall<out CallableDescriptor>,
|
resolvedCall: ResolvedCall<out CallableDescriptor>,
|
||||||
callElement: KtElement,
|
callElement: KtElement,
|
||||||
callKindHandler: CallKindHandler,
|
callKindHandler: CallKindHandler,
|
||||||
replacement: ReplaceWithAnnotationAnalyzer.ReplacementExpression
|
replacement: ReplacementExpression
|
||||||
): KtElement {
|
): KtElement {
|
||||||
val project = element.project
|
val project = element.project
|
||||||
val psiFactory = KtPsiFactory(project)
|
val psiFactory = KtPsiFactory(project)
|
||||||
@@ -196,7 +197,7 @@ private fun ConstructedExpressionWrapper.processValueParameterUsages(
|
|||||||
|
|
||||||
val parameterName = parameter.name
|
val parameterName = parameter.name
|
||||||
val usages = expression.collectDescendantsOfType<KtExpression> {
|
val usages = expression.collectDescendantsOfType<KtExpression> {
|
||||||
it[ReplaceWithAnnotationAnalyzer.PARAMETER_USAGE_KEY] == parameterName
|
it[ReplacementExpression.PARAMETER_USAGE_KEY] == parameterName
|
||||||
}
|
}
|
||||||
usages.forEach {
|
usages.forEach {
|
||||||
val usageArgument = it.parent as? KtValueArgument
|
val usageArgument = it.parent as? KtValueArgument
|
||||||
@@ -235,7 +236,7 @@ private fun ConstructedExpressionWrapper.processTypeParameterUsages(resolvedCall
|
|||||||
for ((index, typeParameter) in typeParameters.withIndex()) {
|
for ((index, typeParameter) in typeParameters.withIndex()) {
|
||||||
val parameterName = typeParameter.name
|
val parameterName = typeParameter.name
|
||||||
val usages = expression.collectDescendantsOfType<KtExpression> {
|
val usages = expression.collectDescendantsOfType<KtExpression> {
|
||||||
it[ReplaceWithAnnotationAnalyzer.TYPE_PARAMETER_USAGE_KEY] == parameterName
|
it[ReplacementExpression.TYPE_PARAMETER_USAGE_KEY] == parameterName
|
||||||
}
|
}
|
||||||
|
|
||||||
val factory = KtPsiFactory(callElement)
|
val factory = KtPsiFactory(callElement)
|
||||||
@@ -353,13 +354,13 @@ private fun argumentForParameter(
|
|||||||
val (expression, parameterUsages) = defaultValue
|
val (expression, parameterUsages) = defaultValue
|
||||||
|
|
||||||
for ((param, usages) in parameterUsages) {
|
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()
|
val expressionCopy = expression.copied()
|
||||||
|
|
||||||
// clean up user data in original
|
// 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)
|
return Argument(expressionCopy, null/*TODO*/, isDefaultValue = true)
|
||||||
}
|
}
|
||||||
@@ -430,8 +431,8 @@ private fun postProcessInsertedCode(range: PsiChildRange): PsiChildRange {
|
|||||||
// clean up user data
|
// clean up user data
|
||||||
it.forEachDescendantOfType<KtExpression> {
|
it.forEachDescendantOfType<KtExpression> {
|
||||||
it.clear(USER_CODE_KEY)
|
it.clear(USER_CODE_KEY)
|
||||||
it.clear(ReplaceWithAnnotationAnalyzer.PARAMETER_USAGE_KEY)
|
it.clear(ReplacementExpression.PARAMETER_USAGE_KEY)
|
||||||
it.clear(ReplaceWithAnnotationAnalyzer.TYPE_PARAMETER_USAGE_KEY)
|
it.clear(ReplacementExpression.TYPE_PARAMETER_USAGE_KEY)
|
||||||
it.clear(PARAMETER_VALUE_KEY)
|
it.clear(PARAMETER_VALUE_KEY)
|
||||||
it.clear(RECEIVER_VALUE_KEY)
|
it.clear(RECEIVER_VALUE_KEY)
|
||||||
it.clear(WAS_FUNCTION_LITERAL_ARGUMENT_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 com.intellij.openapi.project.Project
|
||||||
import org.jetbrains.kotlin.idea.core.ShortenReferences
|
import org.jetbrains.kotlin.idea.core.ShortenReferences
|
||||||
import org.jetbrains.kotlin.idea.core.replaced
|
import org.jetbrains.kotlin.idea.core.replaced
|
||||||
|
import org.jetbrains.kotlin.idea.replacement.ReplacementExpression
|
||||||
import org.jetbrains.kotlin.psi.*
|
import org.jetbrains.kotlin.psi.*
|
||||||
import org.jetbrains.kotlin.psi.psiUtil.getQualifiedExpressionForSelectorOrThis
|
import org.jetbrains.kotlin.psi.psiUtil.getQualifiedExpressionForSelectorOrThis
|
||||||
import org.jetbrains.kotlin.utils.addToStdlib.check
|
import org.jetbrains.kotlin.utils.addToStdlib.check
|
||||||
|
|
||||||
class ClassUsageReplacementStrategy(
|
class ClassUsageReplacementStrategy(
|
||||||
typeReplacement: KtUserType?,
|
typeReplacement: KtUserType?,
|
||||||
constructorReplacement: ReplaceWithAnnotationAnalyzer.ReplacementExpression?,
|
constructorReplacement: ReplacementExpression?,
|
||||||
project: Project
|
project: Project
|
||||||
) : UsageReplacementStrategy {
|
) : UsageReplacementStrategy {
|
||||||
|
|
||||||
|
|||||||
+8
-105
@@ -16,57 +16,34 @@
|
|||||||
|
|
||||||
package org.jetbrains.kotlin.idea.quickfix.replaceWith
|
package org.jetbrains.kotlin.idea.quickfix.replaceWith
|
||||||
|
|
||||||
import com.intellij.openapi.util.Key
|
|
||||||
import org.jetbrains.kotlin.descriptors.*
|
import org.jetbrains.kotlin.descriptors.*
|
||||||
import org.jetbrains.kotlin.idea.caches.resolve.resolveImportReference
|
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.KtSimpleNameReference
|
||||||
import org.jetbrains.kotlin.idea.references.canBeResolvedViaImport
|
|
||||||
import org.jetbrains.kotlin.idea.references.mainReference
|
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.ResolutionFacade
|
||||||
import org.jetbrains.kotlin.idea.resolve.frontendService
|
import org.jetbrains.kotlin.idea.resolve.frontendService
|
||||||
import org.jetbrains.kotlin.name.FqName
|
import org.jetbrains.kotlin.name.FqName
|
||||||
import org.jetbrains.kotlin.name.FqNameUnsafe
|
import org.jetbrains.kotlin.name.FqNameUnsafe
|
||||||
import org.jetbrains.kotlin.name.Name
|
import org.jetbrains.kotlin.psi.KtNameReferenceExpression
|
||||||
import org.jetbrains.kotlin.psi.*
|
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.forEachDescendantOfType
|
||||||
import org.jetbrains.kotlin.psi.psiUtil.getReceiverExpression
|
|
||||||
import org.jetbrains.kotlin.resolve.*
|
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.fqNameUnsafe
|
||||||
import org.jetbrains.kotlin.resolve.descriptorUtil.isExtension
|
|
||||||
import org.jetbrains.kotlin.resolve.descriptorUtil.module
|
import org.jetbrains.kotlin.resolve.descriptorUtil.module
|
||||||
import org.jetbrains.kotlin.resolve.lazy.DefaultImportProvider
|
import org.jetbrains.kotlin.resolve.lazy.DefaultImportProvider
|
||||||
import org.jetbrains.kotlin.resolve.lazy.descriptors.ClassResolutionScopesSupport
|
import org.jetbrains.kotlin.resolve.lazy.descriptors.ClassResolutionScopesSupport
|
||||||
import org.jetbrains.kotlin.resolve.scopes.*
|
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.chainImportingScopes
|
||||||
import org.jetbrains.kotlin.resolve.scopes.utils.memberScopeAsImportingScope
|
import org.jetbrains.kotlin.resolve.scopes.utils.memberScopeAsImportingScope
|
||||||
import org.jetbrains.kotlin.storage.LockBasedStorageManager
|
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.*
|
import java.util.*
|
||||||
|
|
||||||
data class ReplaceWith(val pattern: String, val imports: List<String>)
|
data class ReplaceWith(val pattern: String, val imports: List<String>)
|
||||||
|
|
||||||
object ReplaceWithAnnotationAnalyzer {
|
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(
|
fun analyzeCallableReplacement(
|
||||||
annotation: ReplaceWith,
|
annotation: ReplaceWith,
|
||||||
symbolDescriptor: CallableDescriptor,
|
symbolDescriptor: CallableDescriptor,
|
||||||
@@ -85,7 +62,7 @@ object ReplaceWithAnnotationAnalyzer {
|
|||||||
resolutionFacade: ResolutionFacade
|
resolutionFacade: ResolutionFacade
|
||||||
): ReplacementExpression? {
|
): ReplacementExpression? {
|
||||||
val psiFactory = KtPsiFactory(resolutionFacade.project)
|
val psiFactory = KtPsiFactory(resolutionFacade.project)
|
||||||
var expression = try {
|
val expression = try {
|
||||||
psiFactory.createExpression(annotation.pattern)
|
psiFactory.createExpression(annotation.pattern)
|
||||||
}
|
}
|
||||||
catch(t: Throwable) {
|
catch(t: Throwable) {
|
||||||
@@ -98,69 +75,8 @@ object ReplaceWithAnnotationAnalyzer {
|
|||||||
val scope = getResolutionScope(symbolDescriptor, symbolDescriptor,
|
val scope = getResolutionScope(symbolDescriptor, symbolDescriptor,
|
||||||
listOf(explicitImportsScope) + defaultImportsScopes) ?: return null
|
listOf(explicitImportsScope) + defaultImportsScopes) ?: return null
|
||||||
|
|
||||||
var bindingContext = analyzeInContext(expression, module, scope, resolutionFacade)
|
return ReplacementBuilder(symbolDescriptor, resolutionFacade)
|
||||||
|
.buildReplacementExpression(expression, scope, importFqNames(annotation), copyExpression = false)
|
||||||
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)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fun analyzeClassReplacement(
|
fun analyzeClassReplacement(
|
||||||
@@ -232,19 +148,6 @@ object ReplaceWithAnnotationAnalyzer {
|
|||||||
.map(FqNameUnsafe::toSafe)
|
.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? {
|
private fun getResolutionScope(descriptor: DeclarationDescriptor, ownerDescriptor: DeclarationDescriptor, additionalScopes: Collection<ImportingScope>): LexicalScope? {
|
||||||
return when (descriptor) {
|
return when (descriptor) {
|
||||||
is PackageFragmentDescriptor -> {
|
is PackageFragmentDescriptor -> {
|
||||||
|
|||||||
Reference in New Issue
Block a user