Extraction Engine: Support lazy properties and properties with initializer

This commit is contained in:
Alexey Sedunov
2015-03-02 15:56:01 +03:00
parent 03fada71d7
commit 5382504306
35 changed files with 461 additions and 64 deletions
@@ -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
@@ -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()
@@ -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<DuplicateInfo> 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<ExtractionTarget> = 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<PsiElement, String>
)
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
}
@@ -92,7 +92,7 @@ data class ExtractionData(
fun getExpressions(): List<JetExpression> = originalElements.filterIsInstance<JetExpression>()
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()
}
@@ -110,4 +110,4 @@ public fun processDuplicates(
project.executeWriteCommand(MethodDuplicatesHandler.REFACTORING_NAME, replacer)
}
}
}
@@ -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<JetType> {
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<PsiElement, String>()
@@ -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
@@ -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<Int, JetElement>()
@@ -388,7 +419,7 @@ fun ExtractionGeneratorConfiguration.generateDeclaration(): ExtractionResult{
}
fun adjustDeclarationBody(declaration: JetNamedDeclaration) {
val body = declaration.getGeneratedBlockBody()
val body = declaration.getGeneratedBody()
val exprReplacementMap = HashMap<JetElement, (JetElement) -> JetElement>()
val originalOffsetByExpr = LinkedHashMap<JetElement, Int>()
@@ -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<PsiElement>()) { it.range.elements.first() }
@@ -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)
@@ -0,0 +1,10 @@
// WITH_RUNTIME
// EXTRACTION_TARGET: lazy property
class A {
fun foo(): Int {
<selection>val a = 1 + 2
val b = a*2</selection>
val c = b - 1
}
}
@@ -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
}
}
@@ -0,0 +1,11 @@
// WITH_RUNTIME
// EXTRACTION_TARGET: lazy property
class A(val n: Int = 1) {
val m: Int = 2
fun foo(): Int {
return <selection>m + n + 1</selection>
}
}
@@ -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
}
}
@@ -0,0 +1,8 @@
// WITH_RUNTIME
// EXTRACTION_TARGET: lazy property
val n: Int = 1
fun foo(): Int {
return <selection>n + 1</selection>
}
@@ -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
}
@@ -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) <selection>{
println(n)
m + n + 1
}</selection> else 0
}
}
@@ -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
}
}
@@ -0,0 +1,10 @@
// EXTRACTION_TARGET: lazy property
class A(val n: Int = 1) {
val m: Int = 2
fun foo(): Int {
<selection>return m + n + 1</selection>
}
}
@@ -0,0 +1 @@
Can't generate lazy property: return m + n + 1
@@ -1 +1 @@
Can't generate property: n + 1
Can't generate property with getter: n + 1
@@ -1 +1 @@
Can't generate property: 1 + 1
Can't generate property with getter: 1 + 1
@@ -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) <selection>{
println(n)
m + n + 1
}</selection> else 0
}
}
@@ -0,0 +1 @@
Can't generate property with initializer: println(n) m + n + 1
@@ -0,0 +1,11 @@
// EXTRACTION_TARGET: property with initializer
class A(val n: Int = 1) {
val m: Int = 2
fun foo(): Int {
val t = <selection>return m + n + 1</selection>
return t
}
}
@@ -0,0 +1 @@
Can't generate property with initializer: return m + n + 1
@@ -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) <selection>{
m + n + 1
}</selection> else 0
}
}
@@ -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
}
@@ -0,0 +1,9 @@
// EXTRACTION_TARGET: property with initializer
class A {
fun foo(): Int {
<selection>val a = 1 + 2
val b = a*2</selection>
val c = b - 1
}
}
@@ -0,0 +1 @@
Can't generate property with initializer: val a = 1 + 2 val b = a*2
@@ -0,0 +1,10 @@
// EXTRACTION_TARGET: property with initializer
class A(val n: Int = 1) {
val m: Int = 2
fun foo(): Int {
return <selection>m + n + 1</selection>
}
}
@@ -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
}
@@ -0,0 +1,7 @@
// EXTRACTION_TARGET: property with initializer
val n: Int = 1
fun foo(): Int {
return <selection>n + 1</selection>
}
@@ -0,0 +1,9 @@
// EXTRACTION_TARGET: property with initializer
val n: Int = 1
fun foo(): Int {
return i
}
private val i = n + 1
@@ -1 +1 @@
Can't generate property: n + m + 1
Can't generate property with getter: n + m + 1
@@ -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)
)
}
}
@@ -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");