From ab1b985bac186e1133684c569e517b5d69a96085 Mon Sep 17 00:00:00 2001 From: Valentin Kipyatkov Date: Tue, 23 May 2017 15:26:04 +0300 Subject: [PATCH] Inline Property supported for properties with setter #KT-2638 Fixed --- .../CallableUsageReplacementStrategy.kt | 5 +- .../kotlin/idea/codeInliner/CodeInliner.kt | 44 +++++++---- .../PropertyUsageReplacementStrategy.kt | 36 +++++++++ .../DeprecatedSymbolUsageFixBase.kt | 6 +- .../inline/KotlinInlineCallableProcessor.kt | 4 +- .../inline/KotlinInlineFunctionDialog.kt | 5 +- .../inline/KotlinInlineFunctionHandler.kt | 1 + .../inline/KotlinInlineValDialog.kt | 4 +- .../inline/KotlinInlineValHandler.kt | 78 ++++++++++--------- .../property/PrivateSet.kt | 8 ++ .../property/PrivateSet.kt.after | 6 ++ .../property/accessors/GetterAndSetter.kt | 10 +++ .../accessors/GetterAndSetter.kt.after | 4 + .../property/accessors/PlusPlus.kt | 10 +++ .../refactoring/inline/AbstractInlineTest.kt | 8 +- .../inline/InlineTestGenerated.java | 18 +++++ 16 files changed, 181 insertions(+), 66 deletions(-) create mode 100644 idea/src/org/jetbrains/kotlin/idea/codeInliner/PropertyUsageReplacementStrategy.kt create mode 100644 idea/testData/refactoring/inline/inlineVariableOrProperty/property/PrivateSet.kt create mode 100644 idea/testData/refactoring/inline/inlineVariableOrProperty/property/PrivateSet.kt.after create mode 100644 idea/testData/refactoring/inline/inlineVariableOrProperty/property/accessors/GetterAndSetter.kt create mode 100644 idea/testData/refactoring/inline/inlineVariableOrProperty/property/accessors/GetterAndSetter.kt.after create mode 100644 idea/testData/refactoring/inline/inlineVariableOrProperty/property/accessors/PlusPlus.kt diff --git a/idea/src/org/jetbrains/kotlin/idea/codeInliner/CallableUsageReplacementStrategy.kt b/idea/src/org/jetbrains/kotlin/idea/codeInliner/CallableUsageReplacementStrategy.kt index 060d81de3d0..4f19c40c401 100644 --- a/idea/src/org/jetbrains/kotlin/idea/codeInliner/CallableUsageReplacementStrategy.kt +++ b/idea/src/org/jetbrains/kotlin/idea/codeInliner/CallableUsageReplacementStrategy.kt @@ -24,10 +24,7 @@ import org.jetbrains.kotlin.resolve.calls.callUtil.getResolvedCall import org.jetbrains.kotlin.resolve.calls.model.VariableAsFunctionResolvedCall import org.jetbrains.kotlin.resolve.lazy.BodyResolveMode -class CallableUsageReplacementStrategy( - private val replacement: CodeToInline -) : UsageReplacementStrategy { - +class CallableUsageReplacementStrategy(private val replacement: CodeToInline) : UsageReplacementStrategy { override fun createReplacer(usage: KtSimpleNameExpression): (() -> KtElement?)? { val bindingContext = usage.analyze(BodyResolveMode.PARTIAL) val resolvedCall = usage.getResolvedCall(bindingContext) ?: return null diff --git a/idea/src/org/jetbrains/kotlin/idea/codeInliner/CodeInliner.kt b/idea/src/org/jetbrains/kotlin/idea/codeInliner/CodeInliner.kt index 6689e0c3e78..7a252a816c9 100644 --- a/idea/src/org/jetbrains/kotlin/idea/codeInliner/CodeInliner.kt +++ b/idea/src/org/jetbrains/kotlin/idea/codeInliner/CodeInliner.kt @@ -21,6 +21,8 @@ import com.intellij.psi.PsiElement import com.intellij.refactoring.rename.RenameProcessor import org.jetbrains.kotlin.builtins.KotlinBuiltIns import org.jetbrains.kotlin.descriptors.CallableDescriptor +import org.jetbrains.kotlin.descriptors.PropertyDescriptor +import org.jetbrains.kotlin.descriptors.PropertySetterDescriptor import org.jetbrains.kotlin.descriptors.ValueParameterDescriptor import org.jetbrains.kotlin.idea.caches.resolve.analyze import org.jetbrains.kotlin.idea.caches.resolve.getResolutionFacade @@ -63,10 +65,15 @@ class CodeInliner( val descriptor = resolvedCall.resultingDescriptor val file = nameExpression.containingKtFile - val elementToBeReplaced = when (callElement) { - is KtExpression -> callElement.getQualifiedExpressionForSelectorOrThis() - else -> callElement - } + val qualifiedElement = if (callElement is KtExpression) callElement.getQualifiedExpressionForSelectorOrThis() else callElement + val assignment = (qualifiedElement as? KtExpression) + ?.getAssignmentByLHS() + ?.takeIf { it.operationToken == KtTokens.EQ } + val elementToBeReplaced = assignment ?: qualifiedElement + val callableForParameters = if (assignment != null && descriptor is PropertyDescriptor) + descriptor.setter ?: descriptor + else + descriptor val commentSaver = CommentSaver(elementToBeReplaced, saveLineBreaks = true) @@ -103,11 +110,10 @@ class CodeInliner( } } - val introduceValuesForParameters = processValueParameterUsages() + val introduceValuesForParameters = processValueParameterUsages(callableForParameters) processTypeParameterUsages() - val lexicalScope = callElement.parent.getResolutionScope(bindingContext) if (elementToBeReplaced is KtSafeQualifiedExpression) { @@ -164,13 +170,12 @@ class CodeInliner( } } - private fun processValueParameterUsages(): Collection { + private fun processValueParameterUsages(descriptor: CallableDescriptor): Collection { val introduceValuesForParameters = ArrayList() // process parameters in reverse order because default values can use previous parameters - val parameters = resolvedCall.resultingDescriptor.valueParameters - for (parameter in parameters.asReversed()) { - val argument = argumentForParameter(parameter) ?: continue + for (parameter in descriptor.valueParameters.asReversed()) { + val argument = argumentForParameter(parameter, descriptor) ?: continue argument.expression.put(PARAMETER_VALUE_KEY, parameter) @@ -329,7 +334,15 @@ class CodeInliner( val isNamed: Boolean = false, val isDefaultValue: Boolean = false) - private fun argumentForParameter(parameter: ValueParameterDescriptor): Argument? { + private fun argumentForParameter(parameter: ValueParameterDescriptor, callableDescriptor: CallableDescriptor): Argument? { + if (callableDescriptor is PropertySetterDescriptor) { + val valueAssigned = (callElement as? KtExpression) + ?.getQualifiedExpressionForSelectorOrThis() + ?.getAssignmentByLHS() + ?.right ?: return null + return Argument(valueAssigned, bindingContext.getType(valueAssigned)) + } + val resolvedArgument = resolvedCall.valueArguments[parameter]!! when (resolvedArgument) { is ExpressionValueArgument -> { @@ -343,19 +356,18 @@ class CodeInliner( } is DefaultValueArgument -> { - val defaultValue = OptionalParametersHelper.defaultParameterValue(parameter, project) ?: return null - val (expression, parameterUsages) = defaultValue + val (defaultValue, parameterUsages) = OptionalParametersHelper.defaultParameterValue(parameter, project) ?: return null for ((param, usages) in parameterUsages) { usages.forEach { it.put(CodeToInline.PARAMETER_USAGE_KEY, param.name) } } - val expressionCopy = expression.copied() + val defaultValueCopy = defaultValue.copied() // clean up user data in original - expression.forEachDescendantOfType { it.clear(CodeToInline.PARAMETER_USAGE_KEY) } + defaultValue.forEachDescendantOfType { it.clear(CodeToInline.PARAMETER_USAGE_KEY) } - return Argument(expressionCopy, null/*TODO*/, isDefaultValue = true) + return Argument(defaultValueCopy, null/*TODO*/, isDefaultValue = true) } is VarargValueArgument -> { diff --git a/idea/src/org/jetbrains/kotlin/idea/codeInliner/PropertyUsageReplacementStrategy.kt b/idea/src/org/jetbrains/kotlin/idea/codeInliner/PropertyUsageReplacementStrategy.kt new file mode 100644 index 00000000000..356fbce6caf --- /dev/null +++ b/idea/src/org/jetbrains/kotlin/idea/codeInliner/PropertyUsageReplacementStrategy.kt @@ -0,0 +1,36 @@ +/* + * Copyright 2010-2017 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.codeInliner + +import org.jetbrains.kotlin.idea.references.ReferenceAccess +import org.jetbrains.kotlin.idea.references.readWriteAccess +import org.jetbrains.kotlin.psi.KtElement +import org.jetbrains.kotlin.psi.KtSimpleNameExpression + +class PropertyUsageReplacementStrategy(readReplacement: CodeToInline?, writeReplacement: CodeToInline?) : UsageReplacementStrategy { + private val readReplacementStrategy = readReplacement?.let { CallableUsageReplacementStrategy(it) } + private val writeReplacementStrategy = writeReplacement?.let { CallableUsageReplacementStrategy(it) } + + override fun createReplacer(usage: KtSimpleNameExpression): (() -> KtElement?)? { + val access = usage.readWriteAccess(useResolveForReadWrite = true) + return when (access) { + ReferenceAccess.READ -> readReplacementStrategy?.createReplacer(usage) + ReferenceAccess.WRITE -> writeReplacementStrategy?.createReplacer(usage) + ReferenceAccess.READ_WRITE -> null + } + } +} \ No newline at end of file diff --git a/idea/src/org/jetbrains/kotlin/idea/quickfix/replaceWith/DeprecatedSymbolUsageFixBase.kt b/idea/src/org/jetbrains/kotlin/idea/quickfix/replaceWith/DeprecatedSymbolUsageFixBase.kt index a667f1ee43a..93c6545cf6a 100644 --- a/idea/src/org/jetbrains/kotlin/idea/quickfix/replaceWith/DeprecatedSymbolUsageFixBase.kt +++ b/idea/src/org/jetbrains/kotlin/idea/quickfix/replaceWith/DeprecatedSymbolUsageFixBase.kt @@ -28,12 +28,12 @@ import org.jetbrains.kotlin.diagnostics.Diagnostic import org.jetbrains.kotlin.diagnostics.DiagnosticFactory import org.jetbrains.kotlin.diagnostics.Errors import org.jetbrains.kotlin.idea.caches.resolve.getResolutionFacade -import org.jetbrains.kotlin.idea.core.OptionalParametersHelper -import org.jetbrains.kotlin.idea.quickfix.KotlinQuickFixAction -import org.jetbrains.kotlin.idea.references.mainReference import org.jetbrains.kotlin.idea.codeInliner.CallableUsageReplacementStrategy import org.jetbrains.kotlin.idea.codeInliner.ClassUsageReplacementStrategy import org.jetbrains.kotlin.idea.codeInliner.UsageReplacementStrategy +import org.jetbrains.kotlin.idea.core.OptionalParametersHelper +import org.jetbrains.kotlin.idea.quickfix.KotlinQuickFixAction +import org.jetbrains.kotlin.idea.references.mainReference import org.jetbrains.kotlin.psi.KtConstructorCalleeExpression import org.jetbrains.kotlin.psi.KtFile import org.jetbrains.kotlin.psi.KtSimpleNameExpression diff --git a/idea/src/org/jetbrains/kotlin/idea/refactoring/inline/KotlinInlineCallableProcessor.kt b/idea/src/org/jetbrains/kotlin/idea/refactoring/inline/KotlinInlineCallableProcessor.kt index 1183cf401c8..c3d0612749a 100644 --- a/idea/src/org/jetbrains/kotlin/idea/refactoring/inline/KotlinInlineCallableProcessor.kt +++ b/idea/src/org/jetbrains/kotlin/idea/refactoring/inline/KotlinInlineCallableProcessor.kt @@ -26,7 +26,7 @@ import com.intellij.refactoring.util.CommonRefactoringUtil import com.intellij.usageView.UsageInfo import com.intellij.usageView.UsageViewBundle import com.intellij.usageView.UsageViewDescriptor -import org.jetbrains.kotlin.idea.codeInliner.CallableUsageReplacementStrategy +import org.jetbrains.kotlin.idea.codeInliner.UsageReplacementStrategy import org.jetbrains.kotlin.idea.codeInliner.replaceUsages import org.jetbrains.kotlin.idea.references.KtSimpleNameReference import org.jetbrains.kotlin.idea.stubindex.KotlinSourceFilterScope @@ -35,7 +35,7 @@ import org.jetbrains.kotlin.psi.* class KotlinInlineCallableProcessor( project: Project, - private val replacementStrategy: CallableUsageReplacementStrategy, + private val replacementStrategy: UsageReplacementStrategy, private val declaration: KtCallableDeclaration, private val reference: KtSimpleNameReference?, private val inlineThisOnly: Boolean, diff --git a/idea/src/org/jetbrains/kotlin/idea/refactoring/inline/KotlinInlineFunctionDialog.kt b/idea/src/org/jetbrains/kotlin/idea/refactoring/inline/KotlinInlineFunctionDialog.kt index 0e022bdeb8e..a62cf22f4c9 100644 --- a/idea/src/org/jetbrains/kotlin/idea/refactoring/inline/KotlinInlineFunctionDialog.kt +++ b/idea/src/org/jetbrains/kotlin/idea/refactoring/inline/KotlinInlineFunctionDialog.kt @@ -21,7 +21,8 @@ import com.intellij.openapi.project.Project import com.intellij.refactoring.HelpID import com.intellij.refactoring.JavaRefactoringSettings import com.intellij.refactoring.inline.InlineOptionsDialog -import org.jetbrains.kotlin.idea.codeInliner.CallableUsageReplacementStrategy +import org.jetbrains.kotlin.idea.codeInliner.PropertyUsageReplacementStrategy +import org.jetbrains.kotlin.idea.codeInliner.UsageReplacementStrategy import org.jetbrains.kotlin.idea.quickfix.createFromUsage.callableBuilder.getReturnTypeReference import org.jetbrains.kotlin.idea.references.KtSimpleNameReference import org.jetbrains.kotlin.psi.KtConstructor @@ -32,7 +33,7 @@ class KotlinInlineFunctionDialog( project: Project, private val function: KtNamedFunction, private val reference: KtSimpleNameReference?, - private val replacementStrategy: CallableUsageReplacementStrategy, + private val replacementStrategy: UsageReplacementStrategy, private val allowInlineThisOnly: Boolean ) : InlineOptionsDialog(project, true, function) { diff --git a/idea/src/org/jetbrains/kotlin/idea/refactoring/inline/KotlinInlineFunctionHandler.kt b/idea/src/org/jetbrains/kotlin/idea/refactoring/inline/KotlinInlineFunctionHandler.kt index c300ef49dbd..36a41757d98 100644 --- a/idea/src/org/jetbrains/kotlin/idea/refactoring/inline/KotlinInlineFunctionHandler.kt +++ b/idea/src/org/jetbrains/kotlin/idea/refactoring/inline/KotlinInlineFunctionHandler.kt @@ -32,6 +32,7 @@ import org.jetbrains.kotlin.idea.KotlinLanguage import org.jetbrains.kotlin.idea.caches.resolve.analyzeFully import org.jetbrains.kotlin.idea.caches.resolve.resolveToDescriptor import org.jetbrains.kotlin.idea.codeInliner.CallableUsageReplacementStrategy +import org.jetbrains.kotlin.idea.codeInliner.PropertyUsageReplacementStrategy import org.jetbrains.kotlin.idea.references.KtSimpleNameReference import org.jetbrains.kotlin.psi.KtExpression import org.jetbrains.kotlin.psi.KtNamedFunction diff --git a/idea/src/org/jetbrains/kotlin/idea/refactoring/inline/KotlinInlineValDialog.kt b/idea/src/org/jetbrains/kotlin/idea/refactoring/inline/KotlinInlineValDialog.kt index 6bf7bd19c47..cd1220c2751 100644 --- a/idea/src/org/jetbrains/kotlin/idea/refactoring/inline/KotlinInlineValDialog.kt +++ b/idea/src/org/jetbrains/kotlin/idea/refactoring/inline/KotlinInlineValDialog.kt @@ -19,7 +19,7 @@ package org.jetbrains.kotlin.idea.refactoring.inline import com.intellij.openapi.util.text.StringUtil import com.intellij.refactoring.JavaRefactoringSettings import com.intellij.refactoring.inline.InlineOptionsDialog -import org.jetbrains.kotlin.idea.codeInliner.CallableUsageReplacementStrategy +import org.jetbrains.kotlin.idea.codeInliner.UsageReplacementStrategy import org.jetbrains.kotlin.idea.references.KtSimpleNameReference import org.jetbrains.kotlin.psi.KtBinaryExpression import org.jetbrains.kotlin.psi.KtProperty @@ -27,7 +27,7 @@ import org.jetbrains.kotlin.psi.KtProperty class KotlinInlineValDialog( private val property: KtProperty, private val reference: KtSimpleNameReference?, - private val replacementStrategy: CallableUsageReplacementStrategy, + private val replacementStrategy: UsageReplacementStrategy, private val assignmentToDelete: KtBinaryExpression? ) : InlineOptionsDialog(property.project, true, property) { diff --git a/idea/src/org/jetbrains/kotlin/idea/refactoring/inline/KotlinInlineValHandler.kt b/idea/src/org/jetbrains/kotlin/idea/refactoring/inline/KotlinInlineValHandler.kt index 2792faf9cac..4ebdb47f1ce 100644 --- a/idea/src/org/jetbrains/kotlin/idea/refactoring/inline/KotlinInlineValHandler.kt +++ b/idea/src/org/jetbrains/kotlin/idea/refactoring/inline/KotlinInlineValHandler.kt @@ -35,9 +35,9 @@ import org.jetbrains.kotlin.idea.KotlinLanguage import org.jetbrains.kotlin.idea.analysis.analyzeInContext import org.jetbrains.kotlin.idea.caches.resolve.getResolutionFacade import org.jetbrains.kotlin.idea.caches.resolve.resolveToDescriptor -import org.jetbrains.kotlin.idea.codeInliner.CallableUsageReplacementStrategy import org.jetbrains.kotlin.idea.codeInliner.CodeToInline import org.jetbrains.kotlin.idea.codeInliner.CodeToInlineBuilder +import org.jetbrains.kotlin.idea.codeInliner.PropertyUsageReplacementStrategy import org.jetbrains.kotlin.idea.core.copied import org.jetbrains.kotlin.idea.refactoring.checkConflictsInteractively import org.jetbrains.kotlin.idea.references.KtSimpleNameReference @@ -53,7 +53,6 @@ import org.jetbrains.kotlin.psi.psiUtil.getAssignmentByLHS import org.jetbrains.kotlin.psi.psiUtil.getQualifiedExpressionForSelectorOrThis import org.jetbrains.kotlin.resolve.BindingContext import org.jetbrains.kotlin.types.TypeUtils -import org.jetbrains.kotlin.utils.addIfNotNull class KotlinInlineValHandler : InlineActionHandler() { @@ -75,17 +74,11 @@ class KotlinInlineValHandler : InlineActionHandler() { val getter = declaration.getter?.takeIf { it.hasBody() } val setter = declaration.setter?.takeIf { it.hasBody() } - if (getter != null || setter != null) { - if (declaration.initializer != null) { - return showErrorHint(project, editor, "Cannot inline property with accessor(s) and backing field") - } - - if (setter != null) { - return showErrorHint(project, editor, "Inline property not supported for properties with setter") - } + if ((getter != null || setter != null) && declaration.initializer != null) { + return showErrorHint(project, editor, "Cannot inline property with accessor(s) and backing field") } - val (referenceExpressions, foreignUsages) = findUsages(declaration) + val (referenceExpressions, conflicts) = findUsages(declaration) if (referenceExpressions.isEmpty()) { val kind = if (declaration.isLocal) "Variable" else "Property" @@ -93,49 +86,61 @@ class KotlinInlineValHandler : InlineActionHandler() { } val referencesInOriginalFile = referenceExpressions.filter { it.containingFile == file } - val isHighlighting = referencesInOriginalFile.isNotEmpty() + val hasHighlightings = referencesInOriginalFile.isNotEmpty() highlightElements(project, editor, referencesInOriginalFile) - val codeToInline: CodeToInline - val assignment: KtBinaryExpression? - if (getter == null) { + val readReplacement: CodeToInline? + val writeReplacement: CodeToInline? + val assignmentToDelete: KtBinaryExpression? + if (getter == null && setter == null) { val initialization = extractInitialization(declaration, referenceExpressions, project, editor) ?: return - codeToInline = buildCodeToInline(declaration, initialization.value) - assignment = initialization.assignment + readReplacement = buildCodeToInline(declaration, initialization.value) + writeReplacement = null + assignmentToDelete = initialization.assignment } else { val descriptor = declaration.resolveToDescriptor() as PropertyDescriptor - codeToInline = buildCodeToInline(getter, descriptor.type, editor) ?: return - assignment = null + readReplacement = getter?.let { buildCodeToInline(getter, descriptor.type, editor) ?: return } + writeReplacement = setter?.let { buildCodeToInline(setter, descriptor.type, editor) ?: return } + assignmentToDelete = null } - if (foreignUsages.isNotEmpty()) { - val conflicts = MultiMap().apply { - putValue(null, "Property '$name' has non-Kotlin usages. They won't be processed by the Inline refactoring.") - foreignUsages.forEach { putValue(it, it.text) } + if (!conflicts.isEmpty) { + val conflictsCopy = conflicts.copy() + conflictsCopy.putValue(null, "The following usages are not supported by the Inline refactoring. They won't be processed.") + + project.checkConflictsInteractively(conflictsCopy) { + performRefactoring(declaration, readReplacement, writeReplacement, assignmentToDelete, editor, hasHighlightings) } - project.checkConflictsInteractively(conflicts) { performRefactoring(declaration, codeToInline, editor, assignment, isHighlighting) } } else { - performRefactoring(declaration, codeToInline, editor, assignment, isHighlighting) + performRefactoring(declaration, readReplacement, writeReplacement, assignmentToDelete, editor, hasHighlightings) } } - private data class Usages(val referenceExpressions: Collection, val foreignUsages: Collection) + private data class Usages(val referenceExpressions: Collection, val conflicts: MultiMap) private fun findUsages(declaration: KtProperty): Usages { val references = ReferencesSearch.search(declaration) val referenceExpressions = mutableListOf() - val foreignUsages = mutableListOf() + val conflictUsages = MultiMap.create() for (ref in references) { val refElement = ref.element ?: continue if (refElement !is KtElement) { - foreignUsages.add(refElement) + conflictUsages.putValue(refElement, "Non-Kotlin usage: ${refElement.text}") continue } - referenceExpressions.addIfNotNull((refElement as? KtExpression)?.getQualifiedExpressionForSelectorOrThis()) + + val expression = (refElement as? KtExpression)?.getQualifiedExpressionForSelectorOrThis() + //TODO: what if null? + if (expression != null) { + if (expression.readWriteAccess(useResolveForReadWrite = true) == ReferenceAccess.READ_WRITE) { + conflictUsages.putValue(expression, "Unsupported usage: ${expression.parent.text}") + } + referenceExpressions.add(expression) + } } - return Usages(referenceExpressions, foreignUsages) + return Usages(referenceExpressions, conflictUsages) } private data class Initialization(val value: KtExpression, val assignment: KtBinaryExpression?) @@ -189,20 +194,21 @@ class KotlinInlineValHandler : InlineActionHandler() { private fun performRefactoring( declaration: KtProperty, - replacement: CodeToInline, + readReplacement: CodeToInline?, + writeReplacement: CodeToInline?, + assignmentToDelete: KtBinaryExpression?, editor: Editor?, - assignment: KtBinaryExpression?, - isHighlighting: Boolean + hasHighlightings: Boolean ) { - val replacementStrategy = CallableUsageReplacementStrategy(replacement) + val replacementStrategy = PropertyUsageReplacementStrategy(readReplacement, writeReplacement) val reference = editor?.let { TargetElementUtil.findReference(it, it.caretModel.offset) } as? KtSimpleNameReference - val dialog = KotlinInlineValDialog(declaration, reference, replacementStrategy, assignment) + val dialog = KotlinInlineValDialog(declaration, reference, replacementStrategy, assignmentToDelete) if (!ApplicationManager.getApplication().isUnitTestMode) { dialog.show() - if (!dialog.isOK && isHighlighting) { + if (!dialog.isOK && hasHighlightings) { val statusBar = WindowManager.getInstance().getStatusBar(declaration.project) statusBar?.info = RefactoringBundle.message("press.escape.to.remove.the.highlighting") } diff --git a/idea/testData/refactoring/inline/inlineVariableOrProperty/property/PrivateSet.kt b/idea/testData/refactoring/inline/inlineVariableOrProperty/property/PrivateSet.kt new file mode 100644 index 00000000000..19376024137 --- /dev/null +++ b/idea/testData/refactoring/inline/inlineVariableOrProperty/property/PrivateSet.kt @@ -0,0 +1,8 @@ +class A { + var property = 10 + private set + + fun f() { + println(property) + } +} diff --git a/idea/testData/refactoring/inline/inlineVariableOrProperty/property/PrivateSet.kt.after b/idea/testData/refactoring/inline/inlineVariableOrProperty/property/PrivateSet.kt.after new file mode 100644 index 00000000000..e0d26886313 --- /dev/null +++ b/idea/testData/refactoring/inline/inlineVariableOrProperty/property/PrivateSet.kt.after @@ -0,0 +1,6 @@ +class A { + + fun f() { + println(10) + } +} diff --git a/idea/testData/refactoring/inline/inlineVariableOrProperty/property/accessors/GetterAndSetter.kt b/idea/testData/refactoring/inline/inlineVariableOrProperty/property/accessors/GetterAndSetter.kt new file mode 100644 index 00000000000..b1a160b5641 --- /dev/null +++ b/idea/testData/refactoring/inline/inlineVariableOrProperty/property/accessors/GetterAndSetter.kt @@ -0,0 +1,10 @@ +var Int.property: Int + get() = this + set(value) { + println("Set value of $value for $this") + } + +fun foo() { + println(1.property) + 2.property = 10 +} \ No newline at end of file diff --git a/idea/testData/refactoring/inline/inlineVariableOrProperty/property/accessors/GetterAndSetter.kt.after b/idea/testData/refactoring/inline/inlineVariableOrProperty/property/accessors/GetterAndSetter.kt.after new file mode 100644 index 00000000000..919ee770e98 --- /dev/null +++ b/idea/testData/refactoring/inline/inlineVariableOrProperty/property/accessors/GetterAndSetter.kt.after @@ -0,0 +1,4 @@ +fun foo() { + println(1) + println("Set value of ${10} for ${2}") +} \ No newline at end of file diff --git a/idea/testData/refactoring/inline/inlineVariableOrProperty/property/accessors/PlusPlus.kt b/idea/testData/refactoring/inline/inlineVariableOrProperty/property/accessors/PlusPlus.kt new file mode 100644 index 00000000000..93af6c8609a --- /dev/null +++ b/idea/testData/refactoring/inline/inlineVariableOrProperty/property/accessors/PlusPlus.kt @@ -0,0 +1,10 @@ +// ERROR: The following usages are not supported by the Inline refactoring. They won't be processed.\nUnsupported usage: property++ + +var property: Int + get() = 1 + set(value) { + } + +fun foo() { + property++ +} \ No newline at end of file diff --git a/idea/tests/org/jetbrains/kotlin/idea/refactoring/inline/AbstractInlineTest.kt b/idea/tests/org/jetbrains/kotlin/idea/refactoring/inline/AbstractInlineTest.kt index 9e85fc59cbc..fbb61137569 100644 --- a/idea/tests/org/jetbrains/kotlin/idea/refactoring/inline/AbstractInlineTest.kt +++ b/idea/tests/org/jetbrains/kotlin/idea/refactoring/inline/AbstractInlineTest.kt @@ -22,6 +22,7 @@ import com.intellij.codeInsight.TargetElementUtil.REFERENCED_ELEMENT_ACCEPTED import com.intellij.lang.refactoring.InlineActionHandler import com.intellij.openapi.extensions.Extensions import com.intellij.openapi.util.io.FileUtil +import com.intellij.refactoring.BaseRefactoringProcessor import com.intellij.refactoring.util.CommonRefactoringUtil import com.intellij.testFramework.UsefulTestCase import com.intellij.testFramework.fixtures.JavaCodeInsightTestFixture @@ -69,9 +70,14 @@ abstract class AbstractInlineTest : KotlinLightCodeInsightFixtureTestCase() { TestCase.assertEquals("Expected errors", 1, expectedErrors.size) TestCase.assertEquals("Error message", expectedErrors[0].replace("\\n", "\n"), e.message) } + catch (e: BaseRefactoringProcessor.ConflictsInTestsException) { + TestCase.assertFalse("Conflicts: ${e.message}", afterFileExists) + TestCase.assertEquals("Expected errors", 1, expectedErrors.size) + TestCase.assertEquals("Error message", expectedErrors[0].replace("\\n", "\n"), e.message) + } } else { - TestCase.assertFalse(afterFileExists) + TestCase.assertFalse("No refactoring handler available", afterFileExists) } } diff --git a/idea/tests/org/jetbrains/kotlin/idea/refactoring/inline/InlineTestGenerated.java b/idea/tests/org/jetbrains/kotlin/idea/refactoring/inline/InlineTestGenerated.java index cd9663311ea..67c9651a79e 100644 --- a/idea/tests/org/jetbrains/kotlin/idea/refactoring/inline/InlineTestGenerated.java +++ b/idea/tests/org/jetbrains/kotlin/idea/refactoring/inline/InlineTestGenerated.java @@ -1030,6 +1030,12 @@ public class InlineTestGenerated extends AbstractInlineTest { doTest(fileName); } + @TestMetadata("PrivateSet.kt") + public void testPrivateSet() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("idea/testData/refactoring/inline/inlineVariableOrProperty/property/PrivateSet.kt"); + doTest(fileName); + } + @TestMetadata("QualifiedUsage.kt") public void testQualifiedUsage() throws Exception { String fileName = KotlinTestUtils.navigationMetadata("idea/testData/refactoring/inline/inlineVariableOrProperty/property/QualifiedUsage.kt"); @@ -1068,6 +1074,18 @@ public class InlineTestGenerated extends AbstractInlineTest { doTest(fileName); } + @TestMetadata("GetterAndSetter.kt") + public void testGetterAndSetter() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("idea/testData/refactoring/inline/inlineVariableOrProperty/property/accessors/GetterAndSetter.kt"); + doTest(fileName); + } + + @TestMetadata("PlusPlus.kt") + public void testPlusPlus() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("idea/testData/refactoring/inline/inlineVariableOrProperty/property/accessors/PlusPlus.kt"); + doTest(fileName); + } + @TestMetadata("WithInitializer.kt") public void testWithInitializer() throws Exception { String fileName = KotlinTestUtils.navigationMetadata("idea/testData/refactoring/inline/inlineVariableOrProperty/property/accessors/WithInitializer.kt");