This commit is contained in:
Valentin Kipyatkov
2016-10-25 13:12:33 +03:00
parent 4c4c85e37a
commit e5748f7a86
17 changed files with 105 additions and 105 deletions
+1 -1
View File
@@ -44,13 +44,13 @@
<module fileurl="file://$PROJECT_DIR$/idea/idea-analysis/idea-analysis.iml" filepath="$PROJECT_DIR$/idea/idea-analysis/idea-analysis.iml" group="ide" /> <module fileurl="file://$PROJECT_DIR$/idea/idea-analysis/idea-analysis.iml" filepath="$PROJECT_DIR$/idea/idea-analysis/idea-analysis.iml" group="ide" />
<module fileurl="file://$PROJECT_DIR$/idea/idea-android/idea-android.iml" filepath="$PROJECT_DIR$/idea/idea-android/idea-android.iml" group="ide" /> <module fileurl="file://$PROJECT_DIR$/idea/idea-android/idea-android.iml" filepath="$PROJECT_DIR$/idea/idea-android/idea-android.iml" group="ide" />
<module fileurl="file://$PROJECT_DIR$/idea/idea-android/idea-android-output-parser/idea-android-output-parser.iml" filepath="$PROJECT_DIR$/idea/idea-android/idea-android-output-parser/idea-android-output-parser.iml" group="ide" /> <module fileurl="file://$PROJECT_DIR$/idea/idea-android/idea-android-output-parser/idea-android-output-parser.iml" filepath="$PROJECT_DIR$/idea/idea-android/idea-android-output-parser/idea-android-output-parser.iml" group="ide" />
<module fileurl="file://$PROJECT_DIR$/idea/idea-code-inliner/idea-replacement-engine.iml" filepath="$PROJECT_DIR$/idea/idea-code-inliner/idea-replacement-engine.iml" group="ide" />
<module fileurl="file://$PROJECT_DIR$/idea/idea-completion/idea-completion.iml" filepath="$PROJECT_DIR$/idea/idea-completion/idea-completion.iml" group="ide" /> <module fileurl="file://$PROJECT_DIR$/idea/idea-completion/idea-completion.iml" filepath="$PROJECT_DIR$/idea/idea-completion/idea-completion.iml" group="ide" />
<module fileurl="file://$PROJECT_DIR$/idea/idea-core/idea-core.iml" filepath="$PROJECT_DIR$/idea/idea-core/idea-core.iml" group="ide" /> <module fileurl="file://$PROJECT_DIR$/idea/idea-core/idea-core.iml" filepath="$PROJECT_DIR$/idea/idea-core/idea-core.iml" group="ide" />
<module fileurl="file://$PROJECT_DIR$/idea/idea-jps-common/idea-jps-common.iml" filepath="$PROJECT_DIR$/idea/idea-jps-common/idea-jps-common.iml" group="ide" /> <module fileurl="file://$PROJECT_DIR$/idea/idea-jps-common/idea-jps-common.iml" filepath="$PROJECT_DIR$/idea/idea-jps-common/idea-jps-common.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-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" />
@@ -14,7 +14,7 @@
* limitations under the License. * limitations under the License.
*/ */
package org.jetbrains.kotlin.idea.replacement package org.jetbrains.kotlin.idea.inliner
import org.jetbrains.kotlin.idea.caches.resolve.analyze import org.jetbrains.kotlin.idea.caches.resolve.analyze
import org.jetbrains.kotlin.idea.intentions.OperatorToFunctionIntention import org.jetbrains.kotlin.idea.intentions.OperatorToFunctionIntention
@@ -25,7 +25,7 @@ import org.jetbrains.kotlin.resolve.calls.model.isReallySuccess
import org.jetbrains.kotlin.resolve.lazy.BodyResolveMode import org.jetbrains.kotlin.resolve.lazy.BodyResolveMode
class CallableUsageReplacementStrategy( class CallableUsageReplacementStrategy(
private val replacement: ReplacementCode private val replacement: CodeToInline
) : UsageReplacementStrategy { ) : UsageReplacementStrategy {
override fun createReplacer(usage: KtSimpleNameExpression): (() -> KtElement?)? { override fun createReplacer(usage: KtSimpleNameExpression): (() -> KtElement?)? {
@@ -44,7 +44,7 @@ class CallableUsageReplacementStrategy(
createReplacer(nameExpression)!!.invoke() createReplacer(nameExpression)!!.invoke()
} }
else { else {
CallReplacementEngine(usage, bindingContext, resolvedCall, callElement, replacement).performReplacement() CodeInliner(usage, bindingContext, resolvedCall, callElement, replacement).doInline()
} }
} }
} }
@@ -14,7 +14,7 @@
* limitations under the License. * limitations under the License.
*/ */
package org.jetbrains.kotlin.idea.replacement package org.jetbrains.kotlin.idea.inliner
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
@@ -25,7 +25,7 @@ import org.jetbrains.kotlin.utils.addToStdlib.check
class ClassUsageReplacementStrategy( class ClassUsageReplacementStrategy(
typeReplacement: KtUserType?, typeReplacement: KtUserType?,
constructorReplacement: ReplacementCode?, constructorReplacement: CodeToInline?,
project: Project project: Project
) : UsageReplacementStrategy { ) : UsageReplacementStrategy {
@@ -14,7 +14,7 @@
* limitations under the License. * limitations under the License.
*/ */
package org.jetbrains.kotlin.idea.replacement package org.jetbrains.kotlin.idea.inliner
import com.intellij.openapi.util.Key import com.intellij.openapi.util.Key
import com.intellij.psi.PsiElement import com.intellij.psi.PsiElement
@@ -45,18 +45,18 @@ import org.jetbrains.kotlin.types.KotlinType
import org.jetbrains.kotlin.utils.addIfNotNull import org.jetbrains.kotlin.utils.addIfNotNull
import java.util.* import java.util.*
class CallReplacementEngine<TCallElement : KtElement>( class CodeInliner<TCallElement : KtElement>(
private val nameExpression: KtSimpleNameExpression, private val nameExpression: KtSimpleNameExpression,
private val bindingContext: BindingContext, private val bindingContext: BindingContext,
private val resolvedCall: ResolvedCall<out CallableDescriptor>, private val resolvedCall: ResolvedCall<out CallableDescriptor>,
private val callElement: TCallElement, private val callElement: TCallElement,
replacement: ReplacementCode codeToInline: CodeToInline
) { ) {
private val replacement = replacement.toMutable() private val codeToInline = codeToInline.toMutable()
private val project = nameExpression.project private val project = nameExpression.project
private val psiFactory = KtPsiFactory(project) private val psiFactory = KtPsiFactory(project)
fun performReplacement(): KtElement? { fun doInline(): KtElement? {
val descriptor = resolvedCall.resultingDescriptor val descriptor = resolvedCall.resultingDescriptor
val file = nameExpression.getContainingKtFile() val file = nameExpression.getContainingKtFile()
@@ -82,9 +82,9 @@ class CallReplacementEngine<TCallElement : KtElement>(
receiver?.mark(RECEIVER_VALUE_KEY) receiver?.mark(RECEIVER_VALUE_KEY)
//TODO: this@ //TODO: this@
for (thisExpression in replacement.collectDescendantsOfType<KtThisExpression>()) { for (thisExpression in codeToInline.collectDescendantsOfType<KtThisExpression>()) {
if (receiver != null) { if (receiver != null) {
replacement.replaceExpression(thisExpression, receiver) codeToInline.replaceExpression(thisExpression, receiver)
} }
else { else {
thisExpression.mark(RECEIVER_VALUE_KEY) thisExpression.mark(RECEIVER_VALUE_KEY)
@@ -104,25 +104,25 @@ class CallReplacementEngine<TCallElement : KtElement>(
if (elementToBeReplaced is KtExpression) { if (elementToBeReplaced is KtExpression) {
if (receiver != null) { if (receiver != null) {
val thisReplaced = replacement.collectDescendantsOfType<KtExpression> { it[RECEIVER_VALUE_KEY] } val thisReplaced = codeToInline.collectDescendantsOfType<KtExpression> { it[RECEIVER_VALUE_KEY] }
if (receiver.shouldKeepValue(thisReplaced.size)) { if (receiver.shouldKeepValue(thisReplaced.size)) {
replacement.introduceValue(receiver, receiverType, thisReplaced, elementToBeReplaced) codeToInline.introduceValue(receiver, receiverType, thisReplaced, elementToBeReplaced)
} }
} }
for ((parameter, value, valueType) in introduceValuesForParameters) { for ((parameter, value, valueType) in introduceValuesForParameters) {
val usagesReplaced = replacement.collectDescendantsOfType<KtExpression> { it[PARAMETER_VALUE_KEY] == parameter } val usagesReplaced = codeToInline.collectDescendantsOfType<KtExpression> { it[PARAMETER_VALUE_KEY] == parameter }
replacement.introduceValue(value, valueType, usagesReplaced, elementToBeReplaced, nameSuggestion = parameter.name.asString()) codeToInline.introduceValue(value, valueType, usagesReplaced, elementToBeReplaced, nameSuggestion = parameter.name.asString())
} }
} }
replacement.fqNamesToImport codeToInline.fqNamesToImport
.flatMap { file.resolveImportReference(it) } .flatMap { file.resolveImportReference(it) }
.forEach { ImportInsertHelper.getInstance(project).importDescriptor(file, it) } .forEach { ImportInsertHelper.getInstance(project).importDescriptor(file, it) }
val replacementPerformer = when (elementToBeReplaced) { val replacementPerformer = when (elementToBeReplaced) {
is KtExpression -> ExpressionReplacementPerformer(replacement, elementToBeReplaced) is KtExpression -> ExpressionReplacementPerformer(codeToInline, elementToBeReplaced)
is KtAnnotationEntry -> AnnotationEntryReplacementPerformer(replacement, elementToBeReplaced) is KtAnnotationEntry -> AnnotationEntryReplacementPerformer(codeToInline, elementToBeReplaced)
else -> error("Unsupported element") else -> error("Unsupported element")
} }
@@ -146,8 +146,8 @@ class CallReplacementEngine<TCallElement : KtElement>(
argument.expression.put(PARAMETER_VALUE_KEY, parameter) argument.expression.put(PARAMETER_VALUE_KEY, parameter)
val parameterName = parameter.name val parameterName = parameter.name
val usages = replacement.collectDescendantsOfType<KtExpression> { val usages = codeToInline.collectDescendantsOfType<KtExpression> {
it[ReplacementCode.PARAMETER_USAGE_KEY] == parameterName it[CodeToInline.PARAMETER_USAGE_KEY] == parameterName
} }
usages.forEach { usages.forEach {
val usageArgument = it.parent as? KtValueArgument val usageArgument = it.parent as? KtValueArgument
@@ -157,7 +157,7 @@ class CallReplacementEngine<TCallElement : KtElement>(
if (argument.isDefaultValue) { if (argument.isDefaultValue) {
usageArgument?.mark(DEFAULT_PARAMETER_VALUE_KEY) usageArgument?.mark(DEFAULT_PARAMETER_VALUE_KEY)
} }
replacement.replaceExpression(it, argument.expression) codeToInline.replaceExpression(it, argument.expression)
} }
//TODO: sometimes we need to add explicit type arguments here because we don't have expected type in the new context //TODO: sometimes we need to add explicit type arguments here because we don't have expected type in the new context
@@ -185,8 +185,8 @@ class CallReplacementEngine<TCallElement : KtElement>(
for ((index, typeParameter) in typeParameters.withIndex()) { for ((index, typeParameter) in typeParameters.withIndex()) {
val parameterName = typeParameter.name val parameterName = typeParameter.name
val usages = replacement.collectDescendantsOfType<KtExpression> { val usages = codeToInline.collectDescendantsOfType<KtExpression> {
it[ReplacementCode.TYPE_PARAMETER_USAGE_KEY] == parameterName it[CodeToInline.TYPE_PARAMETER_USAGE_KEY] == parameterName
} }
val type = resolvedCall.typeArguments[typeParameter]!! val type = resolvedCall.typeArguments[typeParameter]!!
@@ -207,7 +207,7 @@ class CallReplacementEngine<TCallElement : KtElement>(
val arguments = val arguments =
if (typeElement is KtUserType && KotlinBuiltIns.isArray(type)) typeElement.typeArgumentList?.text.orEmpty() if (typeElement is KtUserType && KotlinBuiltIns.isArray(type)) typeElement.typeArgumentList?.text.orEmpty()
else "" else ""
replacement.replaceExpression(usage, psiFactory.createExpression( codeToInline.replaceExpression(usage, psiFactory.createExpression(
IdeDescriptorRenderers.SOURCE_CODE.renderClassifierName(typeClassifier) + arguments IdeDescriptorRenderers.SOURCE_CODE.renderClassifierName(typeClassifier) + arguments
)) ))
} }
@@ -216,21 +216,21 @@ class CallReplacementEngine<TCallElement : KtElement>(
} }
else { else {
//TODO: tests for this? //TODO: tests for this?
replacement.replaceExpression(usage, psiFactory.createExpression(typeElement.text)) codeToInline.replaceExpression(usage, psiFactory.createExpression(typeElement.text))
} }
} }
} }
} }
private fun wrapCodeForSafeCall(receiver: KtExpression, receiverType: KotlinType?, expressionToBeReplaced: KtExpression) { private fun wrapCodeForSafeCall(receiver: KtExpression, receiverType: KotlinType?, expressionToBeReplaced: KtExpression) {
if (replacement.statementsBefore.isEmpty()) { if (codeToInline.statementsBefore.isEmpty()) {
val qualified = replacement.mainExpression as? KtQualifiedExpression val qualified = codeToInline.mainExpression as? KtQualifiedExpression
if (qualified != null) { if (qualified != null) {
if (qualified.receiverExpression[RECEIVER_VALUE_KEY]) { if (qualified.receiverExpression[RECEIVER_VALUE_KEY]) {
if (qualified is KtSafeQualifiedExpression) return // already safe if (qualified is KtSafeQualifiedExpression) return // already safe
val selector = qualified.selectorExpression val selector = qualified.selectorExpression
if (selector != null) { if (selector != null) {
replacement.mainExpression = psiFactory.createExpressionByPattern("$0?.$1", receiver, selector) codeToInline.mainExpression = psiFactory.createExpressionByPattern("$0?.$1", receiver, selector)
return return
} }
} }
@@ -238,32 +238,32 @@ class CallReplacementEngine<TCallElement : KtElement>(
} }
if (expressionToBeReplaced.isUsedAsExpression(bindingContext)) { if (expressionToBeReplaced.isUsedAsExpression(bindingContext)) {
val thisReplaced = replacement.collectDescendantsOfType<KtExpression> { it[RECEIVER_VALUE_KEY] } val thisReplaced = codeToInline.collectDescendantsOfType<KtExpression> { it[RECEIVER_VALUE_KEY] }
replacement.introduceValue(receiver, receiverType, thisReplaced, expressionToBeReplaced, safeCall = true) codeToInline.introduceValue(receiver, receiverType, thisReplaced, expressionToBeReplaced, safeCall = true)
} }
else { else {
val ifExpression = psiFactory.buildExpression { val ifExpression = psiFactory.buildExpression {
appendFixedText("if (") appendFixedText("if (")
appendExpression(receiver) appendExpression(receiver)
appendFixedText("!=null) {") appendFixedText("!=null) {")
replacement.statementsBefore.forEach { codeToInline.statementsBefore.forEach {
appendExpression(it) appendExpression(it)
appendFixedText("\n") appendFixedText("\n")
} }
replacement.mainExpression?.let { codeToInline.mainExpression?.let {
appendExpression(it) appendExpression(it)
appendFixedText("\n") appendFixedText("\n")
} }
appendFixedText("}") appendFixedText("}")
} }
replacement.mainExpression = ifExpression codeToInline.mainExpression = ifExpression
replacement.statementsBefore.clear() codeToInline.statementsBefore.clear()
} }
} }
private fun keepInfixFormIfPossible() { private fun keepInfixFormIfPossible() {
if (replacement.statementsBefore.isNotEmpty()) return if (codeToInline.statementsBefore.isNotEmpty()) return
val dotQualified = replacement.mainExpression as? KtDotQualifiedExpression ?: return val dotQualified = codeToInline.mainExpression as? KtDotQualifiedExpression ?: return
val receiver = dotQualified.receiverExpression val receiver = dotQualified.receiverExpression
if (!receiver[RECEIVER_VALUE_KEY]) return if (!receiver[RECEIVER_VALUE_KEY]) return
val call = dotQualified.selectorExpression as? KtCallExpression ?: return val call = dotQualified.selectorExpression as? KtCallExpression ?: return
@@ -271,7 +271,7 @@ class CallReplacementEngine<TCallElement : KtElement>(
val argument = call.valueArguments.singleOrNull() ?: return val argument = call.valueArguments.singleOrNull() ?: return
if (argument.getArgumentName() != null) return if (argument.getArgumentName() != null) return
val argumentExpression = argument.getArgumentExpression() ?: return val argumentExpression = argument.getArgumentExpression() ?: return
replacement.mainExpression = psiFactory.createExpressionByPattern("$0 ${nameExpression.text} $1", receiver, argumentExpression) codeToInline.mainExpression = psiFactory.createExpressionByPattern("$0 ${nameExpression.text} $1", receiver, argumentExpression)
} }
private fun KtExpression?.shouldKeepValue(usageCount: Int): Boolean { private fun KtExpression?.shouldKeepValue(usageCount: Int): Boolean {
@@ -317,13 +317,13 @@ class CallReplacementEngine<TCallElement : KtElement>(
val (expression, parameterUsages) = defaultValue val (expression, parameterUsages) = defaultValue
for ((param, usages) in parameterUsages) { for ((param, usages) in parameterUsages) {
usages.forEach { it.put(ReplacementCode.PARAMETER_USAGE_KEY, param.name) } usages.forEach { it.put(CodeToInline.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(ReplacementCode.PARAMETER_USAGE_KEY) } expression.forEachDescendantOfType<KtExpression> { it.clear(CodeToInline.PARAMETER_USAGE_KEY) }
return Argument(expressionCopy, null/*TODO*/, isDefaultValue = true) return Argument(expressionCopy, null/*TODO*/, isDefaultValue = true)
} }
@@ -395,8 +395,8 @@ class CallReplacementEngine<TCallElement : KtElement>(
// 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(ReplacementCode.PARAMETER_USAGE_KEY) it.clear(CodeToInline.PARAMETER_USAGE_KEY)
it.clear(ReplacementCode.TYPE_PARAMETER_USAGE_KEY) it.clear(CodeToInline.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)
@@ -14,25 +14,25 @@
* limitations under the License. * limitations under the License.
*/ */
package org.jetbrains.kotlin.idea.replacement package org.jetbrains.kotlin.idea.inliner
import com.intellij.openapi.util.Key import com.intellij.openapi.util.Key
import org.jetbrains.kotlin.idea.replacement.ReplacementCode.Companion.PARAMETER_USAGE_KEY import org.jetbrains.kotlin.idea.inliner.CodeToInline.Companion.PARAMETER_USAGE_KEY
import org.jetbrains.kotlin.idea.replacement.ReplacementCode.Companion.TYPE_PARAMETER_USAGE_KEY import org.jetbrains.kotlin.idea.inliner.CodeToInline.Companion.TYPE_PARAMETER_USAGE_KEY
import org.jetbrains.kotlin.name.FqName import org.jetbrains.kotlin.name.FqName
import org.jetbrains.kotlin.name.Name import org.jetbrains.kotlin.name.Name
import org.jetbrains.kotlin.psi.KtExpression import org.jetbrains.kotlin.psi.KtExpression
/** /**
* Represents an expression to replace usages of particular callable. * Represents code to be inlined to replace usages of particular callable.
* The expression should be preprocessed in the following way: * The expression should be preprocessed in the following way:
* * Type arguments for all calls should be made explicit * * 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 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 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) * * 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.buildReplacementCode]. * Use [CodeToInlineBuilder.prepareCodeToInline].
*/ */
class ReplacementCode( class CodeToInline(
val mainExpression: KtExpression?, val mainExpression: KtExpression?,
val statementsBefore: List<KtExpression>, val statementsBefore: List<KtExpression>,
val fqNamesToImport: Collection<FqName> val fqNamesToImport: Collection<FqName>
@@ -14,7 +14,7 @@
* limitations under the License. * limitations under the License.
*/ */
package org.jetbrains.kotlin.idea.replacement package org.jetbrains.kotlin.idea.inliner
import org.jetbrains.kotlin.descriptors.CallableDescriptor import org.jetbrains.kotlin.descriptors.CallableDescriptor
import org.jetbrains.kotlin.descriptors.TypeParameterDescriptor import org.jetbrains.kotlin.descriptors.TypeParameterDescriptor
@@ -36,21 +36,21 @@ import org.jetbrains.kotlin.resolve.descriptorUtil.isExtension
import org.jetbrains.kotlin.resolve.scopes.receivers.ImplicitReceiver import org.jetbrains.kotlin.resolve.scopes.receivers.ImplicitReceiver
import java.util.* import java.util.*
class ReplacementBuilder( class CodeToInlineBuilder(
private val targetCallable: CallableDescriptor, private val targetCallable: CallableDescriptor,
private val resolutionFacade: ResolutionFacade private val resolutionFacade: ResolutionFacade
) { ) {
private val psiFactory = KtPsiFactory(resolutionFacade.project) private val psiFactory = KtPsiFactory(resolutionFacade.project)
fun buildReplacementCode( fun prepareCodeToInline(
mainExpression: KtExpression?, mainExpression: KtExpression?,
statementsBefore: List<KtExpression>, statementsBefore: List<KtExpression>,
analyze: () -> BindingContext, analyze: () -> BindingContext,
importFqNames: Collection<FqName> = emptyList() importFqNames: Collection<FqName> = emptyList()
): ReplacementCode { ): CodeToInline {
var bindingContext = analyze() var bindingContext = analyze()
val result = MutableReplacementCode(mainExpression, statementsBefore.toMutableList(), importFqNames.toMutableSet()) val result = MutableCodeToInline(mainExpression, statementsBefore.toMutableList(), importFqNames.toMutableSet())
val typeArgsToAdd = ArrayList<Pair<KtCallExpression, KtTypeArgumentList>>() val typeArgsToAdd = ArrayList<Pair<KtCallExpression, KtTypeArgumentList>>()
result.forEachDescendantOfType<KtCallExpression> { result.forEachDescendantOfType<KtCallExpression> {
@@ -80,10 +80,10 @@ class ReplacementBuilder(
if (expression.getReceiverExpression() == null) { if (expression.getReceiverExpression() == null) {
if (target is ValueParameterDescriptor && target.containingDeclaration == targetCallable) { if (target is ValueParameterDescriptor && target.containingDeclaration == targetCallable) {
expression.putCopyableUserData(ReplacementCode.PARAMETER_USAGE_KEY, target.name) expression.putCopyableUserData(CodeToInline.PARAMETER_USAGE_KEY, target.name)
} }
else if (target is TypeParameterDescriptor && target.containingDeclaration == targetCallable) { else if (target is TypeParameterDescriptor && target.containingDeclaration == targetCallable) {
expression.putCopyableUserData(ReplacementCode.TYPE_PARAMETER_USAGE_KEY, target.name) expression.putCopyableUserData(CodeToInline.TYPE_PARAMETER_USAGE_KEY, target.name)
} }
val resolvedCall = expression.getResolvedCall(bindingContext) val resolvedCall = expression.getResolvedCall(bindingContext)
@@ -14,7 +14,7 @@
* limitations under the License. * limitations under the License.
*/ */
package org.jetbrains.kotlin.idea.replacement package org.jetbrains.kotlin.idea.inliner
import com.intellij.openapi.util.Key import com.intellij.openapi.util.Key
import com.intellij.psi.PsiElement import com.intellij.psi.PsiElement
@@ -29,7 +29,7 @@ import org.jetbrains.kotlin.utils.addToStdlib.singletonOrEmptyList
private val POST_INSERTION_ACTION: Key<(KtElement) -> Unit> = Key("POST_INSERTION_ACTION") private val POST_INSERTION_ACTION: Key<(KtElement) -> Unit> = Key("POST_INSERTION_ACTION")
internal class MutableReplacementCode( internal class MutableCodeToInline(
var mainExpression: KtExpression?, var mainExpression: KtExpression?,
val statementsBefore: MutableList<KtExpression>, val statementsBefore: MutableList<KtExpression>,
val fqNamesToImport: MutableCollection<FqName> val fqNamesToImport: MutableCollection<FqName>
@@ -81,22 +81,22 @@ internal class MutableReplacementCode(
} }
} }
internal fun ReplacementCode.toMutable(): MutableReplacementCode { internal fun CodeToInline.toMutable(): MutableCodeToInline {
return MutableReplacementCode( return MutableCodeToInline(
mainExpression?.copied(), mainExpression?.copied(),
statementsBefore.map { it.copied() }.toMutableList(), statementsBefore.map { it.copied() }.toMutableList(),
fqNamesToImport.toMutableSet()) fqNamesToImport.toMutableSet())
} }
internal fun MutableReplacementCode.toNonMutable(): ReplacementCode { internal fun MutableCodeToInline.toNonMutable(): CodeToInline {
return ReplacementCode(mainExpression, statementsBefore, fqNamesToImport) return CodeToInline(mainExpression, statementsBefore, fqNamesToImport)
} }
internal inline fun <reified T : PsiElement> MutableReplacementCode.collectDescendantsOfType(noinline predicate: (T) -> Boolean = { true }): List<T> { internal inline fun <reified T : PsiElement> MutableCodeToInline.collectDescendantsOfType(noinline predicate: (T) -> Boolean = { true }): List<T> {
return expressions.flatMap { it.collectDescendantsOfType<T>({ true }, predicate) } return expressions.flatMap { it.collectDescendantsOfType<T>({ true }, predicate) }
} }
internal inline fun <reified T : PsiElement> MutableReplacementCode.forEachDescendantOfType(noinline action: (T) -> Unit) { internal inline fun <reified T : PsiElement> MutableCodeToInline.forEachDescendantOfType(noinline action: (T) -> Unit) {
expressions.forEach { it.forEachDescendantOfType<T>(action) } expressions.forEach { it.forEachDescendantOfType<T>(action) }
} }
@@ -14,7 +14,7 @@
* limitations under the License. * limitations under the License.
*/ */
package org.jetbrains.kotlin.idea.replacement package org.jetbrains.kotlin.idea.inliner
import com.intellij.openapi.util.Key import com.intellij.openapi.util.Key
import org.jetbrains.kotlin.idea.caches.resolve.analyze import org.jetbrains.kotlin.idea.caches.resolve.analyze
@@ -31,7 +31,7 @@ import org.jetbrains.kotlin.utils.addToStdlib.singletonOrEmptyList
import java.util.* import java.util.*
internal abstract class ReplacementPerformer<TElement : KtElement>( internal abstract class ReplacementPerformer<TElement : KtElement>(
protected val replacement: MutableReplacementCode, protected val codeToInline: MutableCodeToInline,
protected var elementToBeReplaced: TElement protected var elementToBeReplaced: TElement
) { ) {
protected val psiFactory = KtPsiFactory(elementToBeReplaced) protected val psiFactory = KtPsiFactory(elementToBeReplaced)
@@ -40,18 +40,18 @@ internal abstract class ReplacementPerformer<TElement : KtElement>(
} }
internal class AnnotationEntryReplacementPerformer( internal class AnnotationEntryReplacementPerformer(
replacement: MutableReplacementCode, codeToInline: MutableCodeToInline,
elementToBeReplaced: KtAnnotationEntry elementToBeReplaced: KtAnnotationEntry
) : ReplacementPerformer<KtAnnotationEntry>(replacement, elementToBeReplaced) { ) : ReplacementPerformer<KtAnnotationEntry>(codeToInline, elementToBeReplaced) {
override fun doIt(postProcessing: (PsiChildRange) -> PsiChildRange): KtAnnotationEntry { override fun doIt(postProcessing: (PsiChildRange) -> PsiChildRange): KtAnnotationEntry {
assert(replacement.mainExpression != null) assert(codeToInline.mainExpression != null)
assert(replacement.statementsBefore.isEmpty()) assert(codeToInline.statementsBefore.isEmpty())
val dummyAnnotationEntry = createByPattern("@Dummy($0)", replacement.mainExpression!!) { psiFactory.createAnnotationEntry(it) } val dummyAnnotationEntry = createByPattern("@Dummy($0)", codeToInline.mainExpression!!) { psiFactory.createAnnotationEntry(it) }
val replaced = elementToBeReplaced.replace(dummyAnnotationEntry) val replaced = elementToBeReplaced.replace(dummyAnnotationEntry)
replacement.performPostInsertionActions(replaced.singletonList()) codeToInline.performPostInsertionActions(replaced.singletonList())
var range = PsiChildRange.singleElement(replaced) var range = PsiChildRange.singleElement(replaced)
range = postProcessing(range) range = postProcessing(range)
@@ -65,13 +65,13 @@ internal class AnnotationEntryReplacementPerformer(
} }
internal class ExpressionReplacementPerformer( internal class ExpressionReplacementPerformer(
replacement: MutableReplacementCode, codeToInline: MutableCodeToInline,
expressionToBeReplaced: KtExpression expressionToBeReplaced: KtExpression
) : ReplacementPerformer<KtExpression>(replacement, expressionToBeReplaced) { ) : ReplacementPerformer<KtExpression>(codeToInline, expressionToBeReplaced) {
override fun doIt(postProcessing: (PsiChildRange) -> PsiChildRange): KtExpression? { override fun doIt(postProcessing: (PsiChildRange) -> PsiChildRange): KtExpression? {
val insertedStatements = ArrayList<KtExpression>() val insertedStatements = ArrayList<KtExpression>()
for (statement in replacement.statementsBefore) { for (statement in codeToInline.statementsBefore) {
// copy the statement if it can get invalidated by findOrCreateBlockToInsertStatement() // copy the statement if it can get invalidated by findOrCreateBlockToInsertStatement()
val statementToUse = if (statement.isPhysical) statement.copy() else statement val statementToUse = if (statement.isPhysical) statement.copy() else statement
val anchor = findOrCreateBlockToInsertStatement() val anchor = findOrCreateBlockToInsertStatement()
@@ -83,8 +83,8 @@ internal class ExpressionReplacementPerformer(
insertedStatements.add(inserted) insertedStatements.add(inserted)
} }
val replaced = if (replacement.mainExpression != null) { val replaced = if (codeToInline.mainExpression != null) {
elementToBeReplaced.replace(replacement.mainExpression!!) elementToBeReplaced.replace(codeToInline.mainExpression!!)
} }
else { else {
val bindingContext = elementToBeReplaced.analyze(BodyResolveMode.FULL) val bindingContext = elementToBeReplaced.analyze(BodyResolveMode.FULL)
@@ -98,7 +98,7 @@ internal class ExpressionReplacementPerformer(
} }
} }
replacement.performPostInsertionActions(insertedStatements + replaced.singletonOrEmptyList()) codeToInline.performPostInsertionActions(insertedStatements + replaced.singletonOrEmptyList())
var range = if (replaced != null) { var range = if (replaced != null) {
if (insertedStatements.isEmpty()) { if (insertedStatements.isEmpty()) {
@@ -14,7 +14,7 @@
* limitations under the License. * limitations under the License.
*/ */
package org.jetbrains.kotlin.idea.replacement package org.jetbrains.kotlin.idea.inliner
import com.intellij.openapi.diagnostic.Logger import com.intellij.openapi.diagnostic.Logger
import com.intellij.openapi.progress.ProgressIndicator import com.intellij.openapi.progress.ProgressIndicator
@@ -14,7 +14,7 @@
* limitations under the License. * limitations under the License.
*/ */
package org.jetbrains.kotlin.idea.replacement package org.jetbrains.kotlin.idea.inliner
import org.jetbrains.kotlin.idea.analysis.computeTypeInContext import org.jetbrains.kotlin.idea.analysis.computeTypeInContext
import org.jetbrains.kotlin.idea.caches.resolve.analyze import org.jetbrains.kotlin.idea.caches.resolve.analyze
@@ -38,17 +38,17 @@ import org.jetbrains.kotlin.types.KotlinType
import org.jetbrains.kotlin.utils.addToStdlib.check import org.jetbrains.kotlin.utils.addToStdlib.check
/** /**
* Modifies [MutableReplacementCode] introducing a variable initialized by [value] and replacing all of [usages] with its use. * Modifies [MutableCodeToInline] introducing a variable initialized by [value] and replacing all of [usages] with its use.
* The variable must be initialized (and so the value is calculated) before any other code in [MutableReplacementCode]. * The variable must be initialized (and so the value is calculated) before any other code in [MutableCodeToInline].
* @param value Value to use for variable initialization * @param value Value to use for variable initialization
* @param valueType Type of the value * @param valueType Type of the value
* @param usages Usages to be replaced. This collection can be empty and in this case the actual variable is not needed. * @param usages Usages to be replaced. This collection can be empty and in this case the actual variable is not needed.
* But the expression [value] must be calculated because it may have side effects. * But the expression [value] must be calculated because it may have side effects.
* @param expressionToBeReplaced Expression to be replaced by the [MutableReplacementCode]. * @param expressionToBeReplaced Expression to be replaced by the [MutableCodeToInline].
* @param nameSuggestion Name suggestion for the variable. * @param nameSuggestion Name suggestion for the variable.
* @param safeCall If true, then the whole code must not be executed if the [value] evaluates to null. * @param safeCall If true, then the whole code must not be executed if the [value] evaluates to null.
*/ */
internal fun MutableReplacementCode.introduceValue( internal fun MutableCodeToInline.introduceValue(
value: KtExpression, value: KtExpression,
valueType: KotlinType?, valueType: KotlinType?,
usages: Collection<KtExpression>, usages: Collection<KtExpression>,
@@ -151,7 +151,7 @@ private fun variableNeedsExplicitType(
return valueTypeWithoutExpectedType == null || ErrorUtils.containsErrorType(valueTypeWithoutExpectedType) return valueTypeWithoutExpectedType == null || ErrorUtils.containsErrorType(valueTypeWithoutExpectedType)
} }
private fun collectNameUsages(scope: MutableReplacementCode, name: String): List<KtSimpleNameExpression> { private fun collectNameUsages(scope: MutableCodeToInline, name: String): List<KtSimpleNameExpression> {
return scope.expressions.flatMap { expression -> return scope.expressions.flatMap { expression ->
expression.collectDescendantsOfType<KtSimpleNameExpression> { it.getReceiverExpression() == null && it.getReferencedName() == name } expression.collectDescendantsOfType<KtSimpleNameExpression> { it.getReceiverExpression() == null && it.getReferencedName() == name }
} }
+1 -1
View File
@@ -55,6 +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" /> <orderEntry type="module" module-name="idea-code-inliner" />
</component> </component>
</module> </module>
@@ -25,7 +25,7 @@ import org.jetbrains.kotlin.idea.core.moveCaret
import org.jetbrains.kotlin.idea.core.targetDescriptors import org.jetbrains.kotlin.idea.core.targetDescriptors
import org.jetbrains.kotlin.idea.quickfix.CleanupFix import org.jetbrains.kotlin.idea.quickfix.CleanupFix
import org.jetbrains.kotlin.idea.quickfix.KotlinSingleIntentionActionFactory import org.jetbrains.kotlin.idea.quickfix.KotlinSingleIntentionActionFactory
import org.jetbrains.kotlin.idea.replacement.UsageReplacementStrategy import org.jetbrains.kotlin.idea.inliner.UsageReplacementStrategy
import org.jetbrains.kotlin.psi.KtImportDirective import org.jetbrains.kotlin.psi.KtImportDirective
import org.jetbrains.kotlin.psi.KtSimpleNameExpression import org.jetbrains.kotlin.psi.KtSimpleNameExpression
import org.jetbrains.kotlin.resolve.calls.callUtil.getCalleeExpressionIfAny import org.jetbrains.kotlin.resolve.calls.callUtil.getCalleeExpressionIfAny
@@ -31,9 +31,9 @@ import org.jetbrains.kotlin.idea.caches.resolve.getResolutionFacade
import org.jetbrains.kotlin.idea.core.OptionalParametersHelper import org.jetbrains.kotlin.idea.core.OptionalParametersHelper
import org.jetbrains.kotlin.idea.quickfix.KotlinQuickFixAction import org.jetbrains.kotlin.idea.quickfix.KotlinQuickFixAction
import org.jetbrains.kotlin.idea.references.mainReference import org.jetbrains.kotlin.idea.references.mainReference
import org.jetbrains.kotlin.idea.replacement.CallableUsageReplacementStrategy import org.jetbrains.kotlin.idea.inliner.CallableUsageReplacementStrategy
import org.jetbrains.kotlin.idea.replacement.ClassUsageReplacementStrategy import org.jetbrains.kotlin.idea.inliner.ClassUsageReplacementStrategy
import org.jetbrains.kotlin.idea.replacement.UsageReplacementStrategy import org.jetbrains.kotlin.idea.inliner.UsageReplacementStrategy
import org.jetbrains.kotlin.psi.KtConstructorCalleeExpression import org.jetbrains.kotlin.psi.KtConstructorCalleeExpression
import org.jetbrains.kotlin.psi.KtFile import org.jetbrains.kotlin.psi.KtFile
import org.jetbrains.kotlin.psi.KtSimpleNameExpression import org.jetbrains.kotlin.psi.KtSimpleNameExpression
@@ -23,8 +23,8 @@ import com.intellij.psi.PsiFile
import org.jetbrains.kotlin.diagnostics.Diagnostic import org.jetbrains.kotlin.diagnostics.Diagnostic
import org.jetbrains.kotlin.idea.quickfix.KotlinSingleIntentionActionFactory import org.jetbrains.kotlin.idea.quickfix.KotlinSingleIntentionActionFactory
import org.jetbrains.kotlin.idea.references.mainReference import org.jetbrains.kotlin.idea.references.mainReference
import org.jetbrains.kotlin.idea.replacement.UsageReplacementStrategy import org.jetbrains.kotlin.idea.inliner.UsageReplacementStrategy
import org.jetbrains.kotlin.idea.replacement.replaceUsagesInWholeProject import org.jetbrains.kotlin.idea.inliner.replaceUsagesInWholeProject
import org.jetbrains.kotlin.psi.* import org.jetbrains.kotlin.psi.*
import org.jetbrains.kotlin.renderer.ClassifierNamePolicy import org.jetbrains.kotlin.renderer.ClassifierNamePolicy
import org.jetbrains.kotlin.renderer.DescriptorRenderer import org.jetbrains.kotlin.renderer.DescriptorRenderer
@@ -21,8 +21,8 @@ import org.jetbrains.kotlin.idea.analysis.analyzeInContext
import org.jetbrains.kotlin.idea.caches.resolve.resolveImportReference import org.jetbrains.kotlin.idea.caches.resolve.resolveImportReference
import org.jetbrains.kotlin.idea.references.KtSimpleNameReference import org.jetbrains.kotlin.idea.references.KtSimpleNameReference
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.inliner.CodeToInlineBuilder
import org.jetbrains.kotlin.idea.replacement.ReplacementCode import org.jetbrains.kotlin.idea.inliner.CodeToInline
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
@@ -50,7 +50,7 @@ object ReplaceWithAnnotationAnalyzer {
annotation: ReplaceWith, annotation: ReplaceWith,
symbolDescriptor: CallableDescriptor, symbolDescriptor: CallableDescriptor,
resolutionFacade: ResolutionFacade resolutionFacade: ResolutionFacade
): ReplacementCode? { ): CodeToInline? {
val originalDescriptor = (if (symbolDescriptor is CallableMemberDescriptor) val originalDescriptor = (if (symbolDescriptor is CallableMemberDescriptor)
DescriptorUtils.unwrapFakeOverride(symbolDescriptor) DescriptorUtils.unwrapFakeOverride(symbolDescriptor)
else else
@@ -62,7 +62,7 @@ object ReplaceWithAnnotationAnalyzer {
annotation: ReplaceWith, annotation: ReplaceWith,
symbolDescriptor: CallableDescriptor, symbolDescriptor: CallableDescriptor,
resolutionFacade: ResolutionFacade resolutionFacade: ResolutionFacade
): ReplacementCode? { ): CodeToInline? {
val psiFactory = KtPsiFactory(resolutionFacade.project) val psiFactory = KtPsiFactory(resolutionFacade.project)
val expression = try { val expression = try {
psiFactory.createExpression(annotation.pattern) psiFactory.createExpression(annotation.pattern)
@@ -89,8 +89,8 @@ object ReplaceWithAnnotationAnalyzer {
return expression.analyzeInContext(scope, expressionTypingServices = expressionTypingServices) return expression.analyzeInContext(scope, expressionTypingServices = expressionTypingServices)
} }
return ReplacementBuilder(symbolDescriptor, resolutionFacade) return CodeToInlineBuilder(symbolDescriptor, resolutionFacade)
.buildReplacementCode(expression, emptyList(), ::analyzeExpression, importFqNames = importFqNames(annotation)) .prepareCodeToInline(expression, emptyList(), ::analyzeExpression, importFqNames = importFqNames(annotation))
} }
fun analyzeClassReplacement( fun analyzeClassReplacement(
@@ -29,9 +29,9 @@ import org.jetbrains.kotlin.idea.analysis.analyzeInContext
import org.jetbrains.kotlin.idea.caches.resolve.getResolutionFacade import org.jetbrains.kotlin.idea.caches.resolve.getResolutionFacade
import org.jetbrains.kotlin.idea.caches.resolve.resolveToDescriptor import org.jetbrains.kotlin.idea.caches.resolve.resolveToDescriptor
import org.jetbrains.kotlin.idea.core.copied import org.jetbrains.kotlin.idea.core.copied
import org.jetbrains.kotlin.idea.replacement.CallableUsageReplacementStrategy import org.jetbrains.kotlin.idea.inliner.CallableUsageReplacementStrategy
import org.jetbrains.kotlin.idea.replacement.ReplacementBuilder import org.jetbrains.kotlin.idea.inliner.CodeToInlineBuilder
import org.jetbrains.kotlin.idea.replacement.replaceUsagesInWholeProject import org.jetbrains.kotlin.idea.inliner.replaceUsagesInWholeProject
import org.jetbrains.kotlin.idea.util.getResolutionScope import org.jetbrains.kotlin.idea.util.getResolutionScope
import org.jetbrains.kotlin.psi.KtBlockExpression import org.jetbrains.kotlin.psi.KtBlockExpression
import org.jetbrains.kotlin.psi.KtNamedFunction import org.jetbrains.kotlin.psi.KtNamedFunction
@@ -67,21 +67,21 @@ class KotlinInlineFunctionHandler: InlineActionHandler() {
expectedType = expectedType) expectedType = expectedType)
} }
val replacementBuilder = ReplacementBuilder(descriptor, element.getResolutionFacade()) val replacementBuilder = CodeToInlineBuilder(descriptor, element.getResolutionFacade())
val replacement = if (element.hasBlockBody()) { val replacement = if (element.hasBlockBody()) {
bodyCopy as KtBlockExpression bodyCopy as KtBlockExpression
val statements = bodyCopy.statements val statements = bodyCopy.statements
//TODO: check no other return's! //TODO: check no other return's!
val lastReturn = statements.lastOrNull() as? KtReturnExpression val lastReturn = statements.lastOrNull() as? KtReturnExpression
if (lastReturn != null) { if (lastReturn != null) {
replacementBuilder.buildReplacementCode(lastReturn.returnedExpression, statements.dropLast(1), ::analyzeBodyCopy) replacementBuilder.prepareCodeToInline(lastReturn.returnedExpression, statements.dropLast(1), ::analyzeBodyCopy)
} }
else { else {
replacementBuilder.buildReplacementCode(null, statements, ::analyzeBodyCopy) replacementBuilder.prepareCodeToInline(null, statements, ::analyzeBodyCopy)
} }
} }
else { else {
replacementBuilder.buildReplacementCode(bodyCopy, emptyList(), ::analyzeBodyCopy) replacementBuilder.prepareCodeToInline(bodyCopy, emptyList(), ::analyzeBodyCopy)
} }
val commandName = RefactoringBundle.message("inline.command", element.name) val commandName = RefactoringBundle.message("inline.command", element.name)