diff --git a/idea/idea-core/src/org/jetbrains/kotlin/idea/core/psiModificationUtils.kt b/idea/idea-core/src/org/jetbrains/kotlin/idea/core/psiModificationUtils.kt index 3d290d3f17d..ff6492ddd16 100644 --- a/idea/idea-core/src/org/jetbrains/kotlin/idea/core/psiModificationUtils.kt +++ b/idea/idea-core/src/org/jetbrains/kotlin/idea/core/psiModificationUtils.kt @@ -74,18 +74,20 @@ fun KtLambdaArgument.getLambdaArgumentName(bindingContext: BindingContext): Name fun KtLambdaArgument.moveInsideParenthesesAndReplaceWith( replacement: KtExpression, - functionLiteralArgumentName: Name? + functionLiteralArgumentName: Name?, + withNameCheck: Boolean = true, ): KtCallExpression { val oldCallExpression = parent as KtCallExpression val newCallExpression = oldCallExpression.copy() as KtCallExpression val psiFactory = KtPsiFactory(project) - val argument = if (shouldLambdaParameterBeNamed(newCallExpression.getValueArgumentsInParentheses(), oldCallExpression)) { - psiFactory.createArgument(replacement, functionLiteralArgumentName) - } else { - psiFactory.createArgument(replacement) - } + val argument = + if (withNameCheck && shouldLambdaParameterBeNamed(newCallExpression.getValueArgumentsInParentheses(), oldCallExpression)) { + psiFactory.createArgument(replacement, functionLiteralArgumentName) + } else { + psiFactory.createArgument(replacement) + } val functionLiteralArgument = newCallExpression.lambdaArguments.firstOrNull()!! val valueArgumentList = newCallExpression.valueArgumentList ?: psiFactory.createCallArguments("()") diff --git a/idea/src/org/jetbrains/kotlin/idea/codeInliner/ReplacementPerformer.kt b/idea/src/org/jetbrains/kotlin/idea/codeInliner/ReplacementPerformer.kt index 096c0a61b86..0bbf1c192c9 100644 --- a/idea/src/org/jetbrains/kotlin/idea/codeInliner/ReplacementPerformer.kt +++ b/idea/src/org/jetbrains/kotlin/idea/codeInliner/ReplacementPerformer.kt @@ -1,17 +1,6 @@ /* - * 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. + * Copyright 2010-2020 JetBrains s.r.o. and Kotlin Programming Language contributors. + * Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file. */ package org.jetbrains.kotlin.idea.codeInliner @@ -23,6 +12,7 @@ import org.jetbrains.kotlin.descriptors.annotations.AnnotationUseSiteTarget import org.jetbrains.kotlin.idea.caches.resolve.analyze import org.jetbrains.kotlin.idea.core.canDropBraces import org.jetbrains.kotlin.idea.core.dropBraces +import org.jetbrains.kotlin.idea.core.moveInsideParenthesesAndReplaceWith import org.jetbrains.kotlin.idea.core.replaced import org.jetbrains.kotlin.idea.intentions.ConvertToBlockBodyIntention import org.jetbrains.kotlin.psi.* @@ -43,26 +33,43 @@ internal abstract class ReplacementPerformer( abstract fun doIt(postProcessing: (PsiChildRange) -> PsiChildRange): TElement? } -internal class AnnotationEntryReplacementPerformer( +internal abstract class AbstractSimpleReplacementPerformer( codeToInline: MutableCodeToInline, - elementToBeReplaced: KtAnnotationEntry -) : ReplacementPerformer(codeToInline, elementToBeReplaced) { + elementToBeReplaced: TElement +) : ReplacementPerformer(codeToInline, elementToBeReplaced) { + protected abstract fun createDummyElement(mainExpression: KtExpression): TElement - override fun doIt(postProcessing: (PsiChildRange) -> PsiChildRange): KtAnnotationEntry { - assert(codeToInline.mainExpression != null) + protected open fun rangeToElement(range: PsiChildRange): TElement { + assert(range.first == range.last) + + @Suppress("UNCHECKED_CAST") + return range.first as TElement + } + + final override fun doIt(postProcessing: (PsiChildRange) -> PsiChildRange): TElement { assert(codeToInline.statementsBefore.isEmpty()) + val mainExpression = codeToInline.mainExpression ?: error("mainExpression mustn't be null") - val useSiteTarget = elementToBeReplaced.useSiteTarget?.getAnnotationUseSiteTarget() - val useSiteTargetText = useSiteTarget?.renderName?.let { "$it:" } ?: "" - val isFileUseSiteTarget = useSiteTarget == AnnotationUseSiteTarget.FILE - - val dummyAnnotationEntry = createByPattern("@Dummy($0)", codeToInline.mainExpression!!) { psiFactory.createAnnotationEntry(it) } - val replaced = elementToBeReplaced.replace(dummyAnnotationEntry) + val dummyElement = createDummyElement(mainExpression) + val replaced = elementToBeReplaced.replace(dummyElement) codeToInline.performPostInsertionActions(listOf(replaced)) - var range = PsiChildRange.singleElement(replaced) - range = postProcessing(range) + return rangeToElement(postProcessing(PsiChildRange.singleElement(replaced))) + } +} + +internal class AnnotationEntryReplacementPerformer( + codeToInline: MutableCodeToInline, + elementToBeReplaced: KtAnnotationEntry +) : AbstractSimpleReplacementPerformer(codeToInline, elementToBeReplaced) { + override fun createDummyElement(mainExpression: KtExpression): KtAnnotationEntry = + createByPattern("@Dummy($0)", mainExpression) { psiFactory.createAnnotationEntry(it) } + + override fun rangeToElement(range: PsiChildRange): KtAnnotationEntry { + val useSiteTarget = elementToBeReplaced.useSiteTarget?.getAnnotationUseSiteTarget() + val useSiteTargetText = useSiteTarget?.renderName?.let { "$it:" } ?: "" + val isFileUseSiteTarget = useSiteTarget == AnnotationUseSiteTarget.FILE assert(range.first == range.last) assert(range.first is KtAnnotationEntry) @@ -76,12 +83,39 @@ internal class AnnotationEntryReplacementPerformer( } } +internal class SuperTypeCallEntryReplacementPerformer( + codeToInline: MutableCodeToInline, + elementToBeReplaced: KtSuperTypeCallEntry +) : AbstractSimpleReplacementPerformer(codeToInline, elementToBeReplaced) { + override fun createDummyElement(mainExpression: KtExpression): KtSuperTypeCallEntry { + val text = if (mainExpression is KtCallExpression && mainExpression.lambdaArguments.isNotEmpty()) { + callWithoutLambdaArguments(mainExpression) + } else { + mainExpression.text + } + + return psiFactory.createSuperTypeCallEntry(text) + } +} + +private fun callWithoutLambdaArguments(callExpression: KtCallExpression): String { + val copy = callExpression.copy() as KtCallExpression + val lambdaArgument = copy.lambdaArguments.first() + + val argumentExpression = lambdaArgument.getArgumentExpression() ?: return callExpression.text + return lambdaArgument.moveInsideParenthesesAndReplaceWith( + replacement = argumentExpression, + functionLiteralArgumentName = null, + withNameCheck = false + ).text ?: callExpression.text +} + internal class ExpressionReplacementPerformer( codeToInline: MutableCodeToInline, expressionToBeReplaced: KtExpression ) : ReplacementPerformer(codeToInline, expressionToBeReplaced) { - fun KtExpression.replacedWithStringTemplate(templateExpression: KtStringTemplateExpression): KtExpression? { + private fun KtExpression.replacedWithStringTemplate(templateExpression: KtStringTemplateExpression): KtExpression? { val parent = this.parent return if (parent is KtStringTemplateEntryWithExpression