From 5382504306b5c4ea29a372c146f7476fb57f7f04 Mon Sep 17 00:00:00 2001 From: Alexey Sedunov Date: Mon, 2 Mar 2015 15:56:01 +0300 Subject: [PATCH] Extraction Engine: Support lazy properties and properties with initializer --- .../org/jetbrains/kotlin/psi/JetPsiFactory.kt | 21 +++-- .../kotlin/psi/psiUtil/jetPsiUtil.kt | 16 ++-- .../ExtractableCodeDescriptor.kt | 81 +++++++++++++------ .../extractionEngine/ExtractionData.kt | 14 ++-- .../extractionEngine/duplicateUtil.kt | 2 +- .../extractableAnalysisUtil.kt | 23 ++++-- .../extractionEngine/extractorUtil.kt | 62 ++++++++++++-- .../KotlinIntroducePropertyHandler.kt | 7 +- .../extractLazyMultipleExpressions.kt | 10 +++ .../extractLazyMultipleExpressions.kt.after | 17 ++++ .../introduceProperty/extractLazyToClass.kt | 11 +++ .../extractLazyToClass.kt.after | 17 ++++ .../introduceProperty/extractLazyToFile.kt | 8 ++ .../extractLazyToFile.kt.after | 14 ++++ .../introduceProperty/extractLazyWithBlock.kt | 14 ++++ .../extractLazyWithBlock.kt.after | 20 +++++ .../extractLazyWithCallSiteReturn.kt | 10 +++ ...extractLazyWithCallSiteReturn.kt.conflicts | 1 + .../extractToFunction.kt.conflicts | 2 +- .../extractUnit.kt.conflicts | 2 +- .../extractWithInitializerAndBlock.kt | 13 +++ ...xtractWithInitializerAndBlock.kt.conflicts | 1 + ...extractWithInitializerAndCallSiteReturn.kt | 11 +++ ...hInitializerAndCallSiteReturn.kt.conflicts | 1 + ...actWithInitializerAndSingleElementBlock.kt | 12 +++ ...hInitializerAndSingleElementBlock.kt.after | 14 ++++ ...tractWithInitializerMultipleExpressions.kt | 9 +++ ...nitializerMultipleExpressions.kt.conflicts | 1 + .../extractWithInitializerToClass.kt | 10 +++ .../extractWithInitializerToClass.kt.after | 12 +++ .../extractWithInitializerToFile.kt | 7 ++ .../extractWithInitializerToFile.kt.after | 9 +++ .../extractWithParams.kt.conflicts | 2 +- .../AbstractJetExtractionTest.kt | 5 +- .../JetExtractionTestGenerated.java | 66 +++++++++++++++ 35 files changed, 461 insertions(+), 64 deletions(-) create mode 100644 idea/testData/refactoring/introduceProperty/extractLazyMultipleExpressions.kt create mode 100644 idea/testData/refactoring/introduceProperty/extractLazyMultipleExpressions.kt.after create mode 100644 idea/testData/refactoring/introduceProperty/extractLazyToClass.kt create mode 100644 idea/testData/refactoring/introduceProperty/extractLazyToClass.kt.after create mode 100644 idea/testData/refactoring/introduceProperty/extractLazyToFile.kt create mode 100644 idea/testData/refactoring/introduceProperty/extractLazyToFile.kt.after create mode 100644 idea/testData/refactoring/introduceProperty/extractLazyWithBlock.kt create mode 100644 idea/testData/refactoring/introduceProperty/extractLazyWithBlock.kt.after create mode 100644 idea/testData/refactoring/introduceProperty/extractLazyWithCallSiteReturn.kt create mode 100644 idea/testData/refactoring/introduceProperty/extractLazyWithCallSiteReturn.kt.conflicts create mode 100644 idea/testData/refactoring/introduceProperty/extractWithInitializerAndBlock.kt create mode 100644 idea/testData/refactoring/introduceProperty/extractWithInitializerAndBlock.kt.conflicts create mode 100644 idea/testData/refactoring/introduceProperty/extractWithInitializerAndCallSiteReturn.kt create mode 100644 idea/testData/refactoring/introduceProperty/extractWithInitializerAndCallSiteReturn.kt.conflicts create mode 100644 idea/testData/refactoring/introduceProperty/extractWithInitializerAndSingleElementBlock.kt create mode 100644 idea/testData/refactoring/introduceProperty/extractWithInitializerAndSingleElementBlock.kt.after create mode 100644 idea/testData/refactoring/introduceProperty/extractWithInitializerMultipleExpressions.kt create mode 100644 idea/testData/refactoring/introduceProperty/extractWithInitializerMultipleExpressions.kt.conflicts create mode 100644 idea/testData/refactoring/introduceProperty/extractWithInitializerToClass.kt create mode 100644 idea/testData/refactoring/introduceProperty/extractWithInitializerToClass.kt.after create mode 100644 idea/testData/refactoring/introduceProperty/extractWithInitializerToFile.kt create mode 100644 idea/testData/refactoring/introduceProperty/extractWithInitializerToFile.kt.after diff --git a/compiler/frontend/src/org/jetbrains/kotlin/psi/JetPsiFactory.kt b/compiler/frontend/src/org/jetbrains/kotlin/psi/JetPsiFactory.kt index 639043c7fe7..50241abb8d5 100644 --- a/compiler/frontend/src/org/jetbrains/kotlin/psi/JetPsiFactory.kt +++ b/compiler/frontend/src/org/jetbrains/kotlin/psi/JetPsiFactory.kt @@ -535,7 +535,7 @@ public class JetPsiFactory(private val project: Project) { state = State.RECEIVER } - private fun blockPrefix() = when (target) { + private fun bodyPrefix() = when (target) { Target.FUNCTION -> "" Target.READ_ONLY_PROPERTY -> "\nget()" } @@ -621,19 +621,28 @@ public class JetPsiFactory(private val project: Project) { return this } - public fun simpleBody(body: String): CallableBuilder { + public fun blockBody(body: String): CallableBuilder { assert(state == State.BODY || state == State.TYPE_CONSTRAINTS) - sb.append(blockPrefix()).append(" = ").append(body) + sb.append(bodyPrefix()).append(" {\n").append(body).append("\n}") state = State.DONE return this } - public fun blockBody(body: String): CallableBuilder { - assert(state == State.BODY || state == State.TYPE_CONSTRAINTS) + public fun initializer(body: String): CallableBuilder { + assert(target == Target.READ_ONLY_PROPERTY && (state == State.BODY || state == State.TYPE_CONSTRAINTS)) - sb.append(blockPrefix()).append(" {\n").append(body).append("\n}") + sb.append(" = ").append(body) + state = State.DONE + + return this + } + + public fun lazyBody(body: String): CallableBuilder { + assert(target == Target.READ_ONLY_PROPERTY && (state == State.BODY || state == State.TYPE_CONSTRAINTS)) + + sb.append(" by kotlin.properties.Delegates.lazy {\n").append(body).append("\n}") state = State.DONE return this diff --git a/compiler/frontend/src/org/jetbrains/kotlin/psi/psiUtil/jetPsiUtil.kt b/compiler/frontend/src/org/jetbrains/kotlin/psi/psiUtil/jetPsiUtil.kt index c19f8860d3a..d2e99459c25 100644 --- a/compiler/frontend/src/org/jetbrains/kotlin/psi/psiUtil/jetPsiUtil.kt +++ b/compiler/frontend/src/org/jetbrains/kotlin/psi/psiUtil/jetPsiUtil.kt @@ -130,11 +130,17 @@ public fun JetElement.outermostLastBlockElement(predicate: (JetElement) -> Boole return JetPsiUtil.getOutermostLastBlockElement(this) { e -> e != null && predicate(e) } } -public fun JetBlockExpression.appendElement(element: JetElement): JetElement = - addAfter(element, getRBrace()!!.getPrevSibling()!!)!! as JetElement - -public fun JetBlockExpression.prependElement(element: JetElement): JetElement = - addBefore(element, getLBrace()!!.getNextSibling()!!)!! as JetElement +public fun JetBlockExpression.appendElement(element: JetElement): JetElement { + val rBrace = getRBrace() + val anchor = if (rBrace == null) { + val lastChild = getLastChild() + if (lastChild !is PsiWhiteSpace) addAfter(JetPsiFactory(this).createNewLine(), lastChild)!! else lastChild + } + else { + rBrace.getPrevSibling()!! + } + return addAfter(element, anchor)!! as JetElement +} public fun JetElement.wrapInBlock(): JetBlockExpression { val block = JetPsiFactory(this).createEmptyBody() diff --git a/idea/src/org/jetbrains/kotlin/idea/refactoring/introduce/extractionEngine/ExtractableCodeDescriptor.kt b/idea/src/org/jetbrains/kotlin/idea/refactoring/introduce/extractionEngine/ExtractableCodeDescriptor.kt index 2d933ffea5c..c469421ce64 100644 --- a/idea/src/org/jetbrains/kotlin/idea/refactoring/introduce/extractionEngine/ExtractableCodeDescriptor.kt +++ b/idea/src/org/jetbrains/kotlin/idea/refactoring/introduce/extractionEngine/ExtractableCodeDescriptor.kt @@ -17,37 +17,21 @@ package org.jetbrains.kotlin.idea.refactoring.introduce.extractionEngine import org.jetbrains.kotlin.types.JetType -import org.jetbrains.kotlin.psi.JetPsiFactory -import org.jetbrains.kotlin.psi.JetCallExpression -import org.jetbrains.kotlin.psi.JetElement import org.jetbrains.kotlin.builtins.KotlinBuiltIns import com.intellij.util.containers.MultiMap import com.intellij.psi.PsiElement -import org.jetbrains.kotlin.psi.JetThisExpression import org.jetbrains.kotlin.idea.references.JetSimpleNameReference import org.jetbrains.kotlin.name.FqName import org.jetbrains.kotlin.idea.references.JetSimpleNameReference.ShorteningMode import org.jetbrains.kotlin.psi.psiUtil.replaced -import org.jetbrains.kotlin.psi.JetQualifiedExpression -import org.jetbrains.kotlin.psi.JetTypeParameter -import org.jetbrains.kotlin.psi.JetTypeConstraint import org.jetbrains.kotlin.idea.refactoring.introduce.extractionEngine.AnalysisResult.Status import org.jetbrains.kotlin.idea.refactoring.JetRefactoringBundle import org.jetbrains.kotlin.idea.refactoring.introduce.extractionEngine.AnalysisResult.ErrorMessage import org.jetbrains.kotlin.descriptors.DeclarationDescriptor -import org.jetbrains.kotlin.psi.JetProperty -import org.jetbrains.kotlin.psi.JetDeclaration import com.intellij.openapi.util.text.StringUtil -import org.jetbrains.kotlin.psi.JetClassBody -import org.jetbrains.kotlin.psi.JetFile -import org.jetbrains.kotlin.psi.JetNamedDeclaration import org.jetbrains.kotlin.types.CommonSupertypes -import java.util.Collections -import org.jetbrains.kotlin.psi.JetExpression import org.jetbrains.kotlin.resolve.DescriptorUtils -import org.jetbrains.kotlin.psi.JetReturnExpression -import org.jetbrains.kotlin.idea.refactoring.introduce.extractionEngine.OutputValue.ExpressionValue -import org.jetbrains.kotlin.idea.refactoring.introduce.extractionEngine.OutputValue.Jump +import org.jetbrains.kotlin.idea.refactoring.introduce.extractionEngine.OutputValue.* import org.jetbrains.kotlin.descriptors.ModuleDescriptor import org.jetbrains.kotlin.types.TypeUtils import kotlin.properties.Delegates @@ -57,6 +41,8 @@ import org.jetbrains.kotlin.idea.util.psi.patternMatching.JetPsiRange import org.jetbrains.kotlin.resolve.BindingContext import org.jetbrains.kotlin.idea.util.isUnit import org.jetbrains.kotlin.resolve.descriptorUtil.resolveTopLevelClass +import org.jetbrains.kotlin.psi.* +import java.util.* trait Parameter { val argumentText: String @@ -314,11 +300,62 @@ data class ExtractableCodeDescriptor( val controlFlow: ControlFlow ) { val name: String get() = suggestedNames.firstOrNull() ?: "" + val duplicates: List by Delegates.lazy { findDuplicates() } } +enum class ExtractionTarget(val name: String) { + FUNCTION : ExtractionTarget("function") { + override fun isAvailable(descriptor: ExtractableCodeDescriptor) = true + } + + PROPERTY_WITH_INITIALIZER : ExtractionTarget("property with initializer") { + override fun isAvailable(descriptor: ExtractableCodeDescriptor): Boolean { + return checkSignatureAndParent(descriptor) && checkSimpleControlFlow(descriptor) && checkSimpleBody(descriptor) + } + } + + PROPERTY_WITH_GETTER : ExtractionTarget("property with getter") { + override fun isAvailable(descriptor: ExtractableCodeDescriptor): Boolean { + return checkSignatureAndParent(descriptor) + } + } + + LAZY_PROPERTY : ExtractionTarget("lazy property") { + override fun isAvailable(descriptor: ExtractableCodeDescriptor): Boolean { + return checkSignatureAndParent(descriptor) && checkSimpleControlFlow(descriptor) + } + } + + abstract fun isAvailable(descriptor: ExtractableCodeDescriptor): Boolean + + class object { + fun checkSimpleBody(descriptor: ExtractableCodeDescriptor): Boolean { + val expression = descriptor.extractionData.getExpressions().singleOrNull() + return expression != null && expression !is JetDeclaration && expression !is JetBlockExpression + } + + fun checkSimpleControlFlow(descriptor: ExtractableCodeDescriptor): Boolean { + val outputValue = descriptor.controlFlow.outputValues.singleOrNull() + return (outputValue is ExpressionValue && !outputValue.callSiteReturn) || outputValue is Initializer + } + + fun checkSignatureAndParent(descriptor: ExtractableCodeDescriptor): Boolean { + if (!descriptor.parameters.isEmpty()) return false + if (descriptor.controlFlow.outputValueBoxer.returnType.isUnit()) return false + + val parent = descriptor.extractionData.targetSibling.getParent() + return (parent is JetFile || parent is JetClassBody) + } + } +} + +val propertyTargets: List = listOf(ExtractionTarget.PROPERTY_WITH_INITIALIZER, + ExtractionTarget.PROPERTY_WITH_GETTER, + ExtractionTarget.LAZY_PROPERTY) + data class ExtractionGeneratorOptions( val inTempFile: Boolean = false, - val extractAsProperty: Boolean = false, + val target: ExtractionTarget = ExtractionTarget.FUNCTION, val flexibleTypesAllowed: Boolean = false ) { class object { @@ -393,11 +430,3 @@ class ExtractableCodeDescriptorWithConflicts( val descriptor: ExtractableCodeDescriptor, val conflicts: MultiMap ) - -fun ExtractableCodeDescriptor.canGenerateProperty(): Boolean { - if (!parameters.isEmpty()) return false - if (controlFlow.outputValueBoxer.returnType.isUnit()) return false - - val parent = extractionData.targetSibling.getParent() - return parent is JetFile || parent is JetClassBody -} diff --git a/idea/src/org/jetbrains/kotlin/idea/refactoring/introduce/extractionEngine/ExtractionData.kt b/idea/src/org/jetbrains/kotlin/idea/refactoring/introduce/extractionEngine/ExtractionData.kt index 09fe01f33ff..c7928ce64a2 100644 --- a/idea/src/org/jetbrains/kotlin/idea/refactoring/introduce/extractionEngine/ExtractionData.kt +++ b/idea/src/org/jetbrains/kotlin/idea/refactoring/introduce/extractionEngine/ExtractionData.kt @@ -92,7 +92,7 @@ data class ExtractionData( fun getExpressions(): List = originalElements.filterIsInstance() - fun getCodeFragmentTextRange(): TextRange? { + private fun getCodeFragmentTextRange(): TextRange? { val originalElements = originalElements return when (originalElements.size()) { 0 -> null @@ -105,8 +105,9 @@ data class ExtractionData( } } - fun getCodeFragmentText(): String = - getCodeFragmentTextRange()?.let { originalFile.getText()?.substring(it.getStartOffset(), it.getEndOffset()) } ?: "" + val codeFragmentText: String by Delegates.lazy { + getCodeFragmentTextRange()?.let { originalFile.getText()?.substring(it.getStartOffset(), it.getEndOffset()) } ?: "" + } val originalStartOffset = originalElements.firstOrNull()?.let { e -> e.getTextRange()!!.getStartOffset() } @@ -179,6 +180,9 @@ data class ExtractionData( // Hack: // we can't get first element offset through getStatement()/getChildren() since they skip comments and whitespaces // So we take offset of the left brace instead and increase it by 2 (which is length of "{\n" separating block start and its first element) -private fun JetBlockExpression.getBlockContentOffset(): Int { - return getLBrace()!!.getTextRange()!!.getStartOffset() + 2 +private fun JetExpression.getBlockContentOffset(): Int { + (this as? JetBlockExpression)?.getLBrace()?.let { + return it.getTextRange()!!.getStartOffset() + 2 + } + return getTextRange()!!.getStartOffset() } diff --git a/idea/src/org/jetbrains/kotlin/idea/refactoring/introduce/extractionEngine/duplicateUtil.kt b/idea/src/org/jetbrains/kotlin/idea/refactoring/introduce/extractionEngine/duplicateUtil.kt index 4da48953d56..e812b18fa67 100644 --- a/idea/src/org/jetbrains/kotlin/idea/refactoring/introduce/extractionEngine/duplicateUtil.kt +++ b/idea/src/org/jetbrains/kotlin/idea/refactoring/introduce/extractionEngine/duplicateUtil.kt @@ -110,4 +110,4 @@ public fun processDuplicates( project.executeWriteCommand(MethodDuplicatesHandler.REFACTORING_NAME, replacer) } -} +} \ No newline at end of file diff --git a/idea/src/org/jetbrains/kotlin/idea/refactoring/introduce/extractionEngine/extractableAnalysisUtil.kt b/idea/src/org/jetbrains/kotlin/idea/refactoring/introduce/extractionEngine/extractableAnalysisUtil.kt index 3ab26a6f2a5..d72f119ad23 100644 --- a/idea/src/org/jetbrains/kotlin/idea/refactoring/introduce/extractionEngine/extractableAnalysisUtil.kt +++ b/idea/src/org/jetbrains/kotlin/idea/refactoring/introduce/extractionEngine/extractableAnalysisUtil.kt @@ -69,6 +69,7 @@ import org.jetbrains.kotlin.psi.psiUtil.getStrictParentOfType import org.jetbrains.kotlin.idea.refactoring.comparePossiblyOverridingDescriptors import org.jetbrains.kotlin.idea.util.makeNullable import org.jetbrains.kotlin.resolve.calls.CallTransformer +import org.jetbrains.kotlin.resolve.calls.callUtil.* private val DEFAULT_FUNCTION_NAME = "myFun" private val DEFAULT_RETURN_TYPE = KotlinBuiltIns.getInstance().getUnitType() @@ -391,7 +392,7 @@ fun ExtractionData.createTemporaryDeclaration(functionText: String): JetNamedDec } private fun ExtractionData.createTemporaryCodeBlock(): JetBlockExpression = - (createTemporaryDeclaration("fun() {\n${getCodeFragmentText()}\n}\n") as JetNamedFunction).getBodyExpression() as JetBlockExpression + (createTemporaryDeclaration("fun() {\n$codeFragmentText\n}\n") as JetNamedFunction).getBodyExpression() as JetBlockExpression private fun JetType.collectReferencedTypes(processTypeArguments: Boolean): List { if (!processTypeArguments) return Collections.singletonList(this) @@ -644,7 +645,7 @@ private fun ExtractionData.inferParametersInfo( "this$label" } else - (thisExpr ?: ref).getText() ?: throw AssertionError("'this' reference shouldn't be empty: code fragment = ${getCodeFragmentText()}") + (thisExpr ?: ref).getText() ?: throw AssertionError("'this' reference shouldn't be empty: code fragment = $codeFragmentText") MutableParameter(argumentText, descriptorToExtract, extractThis) } @@ -835,11 +836,21 @@ fun ExtractionData.performAnalysis(): AnalysisResult { ) } -private fun JetNamedDeclaration.getGeneratedBlockBody() = +private fun JetNamedDeclaration.getGeneratedBody() = when (this) { is JetNamedFunction -> getBodyExpression() - else -> (this as JetProperty).getGetter()!!.getBodyExpression() - } as? JetBlockExpression ?: throw AssertionError("Couldn't get block body for this declaration: ${JetPsiUtil.getElementTextWithContext(this)}") + else -> { + val property = this as JetProperty + + property.getGetter()?.getBodyExpression()?.let { return it } + property.getInitializer()?.let { return it } + // We assume lazy property here with delegate expression 'by Delegates.lazy { body }' + property.getDelegateExpression()?.let { + val call = it.getCalleeExpressionIfAny()?.getParent() as? JetCallExpression + call?.getFunctionLiteralArguments()?.singleOrNull()?.getFunctionLiteral()?.getBodyExpression() + } + } + } ?: throw AssertionError("Couldn't get block body for this declaration: ${JetPsiUtil.getElementTextWithContext(this)}") fun ExtractableCodeDescriptor.validate(): ExtractableCodeDescriptorWithConflicts { val conflicts = MultiMap() @@ -847,7 +858,7 @@ fun ExtractableCodeDescriptor.validate(): ExtractableCodeDescriptorWithConflicts val result = ExtractionGeneratorConfiguration(this, ExtractionGeneratorOptions(inTempFile = true)).generateDeclaration() val valueParameterList = (result.declaration as? JetNamedFunction)?.getValueParameterList() - val bindingContext = result.declaration.getGeneratedBlockBody().analyze() + val bindingContext = result.declaration.getGeneratedBody().analyze() for ((originalOffset, resolveResult) in extractionData.refOffsetToDeclaration) { if (resolveResult.declaration.isInsideOf(extractionData.originalElements)) continue diff --git a/idea/src/org/jetbrains/kotlin/idea/refactoring/introduce/extractionEngine/extractorUtil.kt b/idea/src/org/jetbrains/kotlin/idea/refactoring/introduce/extractionEngine/extractorUtil.kt index 24de6e2e22f..673f7790b06 100644 --- a/idea/src/org/jetbrains/kotlin/idea/refactoring/introduce/extractionEngine/extractorUtil.kt +++ b/idea/src/org/jetbrains/kotlin/idea/refactoring/introduce/extractionEngine/extractorUtil.kt @@ -17,6 +17,25 @@ package org.jetbrains.kotlin.idea.refactoring.introduce.extractionEngine import com.intellij.psi.PsiElement +import org.jetbrains.kotlin.psi.JetDeclaration +import org.jetbrains.kotlin.psi.JetProperty +import org.jetbrains.kotlin.psi.JetPsiFactory.CallableBuilder +import org.jetbrains.kotlin.psi.JetNamedDeclaration +import org.jetbrains.kotlin.psi.JetPsiFactory +import java.util.LinkedHashMap +import java.util.Collections +import org.jetbrains.kotlin.psi.psiUtil.isFunctionLiteralOutsideParentheses +import org.jetbrains.kotlin.psi.JetFunctionLiteralArgument +import org.jetbrains.kotlin.idea.util.psiModificationUtil.moveInsideParenthesesAndReplaceWith +import org.jetbrains.kotlin.psi.psiUtil.appendElement +import org.jetbrains.kotlin.psi.psiUtil.replaced +import org.jetbrains.kotlin.psi.JetBlockExpression +import org.jetbrains.kotlin.idea.refactoring.introduce.extractionEngine.OutputValue.ParameterUpdate +import org.jetbrains.kotlin.idea.refactoring.introduce.extractionEngine.OutputValue.Jump +import org.jetbrains.kotlin.idea.refactoring.introduce.extractionEngine.OutputValue.Initializer +import org.jetbrains.kotlin.idea.refactoring.introduce.extractionEngine.OutputValue.ExpressionValue +import org.jetbrains.kotlin.psi.JetReturnExpression +import org.jetbrains.kotlin.idea.refactoring.JetNameValidatorImpl import org.jetbrains.kotlin.builtins.KotlinBuiltIns import org.jetbrains.kotlin.idea.refactoring.JetNameSuggester import org.jetbrains.kotlin.idea.refactoring.JetNameValidatorImpl @@ -52,11 +71,12 @@ fun ExtractionGeneratorConfiguration.getDeclarationText( DescriptorRenderer.FLEXIBLE_TYPES_FOR_CODE else IdeDescriptorRenderers.SOURCE_CODE ): String { - if (!descriptor.canGenerateProperty() && generatorOptions.extractAsProperty) { - throw IllegalArgumentException("Can't generate property: ${descriptor.extractionData.getCodeFragmentText()}") + val extractionTarget = generatorOptions.target + if (!extractionTarget.isAvailable(descriptor)) { + throw IllegalArgumentException("Can't generate ${extractionTarget.name}: ${descriptor.extractionData.codeFragmentText}") } - val builderTarget = if (generatorOptions.extractAsProperty) Target.READ_ONLY_PROPERTY else Target.FUNCTION + val builderTarget = if (extractionTarget == ExtractionTarget.FUNCTION) CallableBuilder.Target.FUNCTION else CallableBuilder.Target.READ_ONLY_PROPERTY return CallableBuilder(builderTarget).let { builder -> builder.modifier(descriptor.visibility) @@ -78,13 +98,22 @@ fun ExtractionGeneratorConfiguration.getDeclarationText( } with(descriptor.controlFlow.outputValueBoxer.returnType) { - if (isDefault() || isError()) builder.noReturnType() else builder.returnType(this.typeAsString()) + if (isDefault() || isError() || extractionTarget == ExtractionTarget.PROPERTY_WITH_INITIALIZER) { + builder.noReturnType() + } else { + builder.returnType(typeAsString()) + } } builder.typeConstraints(descriptor.typeParameters.flatMap { it.originalConstraints }.map { it.getText()!! }) if (withBody) { - builder.blockBody(descriptor.extractionData.getCodeFragmentText()) + val bodyText = descriptor.extractionData.codeFragmentText + when (extractionTarget) { + ExtractionTarget.FUNCTION, ExtractionTarget.PROPERTY_WITH_GETTER -> builder.blockBody(bodyText) + ExtractionTarget.PROPERTY_WITH_INITIALIZER -> builder.initializer(bodyText) + ExtractionTarget.LAZY_PROPERTY -> builder.lazyBody(bodyText) + } } builder.asString() @@ -337,7 +366,9 @@ private fun makeCall( } } -fun ExtractionGeneratorConfiguration.generateDeclaration(): ExtractionResult{ +fun ExtractionGeneratorConfiguration.generateDeclaration( + declarationToReplace: JetNamedDeclaration? = null +): ExtractionResult{ val psiFactory = JetPsiFactory(descriptor.extractionData.originalFile) val nameByOffset = HashMap() @@ -388,7 +419,7 @@ fun ExtractionGeneratorConfiguration.generateDeclaration(): ExtractionResult{ } fun adjustDeclarationBody(declaration: JetNamedDeclaration) { - val body = declaration.getGeneratedBlockBody() + val body = declaration.getGeneratedBody() val exprReplacementMap = HashMap JetElement>() val originalOffsetByExpr = LinkedHashMap() @@ -444,6 +475,10 @@ fun ExtractionGeneratorConfiguration.generateDeclaration(): ExtractionResult{ } } + if (generatorOptions.target == ExtractionTarget.PROPERTY_WITH_INITIALIZER) return + + if (body !is JetBlockExpression) throw AssertionError("Block body expected: ${descriptor.extractionData.codeFragmentText}") + val firstExpression = body.getStatements().firstOrNull() if (firstExpression != null) { for (param in descriptor.parameters) { @@ -474,6 +509,15 @@ fun ExtractionGeneratorConfiguration.generateDeclaration(): ExtractionResult{ val returnExpression = descriptor.controlFlow.outputValueBoxer.getReturnExpression(getReturnArguments(defaultExpression), psiFactory) if (returnExpression == null) return + if (generatorOptions.target == ExtractionTarget.LAZY_PROPERTY) { + // In the case of lazy property absence of default value means that output values are of OutputValue.Initializer type + // We just add resulting expressions without return, since returns are prohibited in the body of lazy property + if (defaultValue == null) { + body.appendElement(returnExpression.getReturnedExpression()!!) + } + return + } + when { defaultValue == null -> body.appendElement(returnExpression) @@ -484,6 +528,8 @@ fun ExtractionGeneratorConfiguration.generateDeclaration(): ExtractionResult{ } fun insertDeclaration(declaration: JetNamedDeclaration, anchor: PsiElement): JetNamedDeclaration { + declarationToReplace?.let { return it.replace(declaration) as JetNamedDeclaration } + return with(descriptor.extractionData) { val targetContainer = anchor.getParent()!! val emptyLines = psiFactory.createWhiteSpace("\n\n") @@ -502,7 +548,7 @@ fun ExtractionGeneratorConfiguration.generateDeclaration(): ExtractionResult{ } } - val duplicates = if (generatorOptions.inTempFile) Collections.emptyList() else descriptor.findDuplicates() + val duplicates = if (generatorOptions.inTempFile) Collections.emptyList() else descriptor.duplicates val anchor = with(descriptor.extractionData) { val anchorCandidates = duplicates.mapTo(ArrayList()) { it.range.elements.first() } diff --git a/idea/src/org/jetbrains/kotlin/idea/refactoring/introduce/introduceProperty/KotlinIntroducePropertyHandler.kt b/idea/src/org/jetbrains/kotlin/idea/refactoring/introduce/introduceProperty/KotlinIntroducePropertyHandler.kt index b5908aa26a4..e31d8c6b013 100644 --- a/idea/src/org/jetbrains/kotlin/idea/refactoring/introduce/introduceProperty/KotlinIntroducePropertyHandler.kt +++ b/idea/src/org/jetbrains/kotlin/idea/refactoring/introduce/introduceProperty/KotlinIntroducePropertyHandler.kt @@ -28,8 +28,8 @@ import org.jetbrains.kotlin.idea.util.psi.patternMatching.* import kotlin.test.* import com.intellij.openapi.application.* import org.jetbrains.kotlin.idea.refactoring.introduce.introduceProperty.* -import org.jetbrains.kotlin.idea.refactoring.introduce.introduceVariable.* import java.util.* +import org.jetbrains.kotlin.idea.refactoring.introduce.introduceVariable.* public class KotlinIntroducePropertyHandler( val helper: ExtractionEngineHelper = KotlinIntroducePropertyHandler.InteractiveExtractionHelper @@ -42,8 +42,9 @@ public class KotlinIntroducePropertyHandler( continuation: (ExtractionGeneratorConfiguration) -> Unit ) { val descriptor = descriptorWithConflicts.descriptor - if (descriptor.canGenerateProperty()) { - continuation(ExtractionGeneratorConfiguration(descriptor, ExtractionGeneratorOptions.DEFAULT.copy(extractAsProperty = true))) + val target = propertyTargets.filter { it.isAvailable(descriptor) }.firstOrNull() + if (target != null) { + continuation(ExtractionGeneratorConfiguration(descriptor, ExtractionGeneratorOptions.DEFAULT.copy(target = target))) } else { showErrorHint(project, editor, "Can't introduce property for this expression", INTRODUCE_PROPERTY) diff --git a/idea/testData/refactoring/introduceProperty/extractLazyMultipleExpressions.kt b/idea/testData/refactoring/introduceProperty/extractLazyMultipleExpressions.kt new file mode 100644 index 00000000000..ec41ca6fd53 --- /dev/null +++ b/idea/testData/refactoring/introduceProperty/extractLazyMultipleExpressions.kt @@ -0,0 +1,10 @@ +// WITH_RUNTIME +// EXTRACTION_TARGET: lazy property +class A { + fun foo(): Int { + val a = 1 + 2 + val b = a*2 + val c = b - 1 + } +} + diff --git a/idea/testData/refactoring/introduceProperty/extractLazyMultipleExpressions.kt.after b/idea/testData/refactoring/introduceProperty/extractLazyMultipleExpressions.kt.after new file mode 100644 index 00000000000..4fbb6042d40 --- /dev/null +++ b/idea/testData/refactoring/introduceProperty/extractLazyMultipleExpressions.kt.after @@ -0,0 +1,17 @@ +import kotlin.properties.Delegates + +// WITH_RUNTIME +// EXTRACTION_TARGET: lazy property +class A { + fun foo(): Int { + val b = i + val c = b - 1 + } + + private val i: Int by Delegates.lazy { + val a = 1 + 2 + val b = a * 2 + b + } +} + diff --git a/idea/testData/refactoring/introduceProperty/extractLazyToClass.kt b/idea/testData/refactoring/introduceProperty/extractLazyToClass.kt new file mode 100644 index 00000000000..013b060075c --- /dev/null +++ b/idea/testData/refactoring/introduceProperty/extractLazyToClass.kt @@ -0,0 +1,11 @@ +// WITH_RUNTIME +// EXTRACTION_TARGET: lazy property + +class A(val n: Int = 1) { + val m: Int = 2 + + fun foo(): Int { + return m + n + 1 + } +} + diff --git a/idea/testData/refactoring/introduceProperty/extractLazyToClass.kt.after b/idea/testData/refactoring/introduceProperty/extractLazyToClass.kt.after new file mode 100644 index 00000000000..bc5618c8576 --- /dev/null +++ b/idea/testData/refactoring/introduceProperty/extractLazyToClass.kt.after @@ -0,0 +1,17 @@ +// WITH_RUNTIME +// EXTRACTION_TARGET: lazy property + +import kotlin.properties.Delegates + +class A(val n: Int = 1) { + val m: Int = 2 + + fun foo(): Int { + return i + } + + private val i: Int by Delegates.lazy { + m + n + 1 + } +} + diff --git a/idea/testData/refactoring/introduceProperty/extractLazyToFile.kt b/idea/testData/refactoring/introduceProperty/extractLazyToFile.kt new file mode 100644 index 00000000000..7e0ebee84bc --- /dev/null +++ b/idea/testData/refactoring/introduceProperty/extractLazyToFile.kt @@ -0,0 +1,8 @@ +// WITH_RUNTIME +// EXTRACTION_TARGET: lazy property + +val n: Int = 1 + +fun foo(): Int { + return n + 1 +} \ No newline at end of file diff --git a/idea/testData/refactoring/introduceProperty/extractLazyToFile.kt.after b/idea/testData/refactoring/introduceProperty/extractLazyToFile.kt.after new file mode 100644 index 00000000000..247e35bc8ee --- /dev/null +++ b/idea/testData/refactoring/introduceProperty/extractLazyToFile.kt.after @@ -0,0 +1,14 @@ +// WITH_RUNTIME +// EXTRACTION_TARGET: lazy property + +import kotlin.properties.Delegates + +val n: Int = 1 + +fun foo(): Int { + return i +} + +private val i: Int by Delegates.lazy { + n + 1 +} \ No newline at end of file diff --git a/idea/testData/refactoring/introduceProperty/extractLazyWithBlock.kt b/idea/testData/refactoring/introduceProperty/extractLazyWithBlock.kt new file mode 100644 index 00000000000..8d498b6ceaf --- /dev/null +++ b/idea/testData/refactoring/introduceProperty/extractLazyWithBlock.kt @@ -0,0 +1,14 @@ +// WITH_RUNTIME +// EXTRACTION_TARGET: lazy property + +class A(val n: Int = 1) { + val m: Int = 2 + + fun foo(): Int { + return if (n > 1) { + println(n) + m + n + 1 + } else 0 + } +} + diff --git a/idea/testData/refactoring/introduceProperty/extractLazyWithBlock.kt.after b/idea/testData/refactoring/introduceProperty/extractLazyWithBlock.kt.after new file mode 100644 index 00000000000..b6f8b8e1c00 --- /dev/null +++ b/idea/testData/refactoring/introduceProperty/extractLazyWithBlock.kt.after @@ -0,0 +1,20 @@ +// WITH_RUNTIME +// EXTRACTION_TARGET: lazy property + +import kotlin.properties.Delegates + +class A(val n: Int = 1) { + val m: Int = 2 + + fun foo(): Int { + return if (n > 1) { + i + } else 0 + } + + private val i: Int by Delegates.lazy { + println(n) + m + n + 1 + } +} + diff --git a/idea/testData/refactoring/introduceProperty/extractLazyWithCallSiteReturn.kt b/idea/testData/refactoring/introduceProperty/extractLazyWithCallSiteReturn.kt new file mode 100644 index 00000000000..c5e5b44f9f6 --- /dev/null +++ b/idea/testData/refactoring/introduceProperty/extractLazyWithCallSiteReturn.kt @@ -0,0 +1,10 @@ +// EXTRACTION_TARGET: lazy property + +class A(val n: Int = 1) { + val m: Int = 2 + + fun foo(): Int { + return m + n + 1 + } +} + diff --git a/idea/testData/refactoring/introduceProperty/extractLazyWithCallSiteReturn.kt.conflicts b/idea/testData/refactoring/introduceProperty/extractLazyWithCallSiteReturn.kt.conflicts new file mode 100644 index 00000000000..1e846dedaba --- /dev/null +++ b/idea/testData/refactoring/introduceProperty/extractLazyWithCallSiteReturn.kt.conflicts @@ -0,0 +1 @@ +Can't generate lazy property: return m + n + 1 \ No newline at end of file diff --git a/idea/testData/refactoring/introduceProperty/extractToFunction.kt.conflicts b/idea/testData/refactoring/introduceProperty/extractToFunction.kt.conflicts index 218ce997768..2f631750119 100644 --- a/idea/testData/refactoring/introduceProperty/extractToFunction.kt.conflicts +++ b/idea/testData/refactoring/introduceProperty/extractToFunction.kt.conflicts @@ -1 +1 @@ -Can't generate property: n + 1 \ No newline at end of file +Can't generate property with getter: n + 1 \ No newline at end of file diff --git a/idea/testData/refactoring/introduceProperty/extractUnit.kt.conflicts b/idea/testData/refactoring/introduceProperty/extractUnit.kt.conflicts index 9ec26226ae5..7fbdaec5ea2 100644 --- a/idea/testData/refactoring/introduceProperty/extractUnit.kt.conflicts +++ b/idea/testData/refactoring/introduceProperty/extractUnit.kt.conflicts @@ -1 +1 @@ -Can't generate property: 1 + 1 \ No newline at end of file +Can't generate property with getter: 1 + 1 \ No newline at end of file diff --git a/idea/testData/refactoring/introduceProperty/extractWithInitializerAndBlock.kt b/idea/testData/refactoring/introduceProperty/extractWithInitializerAndBlock.kt new file mode 100644 index 00000000000..a4dd7e2f6ad --- /dev/null +++ b/idea/testData/refactoring/introduceProperty/extractWithInitializerAndBlock.kt @@ -0,0 +1,13 @@ +// EXTRACTION_TARGET: property with initializer + +class A(val n: Int = 1) { + val m: Int = 2 + + fun foo(): Int { + return if (n > 1) { + println(n) + m + n + 1 + } else 0 + } +} + diff --git a/idea/testData/refactoring/introduceProperty/extractWithInitializerAndBlock.kt.conflicts b/idea/testData/refactoring/introduceProperty/extractWithInitializerAndBlock.kt.conflicts new file mode 100644 index 00000000000..d8dc5e86314 --- /dev/null +++ b/idea/testData/refactoring/introduceProperty/extractWithInitializerAndBlock.kt.conflicts @@ -0,0 +1 @@ +Can't generate property with initializer: println(n) m + n + 1 \ No newline at end of file diff --git a/idea/testData/refactoring/introduceProperty/extractWithInitializerAndCallSiteReturn.kt b/idea/testData/refactoring/introduceProperty/extractWithInitializerAndCallSiteReturn.kt new file mode 100644 index 00000000000..895c13cc881 --- /dev/null +++ b/idea/testData/refactoring/introduceProperty/extractWithInitializerAndCallSiteReturn.kt @@ -0,0 +1,11 @@ +// EXTRACTION_TARGET: property with initializer + +class A(val n: Int = 1) { + val m: Int = 2 + + fun foo(): Int { + val t = return m + n + 1 + return t + } +} + diff --git a/idea/testData/refactoring/introduceProperty/extractWithInitializerAndCallSiteReturn.kt.conflicts b/idea/testData/refactoring/introduceProperty/extractWithInitializerAndCallSiteReturn.kt.conflicts new file mode 100644 index 00000000000..aa6bad9ea08 --- /dev/null +++ b/idea/testData/refactoring/introduceProperty/extractWithInitializerAndCallSiteReturn.kt.conflicts @@ -0,0 +1 @@ +Can't generate property with initializer: return m + n + 1 \ No newline at end of file diff --git a/idea/testData/refactoring/introduceProperty/extractWithInitializerAndSingleElementBlock.kt b/idea/testData/refactoring/introduceProperty/extractWithInitializerAndSingleElementBlock.kt new file mode 100644 index 00000000000..33c7d829242 --- /dev/null +++ b/idea/testData/refactoring/introduceProperty/extractWithInitializerAndSingleElementBlock.kt @@ -0,0 +1,12 @@ +// EXTRACTION_TARGET: property with initializer + +class A(val n: Int = 1) { + val m: Int = 2 + + fun foo(): Int { + return if (n > 1) { + m + n + 1 + } else 0 + } +} + diff --git a/idea/testData/refactoring/introduceProperty/extractWithInitializerAndSingleElementBlock.kt.after b/idea/testData/refactoring/introduceProperty/extractWithInitializerAndSingleElementBlock.kt.after new file mode 100644 index 00000000000..45f90de3516 --- /dev/null +++ b/idea/testData/refactoring/introduceProperty/extractWithInitializerAndSingleElementBlock.kt.after @@ -0,0 +1,14 @@ +// EXTRACTION_TARGET: property with initializer + +class A(val n: Int = 1) { + val m: Int = 2 + + fun foo(): Int { + return if (n > 1) { + i + } else 0 + } + + private val i = m + n + 1 +} + diff --git a/idea/testData/refactoring/introduceProperty/extractWithInitializerMultipleExpressions.kt b/idea/testData/refactoring/introduceProperty/extractWithInitializerMultipleExpressions.kt new file mode 100644 index 00000000000..ffec09b1478 --- /dev/null +++ b/idea/testData/refactoring/introduceProperty/extractWithInitializerMultipleExpressions.kt @@ -0,0 +1,9 @@ +// EXTRACTION_TARGET: property with initializer +class A { + fun foo(): Int { + val a = 1 + 2 + val b = a*2 + val c = b - 1 + } +} + diff --git a/idea/testData/refactoring/introduceProperty/extractWithInitializerMultipleExpressions.kt.conflicts b/idea/testData/refactoring/introduceProperty/extractWithInitializerMultipleExpressions.kt.conflicts new file mode 100644 index 00000000000..4d9d47e0b1d --- /dev/null +++ b/idea/testData/refactoring/introduceProperty/extractWithInitializerMultipleExpressions.kt.conflicts @@ -0,0 +1 @@ +Can't generate property with initializer: val a = 1 + 2 val b = a*2 \ No newline at end of file diff --git a/idea/testData/refactoring/introduceProperty/extractWithInitializerToClass.kt b/idea/testData/refactoring/introduceProperty/extractWithInitializerToClass.kt new file mode 100644 index 00000000000..32ce0c6a159 --- /dev/null +++ b/idea/testData/refactoring/introduceProperty/extractWithInitializerToClass.kt @@ -0,0 +1,10 @@ +// EXTRACTION_TARGET: property with initializer + +class A(val n: Int = 1) { + val m: Int = 2 + + fun foo(): Int { + return m + n + 1 + } +} + diff --git a/idea/testData/refactoring/introduceProperty/extractWithInitializerToClass.kt.after b/idea/testData/refactoring/introduceProperty/extractWithInitializerToClass.kt.after new file mode 100644 index 00000000000..d80514cfe9b --- /dev/null +++ b/idea/testData/refactoring/introduceProperty/extractWithInitializerToClass.kt.after @@ -0,0 +1,12 @@ +// EXTRACTION_TARGET: property with initializer + +class A(val n: Int = 1) { + val m: Int = 2 + + fun foo(): Int { + return i + } + + private val i = m + n + 1 +} + diff --git a/idea/testData/refactoring/introduceProperty/extractWithInitializerToFile.kt b/idea/testData/refactoring/introduceProperty/extractWithInitializerToFile.kt new file mode 100644 index 00000000000..d1f919482c9 --- /dev/null +++ b/idea/testData/refactoring/introduceProperty/extractWithInitializerToFile.kt @@ -0,0 +1,7 @@ +// EXTRACTION_TARGET: property with initializer + +val n: Int = 1 + +fun foo(): Int { + return n + 1 +} \ No newline at end of file diff --git a/idea/testData/refactoring/introduceProperty/extractWithInitializerToFile.kt.after b/idea/testData/refactoring/introduceProperty/extractWithInitializerToFile.kt.after new file mode 100644 index 00000000000..046b82b12b3 --- /dev/null +++ b/idea/testData/refactoring/introduceProperty/extractWithInitializerToFile.kt.after @@ -0,0 +1,9 @@ +// EXTRACTION_TARGET: property with initializer + +val n: Int = 1 + +fun foo(): Int { + return i +} + +private val i = n + 1 \ No newline at end of file diff --git a/idea/testData/refactoring/introduceProperty/extractWithParams.kt.conflicts b/idea/testData/refactoring/introduceProperty/extractWithParams.kt.conflicts index af481ebcb1f..e8507994df2 100644 --- a/idea/testData/refactoring/introduceProperty/extractWithParams.kt.conflicts +++ b/idea/testData/refactoring/introduceProperty/extractWithParams.kt.conflicts @@ -1 +1 @@ -Can't generate property: n + m + 1 \ No newline at end of file +Can't generate property with getter: n + m + 1 \ No newline at end of file diff --git a/idea/tests/org/jetbrains/kotlin/idea/refactoring/introduce/introduceVariable/AbstractJetExtractionTest.kt b/idea/tests/org/jetbrains/kotlin/idea/refactoring/introduce/introduceVariable/AbstractJetExtractionTest.kt index e16d252a130..4d75f511d40 100644 --- a/idea/tests/org/jetbrains/kotlin/idea/refactoring/introduce/introduceVariable/AbstractJetExtractionTest.kt +++ b/idea/tests/org/jetbrains/kotlin/idea/refactoring/introduce/introduceVariable/AbstractJetExtractionTest.kt @@ -61,6 +61,9 @@ public abstract class AbstractJetExtractionTest() : JetLightCodeInsightFixtureTe protected fun doIntroducePropertyTest(path: String) { doTest(path) { file -> + val extractionTarget = propertyTargets.single { + it.name == InTextDirectivesUtils.findStringWithPrefixes(file.getText(), "// EXTRACTION_TARGET: ") + } val helper = object : ExtractionEngineHelper() { override fun configure( descriptor: ExtractableCodeDescriptor, @@ -68,7 +71,7 @@ public abstract class AbstractJetExtractionTest() : JetLightCodeInsightFixtureTe ): ExtractionGeneratorConfiguration { return ExtractionGeneratorConfiguration( descriptor, - generatorOptions.copy(extractAsProperty = true) + generatorOptions.copy(target = extractionTarget) ) } } diff --git a/idea/tests/org/jetbrains/kotlin/idea/refactoring/introduce/introduceVariable/JetExtractionTestGenerated.java b/idea/tests/org/jetbrains/kotlin/idea/refactoring/introduce/introduceVariable/JetExtractionTestGenerated.java index 4e7412838c5..b62542fb24c 100644 --- a/idea/tests/org/jetbrains/kotlin/idea/refactoring/introduce/introduceVariable/JetExtractionTestGenerated.java +++ b/idea/tests/org/jetbrains/kotlin/idea/refactoring/introduce/introduceVariable/JetExtractionTestGenerated.java @@ -1924,6 +1924,36 @@ public class JetExtractionTestGenerated extends AbstractJetExtractionTest { Pattern.compile("^(.+)\\.kt$"), true); } + @TestMetadata("extractLazyMultipleExpressions.kt") + public void testExtractLazyMultipleExpressions() throws Exception { + String fileName = JetTestUtils.navigationMetadata("idea/testData/refactoring/introduceProperty/extractLazyMultipleExpressions.kt"); + doIntroducePropertyTest(fileName); + } + + @TestMetadata("extractLazyToClass.kt") + public void testExtractLazyToClass() throws Exception { + String fileName = JetTestUtils.navigationMetadata("idea/testData/refactoring/introduceProperty/extractLazyToClass.kt"); + doIntroducePropertyTest(fileName); + } + + @TestMetadata("extractLazyToFile.kt") + public void testExtractLazyToFile() throws Exception { + String fileName = JetTestUtils.navigationMetadata("idea/testData/refactoring/introduceProperty/extractLazyToFile.kt"); + doIntroducePropertyTest(fileName); + } + + @TestMetadata("extractLazyWithBlock.kt") + public void testExtractLazyWithBlock() throws Exception { + String fileName = JetTestUtils.navigationMetadata("idea/testData/refactoring/introduceProperty/extractLazyWithBlock.kt"); + doIntroducePropertyTest(fileName); + } + + @TestMetadata("extractLazyWithCallSiteReturn.kt") + public void testExtractLazyWithCallSiteReturn() throws Exception { + String fileName = JetTestUtils.navigationMetadata("idea/testData/refactoring/introduceProperty/extractLazyWithCallSiteReturn.kt"); + doIntroducePropertyTest(fileName); + } + @TestMetadata("extractToClassWithNameClash.kt") public void testExtractToClassWithNameClash() throws Exception { String fileName = JetTestUtils.navigationMetadata("idea/testData/refactoring/introduceProperty/extractToClassWithNameClash.kt"); @@ -1966,6 +1996,42 @@ public class JetExtractionTestGenerated extends AbstractJetExtractionTest { doIntroducePropertyTest(fileName); } + @TestMetadata("extractWithInitializerAndBlock.kt") + public void testExtractWithInitializerAndBlock() throws Exception { + String fileName = JetTestUtils.navigationMetadata("idea/testData/refactoring/introduceProperty/extractWithInitializerAndBlock.kt"); + doIntroducePropertyTest(fileName); + } + + @TestMetadata("extractWithInitializerAndCallSiteReturn.kt") + public void testExtractWithInitializerAndCallSiteReturn() throws Exception { + String fileName = JetTestUtils.navigationMetadata("idea/testData/refactoring/introduceProperty/extractWithInitializerAndCallSiteReturn.kt"); + doIntroducePropertyTest(fileName); + } + + @TestMetadata("extractWithInitializerAndSingleElementBlock.kt") + public void testExtractWithInitializerAndSingleElementBlock() throws Exception { + String fileName = JetTestUtils.navigationMetadata("idea/testData/refactoring/introduceProperty/extractWithInitializerAndSingleElementBlock.kt"); + doIntroducePropertyTest(fileName); + } + + @TestMetadata("extractWithInitializerMultipleExpressions.kt") + public void testExtractWithInitializerMultipleExpressions() throws Exception { + String fileName = JetTestUtils.navigationMetadata("idea/testData/refactoring/introduceProperty/extractWithInitializerMultipleExpressions.kt"); + doIntroducePropertyTest(fileName); + } + + @TestMetadata("extractWithInitializerToClass.kt") + public void testExtractWithInitializerToClass() throws Exception { + String fileName = JetTestUtils.navigationMetadata("idea/testData/refactoring/introduceProperty/extractWithInitializerToClass.kt"); + doIntroducePropertyTest(fileName); + } + + @TestMetadata("extractWithInitializerToFile.kt") + public void testExtractWithInitializerToFile() throws Exception { + String fileName = JetTestUtils.navigationMetadata("idea/testData/refactoring/introduceProperty/extractWithInitializerToFile.kt"); + doIntroducePropertyTest(fileName); + } + @TestMetadata("extractWithParams.kt") public void testExtractWithParams() throws Exception { String fileName = JetTestUtils.navigationMetadata("idea/testData/refactoring/introduceProperty/extractWithParams.kt");