Refactoring: inline val / fun now use the common inliner

This prevents their inconsistent work in some situations
NB: breaks three tests if used alone
This commit is contained in:
Mikhail Glukhikh
2017-04-04 19:19:03 +03:00
parent e6fa7356e1
commit 4ef0096d46
11 changed files with 111 additions and 221 deletions
@@ -18,6 +18,7 @@ package org.jetbrains.kotlin.idea.refactoring.inline
import com.intellij.lang.findUsages.DescriptiveNameUtil import com.intellij.lang.findUsages.DescriptiveNameUtil
import com.intellij.openapi.project.Project import com.intellij.openapi.project.Project
import com.intellij.psi.PsiElement
import com.intellij.psi.search.GlobalSearchScope import com.intellij.psi.search.GlobalSearchScope
import com.intellij.psi.search.searches.ReferencesSearch import com.intellij.psi.search.searches.ReferencesSearch
import com.intellij.refactoring.BaseRefactoringProcessor import com.intellij.refactoring.BaseRefactoringProcessor
@@ -31,25 +32,34 @@ import org.jetbrains.kotlin.idea.codeInliner.replaceUsages
import org.jetbrains.kotlin.idea.references.KtSimpleNameReference import org.jetbrains.kotlin.idea.references.KtSimpleNameReference
import org.jetbrains.kotlin.idea.stubindex.KotlinSourceFilterScope import org.jetbrains.kotlin.idea.stubindex.KotlinSourceFilterScope
import org.jetbrains.kotlin.idea.util.application.runReadAction import org.jetbrains.kotlin.idea.util.application.runReadAction
import org.jetbrains.kotlin.psi.KtCallableDeclaration
import org.jetbrains.kotlin.psi.KtNamedFunction import org.jetbrains.kotlin.psi.KtNamedFunction
import org.jetbrains.kotlin.psi.KtProperty
import org.jetbrains.kotlin.psi.KtSimpleNameExpression import org.jetbrains.kotlin.psi.KtSimpleNameExpression
class KotlinInlineFunctionProcessor( class KotlinInlineCallableProcessor(
project: Project, project: Project,
private val replacementStrategy: CallableUsageReplacementStrategy, private val replacementStrategy: CallableUsageReplacementStrategy,
private val function: KtNamedFunction, private val declaration: KtCallableDeclaration,
private val reference: KtSimpleNameReference?, private val reference: KtSimpleNameReference?,
private val inlineThisOnly: Boolean, private val inlineThisOnly: Boolean,
private val deleteAfter: Boolean private val deleteAfter: Boolean,
private val assignments: Set<PsiElement> = emptySet()
) : BaseRefactoringProcessor(project) { ) : BaseRefactoringProcessor(project) {
private val commandName = "Inlining function ${DescriptiveNameUtil.getDescriptiveName(function)}" private val kind = when (declaration) {
is KtNamedFunction -> "function"
is KtProperty -> if (declaration.isLocal) "local variable" else "property"
else -> "declaration"
}
private val commandName = "Inlining $kind ${DescriptiveNameUtil.getDescriptiveName(declaration)}"
override fun findUsages(): Array<UsageInfo> { override fun findUsages(): Array<UsageInfo> {
if (inlineThisOnly && reference != null) return arrayOf(UsageInfo(reference)) if (inlineThisOnly && reference != null) return arrayOf(UsageInfo(reference))
val usages = runReadAction { val usages = runReadAction {
val searchScope = KotlinSourceFilterScope.projectSources(GlobalSearchScope.projectScope(myProject), myProject) val searchScope = KotlinSourceFilterScope.projectSources(GlobalSearchScope.projectScope(myProject), myProject)
ReferencesSearch.search(function, searchScope) ReferencesSearch.search(declaration, searchScope)
} }
return usages.map(::UsageInfo).toTypedArray() return usages.map(::UsageInfo).toTypedArray()
} }
@@ -58,20 +68,21 @@ class KotlinInlineFunctionProcessor(
val simpleNameUsages = usages.mapNotNull { it.element as? KtSimpleNameExpression } val simpleNameUsages = usages.mapNotNull { it.element as? KtSimpleNameExpression }
replacementStrategy.replaceUsages( replacementStrategy.replaceUsages(
simpleNameUsages, simpleNameUsages,
function, declaration,
myProject, myProject,
commandName, commandName,
{ {
if (deleteAfter) { if (deleteAfter) {
if (usages.size == simpleNameUsages.size) { if (usages.size == simpleNameUsages.size) {
function.delete() declaration.delete()
assignments.forEach(PsiElement::delete)
} }
else { else {
CommonRefactoringUtil.showErrorHint( CommonRefactoringUtil.showErrorHint(
function.project, declaration.project,
null, null,
"Cannot inline ${usages.size - simpleNameUsages.size}/${usages.size} usages", "Cannot inline ${usages.size - simpleNameUsages.size}/${usages.size} usages",
"Inline Function", "Inline $kind",
null null
) )
} }
@@ -90,9 +101,9 @@ class KotlinInlineFunctionProcessor(
override fun getCodeReferencesText(usagesCount: Int, filesCount: Int) = override fun getCodeReferencesText(usagesCount: Int, filesCount: Int) =
RefactoringBundle.message("invocations.to.be.inlined", UsageViewBundle.getReferencesString(usagesCount, filesCount)) RefactoringBundle.message("invocations.to.be.inlined", UsageViewBundle.getReferencesString(usagesCount, filesCount))
override fun getElements() = arrayOf(function) override fun getElements() = arrayOf(declaration)
override fun getProcessedElementsHeader() = "Function to inline" override fun getProcessedElementsHeader() = "${kind.capitalize()} to inline"
} }
} }
} }
@@ -20,7 +20,6 @@ import com.intellij.openapi.help.HelpManager
import com.intellij.openapi.project.Project import com.intellij.openapi.project.Project
import com.intellij.refactoring.HelpID import com.intellij.refactoring.HelpID
import com.intellij.refactoring.JavaRefactoringSettings import com.intellij.refactoring.JavaRefactoringSettings
import com.intellij.refactoring.RefactoringBundle
import com.intellij.refactoring.inline.InlineOptionsDialog import com.intellij.refactoring.inline.InlineOptionsDialog
import org.jetbrains.kotlin.idea.codeInliner.CallableUsageReplacementStrategy import org.jetbrains.kotlin.idea.codeInliner.CallableUsageReplacementStrategy
import org.jetbrains.kotlin.idea.quickfix.createFromUsage.callableBuilder.getReturnTypeReference import org.jetbrains.kotlin.idea.quickfix.createFromUsage.callableBuilder.getReturnTypeReference
@@ -71,7 +70,7 @@ class KotlinInlineFunctionDialog(
public override fun doAction() { public override fun doAction() {
invokeRefactoring( invokeRefactoring(
KotlinInlineFunctionProcessor(project, replacementStrategy, function, reference, KotlinInlineCallableProcessor(project, replacementStrategy, function, reference,
inlineThisOnly = isInlineThisOnly || allowInlineThisOnly, inlineThisOnly = isInlineThisOnly || allowInlineThisOnly,
deleteAfter = !isInlineThisOnly && !isKeepTheDeclaration && !allowInlineThisOnly) deleteAfter = !isInlineThisOnly && !isKeepTheDeclaration && !allowInlineThisOnly)
) )
@@ -17,48 +17,64 @@
package org.jetbrains.kotlin.idea.refactoring.inline package org.jetbrains.kotlin.idea.refactoring.inline
import com.intellij.openapi.util.text.StringUtil import com.intellij.openapi.util.text.StringUtil
import com.intellij.psi.PsiReference import com.intellij.psi.PsiElement
import com.intellij.refactoring.JavaRefactoringSettings import com.intellij.refactoring.JavaRefactoringSettings
import com.intellij.refactoring.inline.AbstractInlineLocalDialog import com.intellij.refactoring.inline.InlineOptionsDialog
import org.jetbrains.kotlin.idea.codeInliner.CallableUsageReplacementStrategy
import org.jetbrains.kotlin.idea.references.KtSimpleNameReference
import org.jetbrains.kotlin.psi.KtProperty import org.jetbrains.kotlin.psi.KtProperty
class KotlinInlineValDialog( class KotlinInlineValDialog(
element: KtProperty, private val property: KtProperty,
ref: PsiReference?, private val reference: KtSimpleNameReference?,
private val occurrenceCount: Int private val replacementStrategy: CallableUsageReplacementStrategy,
) : AbstractInlineLocalDialog(element.project, element, ref, occurrenceCount) { private val assignments: Set<PsiElement>
private val property: KtProperty get() = myElement as KtProperty ) : InlineOptionsDialog(property.project, true, property) {
private var occurrenceCount = initOccurrencesNumber(property)
private val kind = if (property.isLocal) "local variable" else "property" private val kind = if (property.isLocal) "local variable" else "property"
private val refactoringName = "Inline ${StringUtil.capitalizeWords(kind, true)}" private val refactoringName = "Inline ${StringUtil.capitalizeWords(kind, true)}"
init { init {
myInvokedOnReference = ref != null myInvokedOnReference = reference != null
title = refactoringName title = refactoringName
init() init()
} }
override fun allowInlineAll() = true
override fun getBorderTitle() = refactoringName override fun getBorderTitle() = refactoringName
override fun getNameLabelText() = "${kind.capitalize()} ${property.name}" override fun getNameLabelText() = "${kind.capitalize()} ${property.name}"
override fun getInlineAllText(): String? { private val occurrencesString get() = if (occurrenceCount >= 0) {
val occurrencesString = if (occurrenceCount >= 0) { " (" + occurrenceCount + " occurrence" + (if (occurrenceCount == 1) ")" else "s)")
" (" + occurrenceCount + " occurrence" + (if (occurrenceCount == 1) ")" else "s)") } else ""
} else ""
return "Inline all references and remove the $kind" + occurrencesString override fun getInlineAllText() =
} "Inline all references and remove the $kind" + occurrencesString
override fun getKeepTheDeclarationText(): String? =
if (property.isWritable) "Inline all references and keep the $kind" + occurrencesString
else super.getKeepTheDeclarationText()
override fun isInlineThis() = JavaRefactoringSettings.getInstance().INLINE_LOCAL_THIS override fun isInlineThis() = JavaRefactoringSettings.getInstance().INLINE_LOCAL_THIS
override fun getInlineThisText() = "Inline this occurrence and leave the $kind" override fun getInlineThisText() = "Inline this occurrence and leave the $kind"
override fun doAction() { public override fun doAction() {
invokeRefactoring(
KotlinInlineCallableProcessor(project, replacementStrategy, property, reference,
inlineThisOnly = isInlineThisOnly,
deleteAfter = !isInlineThisOnly && !isKeepTheDeclaration,
assignments = assignments)
)
val settings = JavaRefactoringSettings.getInstance() val settings = JavaRefactoringSettings.getInstance()
if (myRbInlineThisOnly.isEnabled && myRbInlineAll.isEnabled) { if (myRbInlineThisOnly.isEnabled && myRbInlineAll.isEnabled) {
settings.INLINE_LOCAL_THIS = isInlineThisOnly settings.INLINE_LOCAL_THIS = isInlineThisOnly
} }
close(OK_EXIT_CODE)
} }
} }
@@ -16,51 +16,39 @@
package org.jetbrains.kotlin.idea.refactoring.inline package org.jetbrains.kotlin.idea.refactoring.inline
import com.google.common.collect.Sets import com.intellij.codeInsight.TargetElementUtil
import com.intellij.lang.Language import com.intellij.lang.Language
import com.intellij.lang.refactoring.InlineActionHandler import com.intellij.lang.refactoring.InlineActionHandler
import com.intellij.openapi.application.ApplicationManager import com.intellij.openapi.application.ApplicationManager
import com.intellij.openapi.editor.Editor import com.intellij.openapi.editor.Editor
import com.intellij.openapi.editor.ex.EditorSettingsExternalizable
import com.intellij.openapi.project.Project import com.intellij.openapi.project.Project
import com.intellij.openapi.wm.WindowManager import com.intellij.openapi.wm.WindowManager
import com.intellij.psi.PsiElement import com.intellij.psi.PsiElement
import com.intellij.psi.PsiReference
import com.intellij.psi.PsiWhiteSpace
import com.intellij.psi.search.searches.ReferencesSearch import com.intellij.psi.search.searches.ReferencesSearch
import com.intellij.refactoring.HelpID import com.intellij.refactoring.HelpID
import com.intellij.refactoring.JavaRefactoringSettings
import com.intellij.refactoring.RefactoringBundle import com.intellij.refactoring.RefactoringBundle
import com.intellij.refactoring.util.CommonRefactoringUtil import com.intellij.refactoring.util.CommonRefactoringUtil
import com.intellij.util.containers.MultiMap import com.intellij.util.containers.MultiMap
import org.jetbrains.kotlin.diagnostics.Errors import org.jetbrains.kotlin.descriptors.VariableDescriptor
import org.jetbrains.kotlin.idea.KotlinLanguage import org.jetbrains.kotlin.idea.KotlinLanguage
import org.jetbrains.kotlin.idea.caches.resolve.analyze 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.codeInsight.shorten.performDelayedRefactoringRequests import org.jetbrains.kotlin.idea.caches.resolve.resolveToDescriptor
import org.jetbrains.kotlin.idea.core.ShortenReferences import org.jetbrains.kotlin.idea.codeInliner.CallableUsageReplacementStrategy
import org.jetbrains.kotlin.idea.core.replaced import org.jetbrains.kotlin.idea.codeInliner.CodeToInlineBuilder
import org.jetbrains.kotlin.idea.refactoring.addTypeArgumentsIfNeeded import org.jetbrains.kotlin.idea.core.copied
import org.jetbrains.kotlin.idea.refactoring.checkConflictsInteractively import org.jetbrains.kotlin.idea.refactoring.checkConflictsInteractively
import org.jetbrains.kotlin.idea.refactoring.getQualifiedTypeArgumentList import org.jetbrains.kotlin.idea.references.KtSimpleNameReference
import org.jetbrains.kotlin.idea.references.mainReference import org.jetbrains.kotlin.idea.util.getResolutionScope
import org.jetbrains.kotlin.idea.resolve.ResolutionFacade
import org.jetbrains.kotlin.idea.util.IdeDescriptorRenderers
import org.jetbrains.kotlin.idea.util.application.executeWriteCommand
import org.jetbrains.kotlin.psi.* import org.jetbrains.kotlin.psi.*
import org.jetbrains.kotlin.psi.psiUtil.* import org.jetbrains.kotlin.psi.psiUtil.getAssignmentByLHS
import org.jetbrains.kotlin.psi.psiUtil.getQualifiedExpressionForSelectorOrThis
import org.jetbrains.kotlin.resolve.BindingContext import org.jetbrains.kotlin.resolve.BindingContext
import org.jetbrains.kotlin.resolve.lazy.BodyResolveMode import org.jetbrains.kotlin.types.TypeUtils
import org.jetbrains.kotlin.types.ErrorUtils
import org.jetbrains.kotlin.types.expressions.OperatorConventions import org.jetbrains.kotlin.types.expressions.OperatorConventions
import org.jetbrains.kotlin.utils.addIfNotNull import org.jetbrains.kotlin.utils.addIfNotNull
import org.jetbrains.kotlin.utils.sure
import java.util.*
class KotlinInlineValHandler : InlineActionHandler() { class KotlinInlineValHandler : InlineActionHandler() {
enum class InlineMode {
ALL, PRIMARY, NONE
}
override fun isEnabledForLanguage(l: Language) = l == KotlinLanguage.INSTANCE override fun isEnabledForLanguage(l: Language) = l == KotlinLanguage.INSTANCE
@@ -69,46 +57,14 @@ class KotlinInlineValHandler : InlineActionHandler() {
return element.getter == null && element.receiverTypeReference == null return element.getter == null && element.receiverTypeReference == null
} }
private fun doReplace(expression: KtExpression, replacement: KtExpression): List<KtExpression> {
val parent = expression.parent
if (parent is KtStringTemplateEntryWithExpression &&
replacement is KtStringTemplateExpression &&
// Do not mix raw and non-raw templates
parent.parent.firstChild.text == replacement.firstChild.text) {
val entriesToAdd = replacement.entries
val templateExpression = parent.parent as KtStringTemplateExpression
val inlinedExpressions = if (entriesToAdd.isNotEmpty()) {
val firstAddedEntry = templateExpression.addRangeBefore(entriesToAdd.first(), entriesToAdd.last(), parent)
val lastNewEntry = parent.prevSibling
val nextElement = parent.nextSibling
if (lastNewEntry is KtSimpleNameStringTemplateEntry &&
lastNewEntry.expression != null &&
!canPlaceAfterSimpleNameEntry(nextElement)) {
lastNewEntry.replace(KtPsiFactory(expression).createBlockStringTemplateEntry(lastNewEntry.expression!!))
}
firstAddedEntry.siblings()
.take(entriesToAdd.size)
.mapNotNull { (it as? KtStringTemplateEntryWithExpression)?.expression }
.toList()
}
else emptyList()
parent.delete()
return inlinedExpressions
}
return listOf(expression.replaced(replacement))
}
override fun inlineElement(project: Project, editor: Editor?, element: PsiElement) { override fun inlineElement(project: Project, editor: Editor?, element: PsiElement) {
val declaration = element as KtProperty val declaration = element as KtProperty
val file = declaration.containingKtFile val file = declaration.containingKtFile
val name = declaration.name ?: return val name = declaration.name ?: return
val references = ReferencesSearch.search(declaration) val references = ReferencesSearch.search(declaration)
val referenceExpressions = ArrayList<KtExpression>() val referenceExpressions = mutableListOf<KtExpression>()
val foreignUsages = ArrayList<PsiElement>() val foreignUsages = mutableListOf<PsiElement>()
for (ref in references) { for (ref in references) {
val refElement = ref.element ?: continue val refElement = ref.element ?: continue
if (refElement !is KtElement) { if (refElement !is KtElement) {
@@ -123,7 +79,7 @@ class KotlinInlineValHandler : InlineActionHandler() {
return showErrorHint(project, editor, "$kind '$name' is never used") return showErrorHint(project, editor, "$kind '$name' is never used")
} }
val assignments = Sets.newHashSet<PsiElement>() val assignments = hashSetOf<PsiElement>()
referenceExpressions.forEach { expression -> referenceExpressions.forEach { expression ->
val parent = expression.parent val parent = expression.parent
@@ -147,9 +103,6 @@ class KotlinInlineValHandler : InlineActionHandler() {
?: return reportAmbiguousAssignment(project, editor, name, assignments) ?: return reportAmbiguousAssignment(project, editor, name, assignments)
} }
val typeArgumentsForCall = getQualifiedTypeArgumentList(initializer)
val parametersForFunctionLiteral = getParametersForFunctionLiteral(initializer)
val referencesInOriginalFile = referenceExpressions.filter { it.containingFile == file } val referencesInOriginalFile = referenceExpressions.filter { it.containingFile == file }
val isHighlighting = referencesInOriginalFile.isNotEmpty() val isHighlighting = referencesInOriginalFile.isNotEmpty()
highlightElements(project, editor, referencesInOriginalFile) highlightElements(project, editor, referencesInOriginalFile)
@@ -158,61 +111,36 @@ class KotlinInlineValHandler : InlineActionHandler() {
preProcessInternalUsages(initializer, referenceExpressions) preProcessInternalUsages(initializer, referenceExpressions)
} }
fun performRefactoring() { val descriptor = element.resolveToDescriptor() as VariableDescriptor
val primaryExpression = if (editor != null) { val expectedType = if (element.typeReference != null)
val offset = editor.caretModel.offset descriptor.returnType ?: TypeUtils.NO_EXPECTED_TYPE
referenceExpressions.firstOrNull { it.textRange.contains(offset) } else
} TypeUtils.NO_EXPECTED_TYPE
else null
val primaryRef = primaryExpression?.mainReference
val inlineMode = showDialog(declaration, primaryRef, referenceExpressions.size) val initializerCopy = initializer.copied()
if (inlineMode == InlineMode.NONE) { fun analyzeInitializerCopy(): BindingContext {
if (isHighlighting) { return initializerCopy.analyzeInContext(initializer.getResolutionScope(),
contextExpression = initializer,
expectedType = expectedType)
}
fun performRefactoring() {
val reference = editor?.let { TargetElementUtil.findReference(it, it.caretModel.offset) } as? KtSimpleNameReference
val replacementBuilder = CodeToInlineBuilder(descriptor, element.getResolutionFacade())
val replacement = replacementBuilder.prepareCodeToInline(initializerCopy, emptyList(), ::analyzeInitializerCopy)
val replacementStrategy = CallableUsageReplacementStrategy(replacement)
val dialog = KotlinInlineValDialog(declaration, reference, replacementStrategy, assignments)
if (!ApplicationManager.getApplication().isUnitTestMode) {
dialog.show()
if (!dialog.isOK && isHighlighting) {
val statusBar = WindowManager.getInstance().getStatusBar(project) val statusBar = WindowManager.getInstance().getStatusBar(project)
statusBar?.info = RefactoringBundle.message("press.escape.to.remove.the.highlighting") statusBar?.info = RefactoringBundle.message("press.escape.to.remove.the.highlighting")
} }
return
} }
else {
val chosenExpressions = if (inlineMode == InlineMode.ALL) referenceExpressions else listOf(primaryExpression) dialog.doAction()
project.executeWriteCommand(RefactoringBundle.message("inline.command", name)) {
val inlinedExpressions = chosenExpressions
.flatMap { referenceExpression ->
if (assignments.contains(referenceExpression!!.parent)) return@flatMap emptyList<KtExpression>()
val importDirective = referenceExpression.getStrictParentOfType<KtImportDirective>()
if (importDirective != null) {
val reference = referenceExpression.getQualifiedElementSelector()?.mainReference
if (reference != null && reference.multiResolve(false).size <= 1) {
importDirective.delete()
}
return@flatMap emptyList<KtExpression>()
}
doReplace(referenceExpression, initializer)
}
.mapNotNull { postProcessInternalReferences(it) }
if (inlineMode == InlineMode.ALL) {
assignments.forEach { it.delete() }
declaration.delete()
}
if (inlinedExpressions.isNotEmpty()) {
if (typeArgumentsForCall != null) {
inlinedExpressions.forEach { addTypeArgumentsIfNeeded(it, typeArgumentsForCall) }
}
parametersForFunctionLiteral?.let { addFunctionLiteralParameterTypes(it, inlinedExpressions) }
if (isHighlighting) {
highlightElements(project, editor, inlinedExpressions)
}
}
performDelayedRefactoringRequests(project)
} }
} }
@@ -238,80 +166,4 @@ class KotlinInlineValHandler : InlineActionHandler() {
CommonRefactoringUtil.showErrorHint(project, editor, message, RefactoringBundle.message("inline.variable.title"), HelpID.INLINE_VARIABLE) CommonRefactoringUtil.showErrorHint(project, editor, message, RefactoringBundle.message("inline.variable.title"), HelpID.INLINE_VARIABLE)
} }
private fun showDialog(property: KtProperty, ref: PsiReference?, occurrenceCount: Int): InlineMode {
if (ApplicationManager.getApplication().isUnitTestMode) return InlineMode.ALL
if ((ref == null || occurrenceCount <= 1) && !EditorSettingsExternalizable.getInstance().isShowInlineLocalDialog) return InlineMode.ALL
val dialog = KotlinInlineValDialog(property, ref, occurrenceCount)
if (!dialog.showAndGet()) return InlineMode.NONE
return if (JavaRefactoringSettings.getInstance().INLINE_LOCAL_THIS) InlineMode.PRIMARY else InlineMode.ALL
}
private fun getParametersForFunctionLiteral(initializer: KtExpression): String? {
val functionLiteralExpression = initializer.unpackFunctionLiteral(true) ?: return null
val context = initializer.analyze(BodyResolveMode.PARTIAL)
val lambdaDescriptor = context.get(BindingContext.FUNCTION, functionLiteralExpression.functionLiteral)
if (lambdaDescriptor == null || ErrorUtils.containsErrorType(lambdaDescriptor)) return null
return lambdaDescriptor.valueParameters.joinToString {
it.name.asString() + ": " + IdeDescriptorRenderers.SOURCE_CODE.renderType(it.type)
}
}
private fun addFunctionLiteralParameterTypes(parameters: String, inlinedExpressions: List<KtExpression>) {
val containingFile = inlinedExpressions.first().containingKtFile
val resolutionFacade = containingFile.getResolutionFacade()
val functionsToAddParameters = inlinedExpressions.mapNotNull {
val lambdaExpr = it.unpackFunctionLiteral(true).sure { "can't find function literal expression for " + it.text }
if (needToAddParameterTypes(lambdaExpr, resolutionFacade)) lambdaExpr else null
}
val psiFactory = KtPsiFactory(containingFile)
for (lambdaExpr in functionsToAddParameters) {
val lambda = lambdaExpr.functionLiteral
val currentParameterList = lambda.valueParameterList
val newParameterList = psiFactory.createParameterList("($parameters)")
if (currentParameterList != null) {
currentParameterList.replace(newParameterList)
}
else {
// TODO: Ugly code, need refactoring
val openBraceElement = lambda.lBrace
val nextSibling = openBraceElement.nextSibling
val whitespaceToAdd = if (nextSibling is PsiWhiteSpace && nextSibling.text.contains("\n"))
nextSibling.copy()
else
null
val whitespaceAndArrow = psiFactory.createWhitespaceAndArrow()
lambda.addRangeAfter(whitespaceAndArrow.first, whitespaceAndArrow.second, openBraceElement)
lambda.addAfter(newParameterList, openBraceElement)
if (whitespaceToAdd != null) {
lambda.addAfter(whitespaceToAdd, openBraceElement)
}
}
ShortenReferences.DEFAULT.process(lambdaExpr.valueParameters)
}
}
private fun needToAddParameterTypes(
lambdaExpression: KtLambdaExpression,
resolutionFacade: ResolutionFacade
): Boolean {
val functionLiteral = lambdaExpression.functionLiteral
val context = resolutionFacade.analyze(lambdaExpression, BodyResolveMode.PARTIAL_WITH_DIAGNOSTICS)
return context.diagnostics.any { diagnostic ->
val factory = diagnostic.factory
val element = diagnostic.psiElement
val hasCantInferParameter = factory == Errors.CANNOT_INFER_PARAMETER_TYPE &&
element.parent.parent == functionLiteral
val hasUnresolvedItOrThis = factory == Errors.UNRESOLVED_REFERENCE &&
element.text == "it" &&
element.getStrictParentOfType<KtFunctionLiteral>() == functionLiteral
hasCantInferParameter || hasUnresolvedItOrThis
}
}
} }
@@ -1,3 +1,3 @@
fun foo() { fun foo() {
val ff = {(it: Int) -> it } val ff = { it: Int -> it }
} }
@@ -1,6 +1,6 @@
fun foo() { fun foo() {
val ff = { val ff = {
(it: Int) -> it: Int ->
it it
} }
} }
@@ -1,3 +1,3 @@
fun foo() { fun foo() {
val ff = (({(x: Int) -> x })) val ff = (({ x: Int -> x }))
} }
@@ -1,3 +1,3 @@
fun foo() { fun foo() {
val ff = {(x: Int) -> x } val ff = { x: Int -> x }
} }
@@ -6,5 +6,6 @@ class Class {
} }
fun f() { fun f() {
Class()
println(239) println(239)
} }
@@ -0,0 +1,5 @@
// ERROR: Cannot perform refactoring.\nVariable length has no initializer
fun foo(s: String) {
val l = s.<caret>length
}
@@ -982,6 +982,12 @@ public class InlineTestGenerated extends AbstractInlineTest {
doTest(fileName); doTest(fileName);
} }
@TestMetadata("Library.kt")
public void testLibrary() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/refactoring/inline/inlineVariableOrProperty/property/Library.kt");
doTest(fileName);
}
@TestMetadata("multiplePackages.kt") @TestMetadata("multiplePackages.kt")
public void testMultiplePackages() throws Exception { public void testMultiplePackages() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/refactoring/inline/inlineVariableOrProperty/property/multiplePackages.kt"); String fileName = KotlinTestUtils.navigationMetadata("idea/testData/refactoring/inline/inlineVariableOrProperty/property/multiplePackages.kt");