From e6bfa555344a36b12128a4f8e23067ea375064c3 Mon Sep 17 00:00:00 2001 From: Valentin Kipyatkov Date: Tue, 23 May 2017 10:42:57 +0300 Subject: [PATCH] Inline Property also supported for properties with getter --- .../inline/KotlinInlineFunctionHandler.kt | 57 +------- .../inline/KotlinInlineValHandler.kt | 134 ++++++++++++------ .../idea/refactoring/inline/inlineUtils.kt | 64 ++++++++- .../property/ExtensionProperty.kt | 7 - .../property/WithGetter.kt | 8 -- .../property/WithInitializerAndGetter.kt | 9 -- .../property/accessors/BlockBody.kt | 9 ++ .../property/accessors/BlockBody.kt.after | 4 + .../property/accessors/ExpressionBody.kt | 8 ++ .../accessors/ExpressionBody.kt.after | 5 + .../property/accessors/ExtensionProperty.kt | 7 + .../accessors/ExtensionProperty.kt.after | 4 + .../property/accessors/WithInitializer.kt | 8 ++ .../refactoring/inline/AbstractInlineTest.kt | 6 +- .../inline/InlineTestGenerated.java | 45 ++++-- 15 files changed, 233 insertions(+), 142 deletions(-) delete mode 100644 idea/testData/refactoring/inline/inlineVariableOrProperty/property/ExtensionProperty.kt delete mode 100644 idea/testData/refactoring/inline/inlineVariableOrProperty/property/WithGetter.kt delete mode 100644 idea/testData/refactoring/inline/inlineVariableOrProperty/property/WithInitializerAndGetter.kt create mode 100644 idea/testData/refactoring/inline/inlineVariableOrProperty/property/accessors/BlockBody.kt create mode 100644 idea/testData/refactoring/inline/inlineVariableOrProperty/property/accessors/BlockBody.kt.after create mode 100644 idea/testData/refactoring/inline/inlineVariableOrProperty/property/accessors/ExpressionBody.kt create mode 100644 idea/testData/refactoring/inline/inlineVariableOrProperty/property/accessors/ExpressionBody.kt.after create mode 100644 idea/testData/refactoring/inline/inlineVariableOrProperty/property/accessors/ExtensionProperty.kt create mode 100644 idea/testData/refactoring/inline/inlineVariableOrProperty/property/accessors/ExtensionProperty.kt.after create mode 100644 idea/testData/refactoring/inline/inlineVariableOrProperty/property/accessors/WithInitializer.kt 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 d3f85f53726..c300ef49dbd 100644 --- a/idea/src/org/jetbrains/kotlin/idea/refactoring/inline/KotlinInlineFunctionHandler.kt +++ b/idea/src/org/jetbrains/kotlin/idea/refactoring/inline/KotlinInlineFunctionHandler.kt @@ -29,24 +29,15 @@ import com.intellij.refactoring.util.CommonRefactoringUtil import org.jetbrains.kotlin.descriptors.FunctionDescriptor import org.jetbrains.kotlin.descriptors.SimpleFunctionDescriptor import org.jetbrains.kotlin.idea.KotlinLanguage -import org.jetbrains.kotlin.idea.analysis.analyzeInContext import org.jetbrains.kotlin.idea.caches.resolve.analyzeFully -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.CodeToInlineBuilder -import org.jetbrains.kotlin.idea.core.copied import org.jetbrains.kotlin.idea.references.KtSimpleNameReference -import org.jetbrains.kotlin.idea.util.getResolutionScope -import org.jetbrains.kotlin.psi.KtBlockExpression import org.jetbrains.kotlin.psi.KtExpression import org.jetbrains.kotlin.psi.KtNamedFunction -import org.jetbrains.kotlin.psi.KtReturnExpression import org.jetbrains.kotlin.psi.psiUtil.anyDescendantOfType -import org.jetbrains.kotlin.psi.psiUtil.collectDescendantsOfType import org.jetbrains.kotlin.resolve.BindingContext import org.jetbrains.kotlin.resolve.calls.callUtil.getResolvedCall -import org.jetbrains.kotlin.types.TypeUtils import org.jetbrains.kotlin.utils.addToStdlib.firstIsInstanceOrNull class KotlinInlineFunctionHandler: InlineActionHandler() { @@ -58,49 +49,6 @@ class KotlinInlineFunctionHandler: InlineActionHandler() { override fun inlineElement(project: Project, editor: Editor?, element: PsiElement) { element as KtNamedFunction - val descriptor = element.resolveToDescriptor() as SimpleFunctionDescriptor - val bodyExpression = element.bodyExpression!! - val bodyCopy = bodyExpression.copied() - - val expectedType = if (!element.hasBlockBody() && element.hasDeclaredReturnType()) - descriptor.returnType ?: TypeUtils.NO_EXPECTED_TYPE - else - TypeUtils.NO_EXPECTED_TYPE - - fun analyzeBodyCopy(): BindingContext { - return bodyCopy.analyzeInContext(bodyExpression.getResolutionScope(), - contextExpression = bodyExpression, - expectedType = expectedType) - } - - val replacementBuilder = CodeToInlineBuilder(descriptor, element.getResolutionFacade()) - val replacement = if (element.hasBlockBody()) { - bodyCopy as KtBlockExpression - val statements = bodyCopy.statements - - val returnStatements = bodyCopy.collectDescendantsOfType { - it.getLabelName().let { it == null || it == element.name } - } - - val lastReturn = statements.lastOrNull() as? KtReturnExpression - if (returnStatements.any { it != lastReturn }) { - val message = RefactoringBundle.getCannotRefactorMessage( - if (returnStatements.size > 1) - "Inline Function is not supported for functions with multiple return statements." - else - "Inline Function is not supported for functions with return statements not at the end of the body." - ) - CommonRefactoringUtil.showErrorHint(project, editor, message, "Inline Function", null) - return - } - - replacementBuilder.prepareCodeToInline(lastReturn?.returnedExpression, - statements.dropLast(returnStatements.size), ::analyzeBodyCopy) - } - else { - replacementBuilder.prepareCodeToInline(bodyCopy, emptyList(), ::analyzeBodyCopy) - } - val reference = editor?.let { TargetElementUtil.findReference(it, it.caretModel.offset) } val nameReference = when (reference) { is KtSimpleNameReference -> reference @@ -114,7 +62,10 @@ class KotlinInlineFunctionHandler: InlineActionHandler() { return } - val replacementStrategy = CallableUsageReplacementStrategy(replacement) + val descriptor = element.resolveToDescriptor() as SimpleFunctionDescriptor + val codeToInline = buildCodeToInline(element, descriptor.returnType, editor) ?: return + + val replacementStrategy = CallableUsageReplacementStrategy(codeToInline) val dialog = KotlinInlineFunctionDialog(project, element, nameReference, replacementStrategy, allowInlineThisOnly = recursive) 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 c49813883be..2792faf9cac 100644 --- a/idea/src/org/jetbrains/kotlin/idea/refactoring/inline/KotlinInlineValHandler.kt +++ b/idea/src/org/jetbrains/kotlin/idea/refactoring/inline/KotlinInlineValHandler.kt @@ -29,12 +29,14 @@ import com.intellij.refactoring.HelpID import com.intellij.refactoring.RefactoringBundle import com.intellij.refactoring.util.CommonRefactoringUtil import com.intellij.util.containers.MultiMap +import org.jetbrains.kotlin.descriptors.PropertyDescriptor import org.jetbrains.kotlin.descriptors.VariableDescriptor 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.core.copied import org.jetbrains.kotlin.idea.refactoring.checkConflictsInteractively @@ -58,18 +60,70 @@ class KotlinInlineValHandler : InlineActionHandler() { override fun isEnabledForLanguage(l: Language) = l == KotlinLanguage.INSTANCE override fun canInlineElement(element: PsiElement): Boolean { - if (element !is KtProperty) return false - return element.getter == null && element.receiverTypeReference == null + return element is KtProperty && element.name != null } override fun inlineElement(project: Project, editor: Editor?, element: PsiElement) { val declaration = element as KtProperty + val name = declaration.name!! + val file = declaration.containingKtFile - val name = declaration.name ?: return if (file.isCompiled) { return showErrorHint(project, editor, "Cannot inline '$name' from a decompiled file") } + 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") + } + } + + val (referenceExpressions, foreignUsages) = findUsages(declaration) + + if (referenceExpressions.isEmpty()) { + val kind = if (declaration.isLocal) "Variable" else "Property" + return showErrorHint(project, editor, "$kind '$name' is never used") //TODO: foreign usages! + } + + val referencesInOriginalFile = referenceExpressions.filter { it.containingFile == file } + val isHighlighting = referencesInOriginalFile.isNotEmpty() + highlightElements(project, editor, referencesInOriginalFile) + + val codeToInline: CodeToInline + val assignment: KtBinaryExpression? + if (getter == null) { + val initialization = extractInitialization(declaration, referenceExpressions, project, editor) ?: return + codeToInline = buildCodeToInline(declaration, initialization.value) + assignment = initialization.assignment + } + else { + val descriptor = declaration.resolveToDescriptor() as PropertyDescriptor + codeToInline = buildCodeToInline(getter, descriptor.type, editor) ?: return + assignment = 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) } + } + project.checkConflictsInteractively(conflicts) { performRefactoring(declaration, codeToInline, editor, assignment, isHighlighting) } + } + else { + performRefactoring(declaration, codeToInline, editor, assignment, isHighlighting) + } + } + + private data class Usages(val referenceExpressions: Collection, val foreignUsages: Collection) + + private fun findUsages(declaration: KtProperty): Usages { val references = ReferencesSearch.search(declaration) val referenceExpressions = mutableListOf() val foreignUsages = mutableListOf() @@ -81,59 +135,41 @@ class KotlinInlineValHandler : InlineActionHandler() { } referenceExpressions.addIfNotNull((refElement as? KtExpression)?.getQualifiedExpressionForSelectorOrThis()) } + return Usages(referenceExpressions, foreignUsages) + } - if (referenceExpressions.isEmpty()) { - val kind = if (declaration.isLocal) "Variable" else "Property" - return showErrorHint(project, editor, "$kind '$name' is never used") - } + private data class Initialization(val value: KtExpression, val assignment: KtBinaryExpression?) + private fun extractInitialization( + declaration: KtProperty, + referenceExpressions: Collection, + project: Project, + editor: Editor? + ): Initialization? { val writeUsages = referenceExpressions.filter { it.readWriteAccess(useResolveForReadWrite = true) != ReferenceAccess.READ } - + val initializerInDeclaration = declaration.initializer - val initializer: KtExpression - val assignment: KtBinaryExpression? if (initializerInDeclaration != null) { if (!writeUsages.isEmpty()) { - return reportAmbiguousAssignment(project, editor, name, writeUsages) + reportAmbiguousAssignment(project, editor, declaration.name!!, writeUsages) + return null } - initializer = initializerInDeclaration - assignment = null + return Initialization(initializerInDeclaration, assignment = null) } else { - assignment = writeUsages.singleOrNull() + val assignment = writeUsages.singleOrNull() ?.getAssignmentByLHS() ?.takeIf { it.operationToken == KtTokens.EQ } - initializer = assignment?.right - ?: return reportAmbiguousAssignment(project, editor, name, writeUsages) - } - - val referencesInOriginalFile = referenceExpressions.filter { it.containingFile == file } - val isHighlighting = referencesInOriginalFile.isNotEmpty() - highlightElements(project, editor, referencesInOriginalFile) - - if (referencesInOriginalFile.size != referenceExpressions.size) { - preProcessInternalUsages(initializer, referenceExpressions) - } - - 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) } + val initializer = assignment?.right + if (initializer == null) { + reportAmbiguousAssignment(project, editor, declaration.name!!, writeUsages) + return null } - project.checkConflictsInteractively(conflicts) { performRefactoring(declaration, initializer, editor, assignment, isHighlighting) } - } - else { - performRefactoring(declaration, initializer, editor, assignment, isHighlighting) + return Initialization(initializer, assignment) } } - fun performRefactoring( - declaration: KtProperty, - initializer: KtExpression, - editor: Editor?, - assignment: KtBinaryExpression?, - isHighlighting: Boolean - ) { + private fun buildCodeToInline(declaration: KtProperty, initializer: KtExpression): CodeToInline { val descriptor = declaration.resolveToDescriptor() as VariableDescriptor val expectedType = if (declaration.typeReference != null) descriptor.returnType ?: TypeUtils.NO_EXPECTED_TYPE @@ -147,11 +183,21 @@ class KotlinInlineValHandler : InlineActionHandler() { expectedType = expectedType) } - val reference = editor?.let { TargetElementUtil.findReference(it, it.caretModel.offset) } as? KtSimpleNameReference - val replacementBuilder = CodeToInlineBuilder(descriptor, declaration.getResolutionFacade()) - val replacement = replacementBuilder.prepareCodeToInline(initializerCopy, emptyList(), ::analyzeInitializerCopy) + val codeToInlineBuilder = CodeToInlineBuilder(descriptor, declaration.getResolutionFacade()) + return codeToInlineBuilder.prepareCodeToInline(initializerCopy, emptyList(), ::analyzeInitializerCopy) + } + + private fun performRefactoring( + declaration: KtProperty, + replacement: CodeToInline, + editor: Editor?, + assignment: KtBinaryExpression?, + isHighlighting: Boolean + ) { val replacementStrategy = CallableUsageReplacementStrategy(replacement) + val reference = editor?.let { TargetElementUtil.findReference(it, it.caretModel.offset) } as? KtSimpleNameReference + val dialog = KotlinInlineValDialog(declaration, reference, replacementStrategy, assignment) if (!ApplicationManager.getApplication().isUnitTestMode) { diff --git a/idea/src/org/jetbrains/kotlin/idea/refactoring/inline/inlineUtils.kt b/idea/src/org/jetbrains/kotlin/idea/refactoring/inline/inlineUtils.kt index 56c6fe1b4c6..de49cf3fbd9 100644 --- a/idea/src/org/jetbrains/kotlin/idea/refactoring/inline/inlineUtils.kt +++ b/idea/src/org/jetbrains/kotlin/idea/refactoring/inline/inlineUtils.kt @@ -25,16 +25,29 @@ import com.intellij.openapi.project.Project import com.intellij.openapi.util.Key import com.intellij.psi.PsiElement import com.intellij.refactoring.RefactoringBundle +import com.intellij.refactoring.util.CommonRefactoringUtil import com.intellij.refactoring.util.RefactoringMessageDialog import com.intellij.usageView.UsageInfo +import org.jetbrains.kotlin.descriptors.CallableDescriptor +import org.jetbrains.kotlin.descriptors.SimpleFunctionDescriptor +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.CodeToInline +import org.jetbrains.kotlin.idea.codeInliner.CodeToInlineBuilder +import org.jetbrains.kotlin.idea.core.copied import org.jetbrains.kotlin.idea.refactoring.move.ContainerChangeInfo import org.jetbrains.kotlin.idea.refactoring.move.ContainerInfo -import org.jetbrains.kotlin.idea.refactoring.move.processInternalReferencesToUpdateOnPackageNameChange import org.jetbrains.kotlin.idea.refactoring.move.postProcessMoveUsages +import org.jetbrains.kotlin.idea.refactoring.move.processInternalReferencesToUpdateOnPackageNameChange +import org.jetbrains.kotlin.idea.util.getResolutionScope import org.jetbrains.kotlin.name.FqName import org.jetbrains.kotlin.psi.* import org.jetbrains.kotlin.psi.psiUtil.collectDescendantsOfType import org.jetbrains.kotlin.psi.psiUtil.createSmartPointer +import org.jetbrains.kotlin.resolve.BindingContext +import org.jetbrains.kotlin.types.KotlinType +import org.jetbrains.kotlin.types.TypeUtils import java.util.* fun highlightElements(project: Project, editor: Editor?, elements: List) { @@ -76,7 +89,7 @@ fun showDialog( internal var KtSimpleNameExpression.internalUsageInfos: MutableMap UsageInfo?>? by CopyableUserDataProperty(Key.create("INTERNAL_USAGE_INFOS")) -internal fun preProcessInternalUsages(element: KtElement, usages: List) { +internal fun preProcessInternalUsages(element: KtElement, usages: Collection) { val mainFile = element.containingKtFile val targetPackages = usages.mapNotNullTo(LinkedHashSet()) { it.containingKtFile.packageFqName } for (targetPackage in targetPackages) { @@ -99,4 +112,49 @@ internal fun postProcessInternalReferences(inlinedElement: E): E expressionsToProcess.forEach { it.internalUsageInfos = null } postProcessMoveUsages(internalUsages) return pointer.element -} \ No newline at end of file +} + +internal fun buildCodeToInline(declaration: KtDeclarationWithBody, returnType: KotlinType?, editor: Editor?): CodeToInline? { + val bodyExpression = declaration.bodyExpression!! + val bodyCopy = bodyExpression.copied() + + val expectedType = if (!declaration.hasBlockBody() && declaration.hasDeclaredReturnType()) + returnType ?: TypeUtils.NO_EXPECTED_TYPE + else + TypeUtils.NO_EXPECTED_TYPE + + fun analyzeBodyCopy(): BindingContext { + return bodyCopy.analyzeInContext(bodyExpression.getResolutionScope(), + contextExpression = bodyExpression, + expectedType = expectedType) + } + + val descriptor = declaration.resolveToDescriptor() + val builder = CodeToInlineBuilder(descriptor as CallableDescriptor, declaration.getResolutionFacade()) + if (declaration.hasBlockBody()) { + bodyCopy as KtBlockExpression + val statements = bodyCopy.statements + + val returnStatements = bodyCopy.collectDescendantsOfType { + it.getLabelName().let { it == null || it == declaration.name } + } + + val lastReturn = statements.lastOrNull() as? KtReturnExpression + if (returnStatements.any { it != lastReturn }) { + val message = RefactoringBundle.getCannotRefactorMessage( + if (returnStatements.size > 1) + "Inline Function is not supported for functions with multiple return statements." + else + "Inline Function is not supported for functions with return statements not at the end of the body." + ) + CommonRefactoringUtil.showErrorHint(declaration.project, editor, message, "Inline Function", null) + return null + } + + return builder.prepareCodeToInline(lastReturn?.returnedExpression, + statements.dropLast(returnStatements.size), ::analyzeBodyCopy) + } + else { + return builder.prepareCodeToInline(bodyCopy, emptyList(), ::analyzeBodyCopy) + } +} diff --git a/idea/testData/refactoring/inline/inlineVariableOrProperty/property/ExtensionProperty.kt b/idea/testData/refactoring/inline/inlineVariableOrProperty/property/ExtensionProperty.kt deleted file mode 100644 index f3165d2d186..00000000000 --- a/idea/testData/refactoring/inline/inlineVariableOrProperty/property/ExtensionProperty.kt +++ /dev/null @@ -1,7 +0,0 @@ -val Int.C = 239 - -// not implemented yet - -fun f() { - println(5.C) -} \ No newline at end of file diff --git a/idea/testData/refactoring/inline/inlineVariableOrProperty/property/WithGetter.kt b/idea/testData/refactoring/inline/inlineVariableOrProperty/property/WithGetter.kt deleted file mode 100644 index 04a3f4227c8..00000000000 --- a/idea/testData/refactoring/inline/inlineVariableOrProperty/property/WithGetter.kt +++ /dev/null @@ -1,8 +0,0 @@ -val C: Int - get() = 239 - -// not implemented yet - -fun f() { - println(C) -} \ No newline at end of file diff --git a/idea/testData/refactoring/inline/inlineVariableOrProperty/property/WithInitializerAndGetter.kt b/idea/testData/refactoring/inline/inlineVariableOrProperty/property/WithInitializerAndGetter.kt deleted file mode 100644 index c80ff09d315..00000000000 --- a/idea/testData/refactoring/inline/inlineVariableOrProperty/property/WithInitializerAndGetter.kt +++ /dev/null @@ -1,9 +0,0 @@ -val C = 239 - get() = $C + 1 - - -// not implemented yet - -fun f() { - println(C) -} \ No newline at end of file diff --git a/idea/testData/refactoring/inline/inlineVariableOrProperty/property/accessors/BlockBody.kt b/idea/testData/refactoring/inline/inlineVariableOrProperty/property/accessors/BlockBody.kt new file mode 100644 index 00000000000..2791f47d246 --- /dev/null +++ b/idea/testData/refactoring/inline/inlineVariableOrProperty/property/accessors/BlockBody.kt @@ -0,0 +1,9 @@ +val property: Int + get { + println("access!") + return 1 + } + +fun foo() { + println(property) +} \ No newline at end of file diff --git a/idea/testData/refactoring/inline/inlineVariableOrProperty/property/accessors/BlockBody.kt.after b/idea/testData/refactoring/inline/inlineVariableOrProperty/property/accessors/BlockBody.kt.after new file mode 100644 index 00000000000..fafe7a628d8 --- /dev/null +++ b/idea/testData/refactoring/inline/inlineVariableOrProperty/property/accessors/BlockBody.kt.after @@ -0,0 +1,4 @@ +fun foo() { + println("access!") + println(1) +} \ No newline at end of file diff --git a/idea/testData/refactoring/inline/inlineVariableOrProperty/property/accessors/ExpressionBody.kt b/idea/testData/refactoring/inline/inlineVariableOrProperty/property/accessors/ExpressionBody.kt new file mode 100644 index 00000000000..af95a83bb99 --- /dev/null +++ b/idea/testData/refactoring/inline/inlineVariableOrProperty/property/accessors/ExpressionBody.kt @@ -0,0 +1,8 @@ +import java.util.* + +val property: Int + get() = Random().nextInt() + +fun foo() { + println(property) +} \ No newline at end of file diff --git a/idea/testData/refactoring/inline/inlineVariableOrProperty/property/accessors/ExpressionBody.kt.after b/idea/testData/refactoring/inline/inlineVariableOrProperty/property/accessors/ExpressionBody.kt.after new file mode 100644 index 00000000000..1b088098211 --- /dev/null +++ b/idea/testData/refactoring/inline/inlineVariableOrProperty/property/accessors/ExpressionBody.kt.after @@ -0,0 +1,5 @@ +import java.util.* + +fun foo() { + println(Random().nextInt()) +} \ No newline at end of file diff --git a/idea/testData/refactoring/inline/inlineVariableOrProperty/property/accessors/ExtensionProperty.kt b/idea/testData/refactoring/inline/inlineVariableOrProperty/property/accessors/ExtensionProperty.kt new file mode 100644 index 00000000000..6ef8f424757 --- /dev/null +++ b/idea/testData/refactoring/inline/inlineVariableOrProperty/property/accessors/ExtensionProperty.kt @@ -0,0 +1,7 @@ +val String.property: Int + get() = length * 2 + +fun String.foo() { + println("a".property) + println(property) +} \ No newline at end of file diff --git a/idea/testData/refactoring/inline/inlineVariableOrProperty/property/accessors/ExtensionProperty.kt.after b/idea/testData/refactoring/inline/inlineVariableOrProperty/property/accessors/ExtensionProperty.kt.after new file mode 100644 index 00000000000..94cc0655de6 --- /dev/null +++ b/idea/testData/refactoring/inline/inlineVariableOrProperty/property/accessors/ExtensionProperty.kt.after @@ -0,0 +1,4 @@ +fun String.foo() { + println("a".length * 2) + println(length * 2) +} \ No newline at end of file diff --git a/idea/testData/refactoring/inline/inlineVariableOrProperty/property/accessors/WithInitializer.kt b/idea/testData/refactoring/inline/inlineVariableOrProperty/property/accessors/WithInitializer.kt new file mode 100644 index 00000000000..5b05043907b --- /dev/null +++ b/idea/testData/refactoring/inline/inlineVariableOrProperty/property/accessors/WithInitializer.kt @@ -0,0 +1,8 @@ +// ERROR: Cannot inline property with accessor(s) and backing field + +val C = 239 + get() = field + 1 + +fun f() { + println(C) +} \ 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 d532e321fd6..9e85fc59cbc 100644 --- a/idea/tests/org/jetbrains/kotlin/idea/refactoring/inline/AbstractInlineTest.kt +++ b/idea/tests/org/jetbrains/kotlin/idea/refactoring/inline/AbstractInlineTest.kt @@ -65,9 +65,9 @@ abstract class AbstractInlineTest : KotlinLightCodeInsightFixtureTestCase() { } } catch (e: CommonRefactoringUtil.RefactoringErrorHintException) { - TestCase.assertFalse(afterFileExists) - TestCase.assertEquals(1, expectedErrors.size) - TestCase.assertEquals(expectedErrors[0].replace("\\n", "\n"), e.message) + TestCase.assertFalse("Refactoring not available: ${e.message}", afterFileExists) + TestCase.assertEquals("Expected errors", 1, expectedErrors.size) + TestCase.assertEquals("Error message", expectedErrors[0].replace("\\n", "\n"), e.message) } } else { 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 0a591690d48..cd9663311ea 100644 --- a/idea/tests/org/jetbrains/kotlin/idea/refactoring/inline/InlineTestGenerated.java +++ b/idea/tests/org/jetbrains/kotlin/idea/refactoring/inline/InlineTestGenerated.java @@ -994,12 +994,6 @@ public class InlineTestGenerated extends AbstractInlineTest { doTest(fileName); } - @TestMetadata("ExtensionProperty.kt") - public void testExtensionProperty() throws Exception { - String fileName = KotlinTestUtils.navigationMetadata("idea/testData/refactoring/inline/inlineVariableOrProperty/property/ExtensionProperty.kt"); - doTest(fileName); - } - @TestMetadata("InstanceProperty.kt") public void testInstanceProperty() throws Exception { String fileName = KotlinTestUtils.navigationMetadata("idea/testData/refactoring/inline/inlineVariableOrProperty/property/InstanceProperty.kt"); @@ -1048,16 +1042,37 @@ public class InlineTestGenerated extends AbstractInlineTest { doTest(fileName); } - @TestMetadata("WithGetter.kt") - public void testWithGetter() throws Exception { - String fileName = KotlinTestUtils.navigationMetadata("idea/testData/refactoring/inline/inlineVariableOrProperty/property/WithGetter.kt"); - doTest(fileName); - } + @TestMetadata("idea/testData/refactoring/inline/inlineVariableOrProperty/property/accessors") + @TestDataPath("$PROJECT_ROOT") + @RunWith(JUnit3RunnerWithInners.class) + public static class Accessors extends AbstractInlineTest { + public void testAllFilesPresentInAccessors() throws Exception { + KotlinTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("idea/testData/refactoring/inline/inlineVariableOrProperty/property/accessors"), Pattern.compile("^(\\w+)\\.kt$"), TargetBackend.ANY, true); + } - @TestMetadata("WithInitializerAndGetter.kt") - public void testWithInitializerAndGetter() throws Exception { - String fileName = KotlinTestUtils.navigationMetadata("idea/testData/refactoring/inline/inlineVariableOrProperty/property/WithInitializerAndGetter.kt"); - doTest(fileName); + @TestMetadata("BlockBody.kt") + public void testBlockBody() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("idea/testData/refactoring/inline/inlineVariableOrProperty/property/accessors/BlockBody.kt"); + doTest(fileName); + } + + @TestMetadata("ExpressionBody.kt") + public void testExpressionBody() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("idea/testData/refactoring/inline/inlineVariableOrProperty/property/accessors/ExpressionBody.kt"); + doTest(fileName); + } + + @TestMetadata("ExtensionProperty.kt") + public void testExtensionProperty() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("idea/testData/refactoring/inline/inlineVariableOrProperty/property/accessors/ExtensionProperty.kt"); + doTest(fileName); + } + + @TestMetadata("WithInitializer.kt") + public void testWithInitializer() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("idea/testData/refactoring/inline/inlineVariableOrProperty/property/accessors/WithInitializer.kt"); + doTest(fileName); + } } }