Extract Function: Support multiple output values

This commit is contained in:
Alexey Sedunov
2014-09-04 17:28:25 +04:00
parent 7a15a88288
commit 76433571f8
68 changed files with 1293 additions and 319 deletions
@@ -62,11 +62,11 @@ fun getFunctionForExtractedFragment(
ErrorMessage.SUPER_CALL -> "Cannot perform an action for expression with super call"
ErrorMessage.DENOTABLE_TYPES -> "Cannot perform an action because following types are unavailable from debugger scope"
ErrorMessage.ERROR_TYPES -> "Cannot perform an action because this code fragment contains erroneous types"
ErrorMessage.MULTIPLE_OUTPUT -> "Cannot perform an action because this code fragment changes more than one variable"
ErrorMessage.DECLARATIONS_OUT_OF_SCOPE,
ErrorMessage.OUTPUT_AND_EXIT_POINT,
ErrorMessage.MULTIPLE_EXIT_POINTS,
ErrorMessage.DECLARATIONS_ARE_USED_OUTSIDE -> "Cannot perform an action for this expression"
else -> throw AssertionError("Unexpected error: $errorMessage")
}
errorMessage.additionalInfo?.let { "$message: ${it.joinToString(", ")}" } ?: message
}.joinToString(", ")
@@ -102,7 +102,7 @@ fun getFunctionForExtractedFragment(
if (targetSibling == null) return null
val analysisResult = ExtractionData(
tmpFile, Collections.singletonList(newDebugExpression), targetSibling, ExtractionOptions(false)
tmpFile, Collections.singletonList(newDebugExpression), targetSibling, ExtractionOptions(false, true)
).performAnalysis()
if (analysisResult.status != Status.SUCCESS) {
throw EvaluateExceptionUtil.createEvaluateException(getErrorMessageForExtractFunctionResult(analysisResult))
@@ -9,7 +9,7 @@ cannot.refactor.package.expression=Cannot introduce package reference
extract.function=Extract Function
cannot.extract.method=Cannot find statements to extract
cannot.find.class.to.extract=Cannot find class to extract method
selected.code.fragment.has.multiple.output.values=Selected code fragment has multiple output values:
selected.code.fragment.has.multiple.output.values=Selected code fragment has more than 3 output values:
declarations.are.used.outside.of.selected.code.fragment=Following declarations are used outside of selected code fragment:
selected.code.fragment.has.multiple.exit.points=Selected code fragment has multiple exit points
selected.code.fragment.has.output.values.and.exit.points=Selected code fragment has output values as well as alternative exit points
@@ -69,6 +69,7 @@ import org.jetbrains.jet.lang.psi.JetClassOrObject
import org.jetbrains.jet.lang.psi.JetMultiDeclaration
public open class ExtractKotlinFunctionHandlerHelper {
open fun adjustExtractionData(data: ExtractionData): ExtractionData = data
open fun adjustGeneratorOptions(options: ExtractionGeneratorOptions): ExtractionGeneratorOptions = options
open fun adjustDescriptor(descriptor: ExtractableCodeDescriptor): ExtractableCodeDescriptor = descriptor
@@ -88,7 +89,7 @@ public class ExtractKotlinFunctionHandler(
) {
val project = file.getProject()
val analysisResult = ExtractionData(file, elements, targetSibling).performAnalysis()
val analysisResult = helper.adjustExtractionData(ExtractionData(file, elements, targetSibling)).performAnalysis()
if (ApplicationManager.getApplication()!!.isUnitTestMode() && analysisResult.status != Status.SUCCESS) {
throw ConflictsInTestsException(analysisResult.messages.map { it.renderMessage() })
@@ -41,6 +41,29 @@ import com.intellij.openapi.util.text.StringUtil
import org.jetbrains.jet.lang.psi.JetClassBody
import org.jetbrains.jet.lang.psi.JetFile
import org.jetbrains.jet.lang.psi.JetNamedDeclaration
import org.jetbrains.jet.lang.resolve.name.Name
import org.jetbrains.jet.lang.types.CommonSupertypes
import org.jetbrains.jet.lang.types.TypeSubstitutor
import org.jetbrains.jet.lang.types.JetTypeImpl
import org.jetbrains.jet.lang.descriptors.annotations.Annotations
import java.util.Collections
import org.jetbrains.jet.lang.types.TypeProjectionImpl
import org.jetbrains.jet.lang.types.Variance
import org.jetbrains.jet.lang.descriptors.ClassDescriptor
import org.jetbrains.jet.lang.types.TypeProjection
import org.jetbrains.jet.lang.types.TypeConstructor
import org.jetbrains.jet.lang.psi.JetExpression
import org.jetbrains.jet.lang.resolve.DescriptorUtils
import org.jetbrains.jet.plugin.refactoring.extractFunction.OutputValue.ExpressionValue
import org.jetbrains.jet.lang.psi.JetReturnExpression
import org.jetbrains.jet.plugin.refactoring.extractFunction.OutputValue.Jump
import org.jetbrains.jet.lang.descriptors.ModuleDescriptor
import org.jetbrains.jet.lang.resolve.lazy.ResolveSessionUtils
import org.jetbrains.jet.lang.types.TypeUtils
import kotlin.properties.Delegates
import com.intellij.util.containers.ContainerUtil
import org.jetbrains.jet.lang.psi.JetCallElement
import org.jetbrains.jet.lang.psi.psiUtil.getQualifiedElementSelector
trait Parameter {
val argumentText: String
@@ -99,61 +122,170 @@ class FqNameReplacement(val fqName: FqName): Replacement {
}
}
trait ControlFlow {
val returnType: JetType
val declarationsToCopy: List<JetDeclaration>
trait OutputValue {
val valueType: JetType
fun toDefault(): ControlFlow = DefaultControlFlow(returnType, declarationsToCopy)
class ExpressionValue(
val callSiteReturn: Boolean,
override val valueType: JetType
): OutputValue
class Jump(
val elementsToReplace: List<JetElement>,
val elementToInsertAfterCall: JetElement,
val conditional: Boolean
): OutputValue {
override val valueType: JetType = with(KotlinBuiltIns.getInstance()) { if (conditional) getBooleanType() else getUnitType() }
}
class ParameterUpdate(val parameter: Parameter): OutputValue {
override val valueType: JetType get() = parameter.parameterType
}
class Initializer(
val initializedDeclaration: JetProperty,
override val valueType: JetType
): OutputValue
}
class DefaultControlFlow(
override val returnType: JetType = DEFAULT_RETURN_TYPE,
override val declarationsToCopy: List<JetDeclaration>
): ControlFlow
abstract class OutputValueBoxer(val outputValues: List<OutputValue>) {
val outputValueTypes: List<JetType> get() = outputValues.map { it.valueType }
trait JumpBasedControlFlow : ControlFlow {
val elementsToReplace: List<JetElement>
val elementToInsertAfterCall: JetElement
abstract val returnType: JetType
protected abstract fun getBoxingExpressionText(arguments: List<String>): String?
fun getReturnExpression(arguments: List<String>, psiFactory: JetPsiFactory): JetReturnExpression? {
return getBoxingExpressionText(arguments)?.let { psiFactory.createReturn(it) }
}
protected abstract fun extractExpressionByIndex(boxedExpression: JetExpression, index: Int): JetExpression?
protected fun extractArgumentExpressionByIndex(boxedExpression: JetExpression, index: Int): JetExpression? {
val call: JetCallExpression? = when (boxedExpression) {
is JetCallExpression -> boxedExpression
is JetQualifiedExpression -> boxedExpression.getSelectorExpression() as? JetCallExpression
else -> null
}
val arguments = call?.getValueArguments()
if (arguments == null || arguments.size <= index) return null
return arguments[index].getArgumentExpression()
}
fun extractExpressionByValue(boxedExpression: JetExpression, value: OutputValue): JetExpression? {
val index = outputValues.indexOf(value)
if (index < 0) return null
return extractExpressionByIndex(boxedExpression, index)
}
abstract fun getUnboxingExpressions(boxedText: String): Map<OutputValue, String>
class AsTuple(
outputValues: List<OutputValue>,
val module: ModuleDescriptor
) : OutputValueBoxer(outputValues) {
{
assert(outputValues.size <= 3, "At most 3 output values are supported")
}
class object {
private val selectors = array("first", "second", "third")
}
override val returnType: JetType by Delegates.lazy {
fun getType(): JetType {
val boxingClass = when (outputValues.size) {
1 -> return outputValues.first().valueType
2 -> ResolveSessionUtils.getClassDescriptorsByFqName(module, FqName("kotlin.Pair")).first()
3 -> ResolveSessionUtils.getClassDescriptorsByFqName(module, FqName("kotlin.Triple")).first()
else -> return DEFAULT_RETURN_TYPE
}
return TypeUtils.substituteParameters(boxingClass, outputValueTypes)
}
getType()
}
override fun getBoxingExpressionText(arguments: List<String>): String? {
return when (arguments.size) {
0 -> null
1 -> arguments.first()
else -> {
val constructorName = DescriptorUtils.getFqName(returnType.getConstructor().getDeclarationDescriptor()!!).asString()
return arguments.joinToString(prefix = "$constructorName(", separator = ", ", postfix = ")")
}
}
}
override fun extractExpressionByIndex(boxedExpression: JetExpression, index: Int): JetExpression? {
if (outputValues.size() == 1) return boxedExpression
return extractArgumentExpressionByIndex(boxedExpression, index)
}
override fun getUnboxingExpressions(boxedText: String): Map<OutputValue, String> {
return when (outputValues.size) {
0 -> Collections.emptyMap()
1 -> Collections.singletonMap(outputValues.first(), boxedText)
else -> {
var i = 0
ContainerUtil.newMapFromKeys(outputValues.iterator()) { "$boxedText.${selectors[i++]}" }
}
}
}
}
class AsList(outputValues: List<OutputValue>): OutputValueBoxer(outputValues) {
override val returnType: JetType by Delegates.lazy {
if (outputValues.isEmpty()) DEFAULT_RETURN_TYPE
else TypeUtils.substituteParameters(
KotlinBuiltIns.getInstance().getList(),
Collections.singletonList(CommonSupertypes.commonSupertype(outputValues.map { it.valueType }))
)
}
override fun getBoxingExpressionText(arguments: List<String>): String? {
if (arguments.isEmpty()) return null
return arguments.joinToString(prefix = "kotlin.listOf(", separator = ", ", postfix = ")")
}
override fun extractExpressionByIndex(boxedExpression: JetExpression, index: Int): JetExpression? {
return extractArgumentExpressionByIndex(boxedExpression, index)
}
override fun getUnboxingExpressions(boxedText: String): Map<OutputValue, String> {
var i = 0
return ContainerUtil.newMapFromKeys(outputValues.iterator()) { "$boxedText[${i++}]" }
}
}
}
class ConditionalJump(
override val elementsToReplace: List<JetElement>,
override val elementToInsertAfterCall: JetElement,
override val declarationsToCopy: List<JetDeclaration>
): JumpBasedControlFlow {
override val returnType: JetType get() = KotlinBuiltIns.getInstance().getBooleanType()
data class ControlFlow(
val outputValues: List<OutputValue>,
val boxerFactory: (List<OutputValue>) -> OutputValueBoxer,
val declarationsToCopy: List<JetDeclaration>
) {
val outputValueBoxer = boxerFactory(outputValues)
val defaultOutputValue: ExpressionValue? = with(outputValues.filterIsInstance(javaClass<ExpressionValue>())) {
if (size > 1) throw IllegalArgumentException("Multiple expression values: ${outputValues.joinToString()}") else firstOrNull()
}
val jumpOutputValue: Jump? = with(outputValues.filterIsInstance(javaClass<Jump>())) {
when {
isEmpty() ->
null
outputValues.size > size || size > 1 ->
throw IllegalArgumentException("Jump values must be the only value if it's present: ${outputValues.joinToString()}")
else ->
first()
}
}
}
class UnconditionalJump(
override val elementsToReplace: List<JetElement>,
override val elementToInsertAfterCall: JetElement,
override val declarationsToCopy: List<JetDeclaration>
): JumpBasedControlFlow {
override val returnType: JetType get() = KotlinBuiltIns.getInstance().getUnitType()
}
class ExpressionEvaluation(
override val returnType: JetType,
override val declarationsToCopy: List<JetDeclaration>
): ControlFlow
class ExpressionEvaluationWithCallSiteReturn(
override val returnType: JetType,
override val declarationsToCopy: List<JetDeclaration>
): ControlFlow
class ParameterUpdate(
val parameter: Parameter,
override val declarationsToCopy: List<JetDeclaration>
): ControlFlow {
override val returnType: JetType get() = parameter.parameterType
}
class Initializer(
val initializedDeclaration: JetProperty,
override val returnType: JetType,
override val declarationsToCopy: List<JetDeclaration>
): ControlFlow
fun ControlFlow.toDefault(): ControlFlow =
copy(outputValues = outputValues.filterNot { it is OutputValue.Jump || it is OutputValue.ExpressionValue })
data class ExtractableCodeDescriptor(
val extractionData: ExtractionData,
@@ -52,9 +52,12 @@ import org.jetbrains.jet.lang.psi.JetClassInitializer
import org.jetbrains.jet.lang.resolve.calls.callUtil.getResolvedCall
import org.jetbrains.jet.lang.resolve.DescriptorToSourceUtils
data class ExtractionOptions(val inferUnitTypeForUnusedValues: Boolean) {
data class ExtractionOptions(
val inferUnitTypeForUnusedValues: Boolean,
val enableListBoxing: Boolean
) {
class object {
val DEFAULT = ExtractionOptions(true)
val DEFAULT = ExtractionOptions(true, false)
}
}
@@ -71,7 +74,7 @@ data class ResolvedReferenceInfo(
val resolveResult: ResolveResult
)
class ExtractionData(
data class ExtractionData(
val originalFile: JetFile,
val originalElements: List<PsiElement>,
val targetSibling: PsiElement,
@@ -37,6 +37,7 @@ import org.jetbrains.jet.lang.cfg.Label
import org.jetbrains.jet.plugin.refactoring.JetNameValidatorImpl
import org.jetbrains.jet.plugin.codeInsight.DescriptorToDeclarationUtil
import org.jetbrains.jet.plugin.imports.canBeReferencedViaImport
import org.jetbrains.jet.lang.resolve.DescriptorUtils
import com.intellij.psi.PsiNamedElement
import org.jetbrains.jet.lang.descriptors.impl.LocalVariableDescriptor
import org.jetbrains.jet.utils.DFS
@@ -61,6 +62,24 @@ import org.jetbrains.jet.lang.resolve.bindingContextUtil.isUsedAsStatement
import org.jetbrains.jet.lang.psi.psiUtil.isAncestor
import org.jetbrains.jet.lang.resolve.DescriptorToSourceUtils
import org.jetbrains.jet.plugin.imports.importableFqNameSafe
import org.jetbrains.jet.lang.psi.psiUtil.isFunctionLiteralOutsideParentheses
import org.jetbrains.jet.plugin.util.psiModificationUtil.moveInsideParenthesesAndReplaceWith
import org.jetbrains.jet.lang.resolve.name.Name
import gnu.trove.THashSet
import gnu.trove.TObjectHashingStrategy
import gnu.trove.THashMap
import org.jetbrains.jet.lang.resolve.name.FqName
import org.jetbrains.jet.lang.resolve.lazy.KotlinCodeAnalyzer
import org.jetbrains.jet.lang.resolve.lazy.ResolveSessionUtils
import org.jetbrains.jet.plugin.refactoring.extractFunction.OutputValue.Initializer
import org.jetbrains.jet.plugin.refactoring.extractFunction.OutputValue.ParameterUpdate
import org.jetbrains.jet.plugin.refactoring.extractFunction.OutputValue.ExpressionValue
import org.jetbrains.jet.plugin.refactoring.extractFunction.OutputValue.Jump
import org.jetbrains.jet.lang.cfg.pseudocode.instructions.special.MarkInstruction
import org.jetbrains.jet.lang.cfg.pseudocode.instructions.special.CompilationErrorInstruction
import org.jetbrains.jet.lang.cfg.pseudocodeTraverser.traverseFollowingInstructions
import org.jetbrains.jet.plugin.refactoring.extractFunction.OutputValueBoxer.AsList
import org.jetbrains.jet.lang.cfg.pseudocodeTraverser.getStartInstruction
private val DEFAULT_FUNCTION_NAME = "myFun"
private val DEFAULT_RETURN_TYPE = KotlinBuiltIns.getInstance().getUnitType()
@@ -83,6 +102,28 @@ private fun List<Instruction>.getModifiedVarDescriptors(bindingContext: BindingC
.filterNotNullTo(HashSet<VariableDescriptor>())
}
private fun List<Instruction>.getVarDescriptorsAccessedAfterwards(bindingContext: BindingContext): Set<VariableDescriptor> {
val accessedAfterwards = HashSet<VariableDescriptor>()
val visitedInstructions = HashSet<Instruction>()
fun doTraversal(instruction: Instruction) {
traverseFollowingInstructions(instruction, visitedInstructions, TraversalOrder.FORWARD) {
when {
it is AccessValueInstruction && it !in this ->
PseudocodeUtil.extractVariableDescriptorIfAny(it, false, bindingContext)?.let { accessedAfterwards.add(it) }
it is LocalFunctionDeclarationInstruction ->
doTraversal(it.body.getEnterInstruction())
}
true
}
}
forEach(::doTraversal)
return accessedAfterwards
}
private fun List<Instruction>.getExitPoints(): List<Instruction> =
filter { localInstruction -> localInstruction.nextInstructions.any { it !in this } }
@@ -139,6 +180,7 @@ private fun ExtractionData.getLocalDeclarationsWithNonLocalUsages(
private fun ExtractionData.analyzeControlFlow(
localInstructions: List<Instruction>,
pseudocode: Pseudocode,
module: ModuleDescriptor,
bindingContext: BindingContext,
modifiedVarDescriptors: Set<VariableDescriptor>,
options: ExtractionOptions,
@@ -202,74 +244,91 @@ private fun ExtractionData.analyzeControlFlow(
val typeOfDefaultFlow = defaultExits.getResultType(bindingContext, options)
val returnValueType = valuedReturnExits.getResultType(bindingContext, options)
val defaultReturnType = if (returnValueType.isMeaningful()) returnValueType else typeOfDefaultFlow
if (defaultReturnType.isError()) return Pair(DefaultControlFlow(DEFAULT_RETURN_TYPE, declarationsToCopy), ErrorMessage.ERROR_TYPES)
val emptyControlFlow =
ControlFlow(Collections.emptyList(), { OutputValueBoxer.AsTuple(it, module) }, declarationsToCopy)
val defaultControlFlow = DefaultControlFlow(defaultReturnType, declarationsToCopy)
val defaultReturnType = if (returnValueType.isMeaningful()) returnValueType else typeOfDefaultFlow
if (defaultReturnType.isError()) return emptyControlFlow to ErrorMessage.ERROR_TYPES
val controlFlow = if (defaultReturnType.isMeaningful()) {
emptyControlFlow.copy(outputValues = Collections.singletonList(ExpressionValue(false, defaultReturnType)))
}
else {
emptyControlFlow
}
if (declarationsToReport.isNotEmpty()) {
val localVarStr = declarationsToReport.map { it.renderForMessage(bindingContext)!! }.distinct().sort()
return Pair(defaultControlFlow, ErrorMessage.DECLARATIONS_ARE_USED_OUTSIDE.addAdditionalInfo(localVarStr))
return controlFlow to ErrorMessage.DECLARATIONS_ARE_USED_OUTSIDE.addAdditionalInfo(localVarStr)
}
val outParameters =
parameters.filter { it.mirrorVarName != null && it.originalDescriptor in modifiedVarDescriptors }.sortBy { it.nameForRef }
val outDeclarations =
declarationsToCopy.filter { bindingContext[BindingContext.DECLARATION_TO_DESCRIPTOR, it] in modifiedVarDescriptors }
val modifiedValueCount = outParameters.size + outDeclarations.size
val outParameters = parameters.filterTo(HashSet<Parameter>()) { it.mirrorVarName != null }
val outValuesCount = outDeclarations.size + outParameters.size
when {
outValuesCount > 1 -> {
val outValuesStr =
(outParameters.map { it.originalDescriptor.renderForMessage() }
+ outDeclarations.map { it.renderForMessage(bindingContext)!! }).sort()
return Pair(defaultControlFlow, ErrorMessage.MULTIPLE_OUTPUT.addAdditionalInfo(outValuesStr))
}
val outputValues = ArrayList<OutputValue>()
outValuesCount == 1 -> {
if (returnValueType.isMeaningful() || typeOfDefaultFlow.isMeaningful() || jumpExits.isNotEmpty()) {
return Pair(defaultControlFlow, ErrorMessage.OUTPUT_AND_EXIT_POINT)
}
val controlFlow =
outDeclarations.firstOrNull()?.let {
val descriptor = bindingContext[BindingContext.DECLARATION_TO_DESCRIPTOR, it] as? CallableDescriptor
Initializer(it as JetProperty, descriptor?.getReturnType() ?: DEFAULT_PARAMETER_TYPE, declarationsToCopy)
} ?: ParameterUpdate(outParameters.first(), declarationsToCopy)
return Pair(controlFlow, null)
}
}
val multipleExitsError = Pair(defaultControlFlow, ErrorMessage.MULTIPLE_EXIT_POINTS)
val multipleExitsError = controlFlow to ErrorMessage.MULTIPLE_EXIT_POINTS
val outputAndExitsError = controlFlow to ErrorMessage.OUTPUT_AND_EXIT_POINT
if (typeOfDefaultFlow.isMeaningful()) {
if (valuedReturnExits.isNotEmpty() || jumpExits.isNotEmpty()) return multipleExitsError
return Pair(ExpressionEvaluation(typeOfDefaultFlow, declarationsToCopy), null)
outputValues.add(ExpressionValue(false, typeOfDefaultFlow))
}
if (valuedReturnExits.isNotEmpty()) {
else if (valuedReturnExits.isNotEmpty()) {
if (jumpExits.isNotEmpty()) return multipleExitsError
if (defaultExits.isNotEmpty()) {
if (modifiedValueCount != 0) return outputAndExitsError
if (valuedReturnExits.size != 1) return multipleExitsError
val element = valuedReturnExits.first!!.element
return Pair(ConditionalJump(listOf(element), element, declarationsToCopy), null)
return controlFlow.copy(outputValues = Collections.singletonList(Jump(listOf(element), element, true))) to null
}
if (!valuedReturnExits.checkEquivalence(false)) return multipleExitsError
return Pair(ExpressionEvaluationWithCallSiteReturn(returnValueType, declarationsToCopy), null)
outputValues.add(ExpressionValue(true, returnValueType))
}
outDeclarations.mapTo(outputValues) {
val descriptor = bindingContext[BindingContext.DECLARATION_TO_DESCRIPTOR, it] as? CallableDescriptor
Initializer(it as JetProperty, descriptor?.getReturnType() ?: DEFAULT_PARAMETER_TYPE)
}
outParameters.mapTo(outputValues) { ParameterUpdate(it) }
if (outputValues.isNotEmpty()) {
if (jumpExits.isNotEmpty()) return outputAndExitsError
val boxerFactory: (List<OutputValue>) -> OutputValueBoxer = when {
outputValues.size > 3 -> {
if (!options.enableListBoxing) {
val outValuesStr =
(outParameters.map { it.originalDescriptor.renderForMessage() }
+ outDeclarations.map { it.renderForMessage(bindingContext)!! }).sort()
return controlFlow to ErrorMessage.MULTIPLE_OUTPUT.addAdditionalInfo(outValuesStr)
}
OutputValueBoxer::AsList
}
else -> controlFlow.boxerFactory
}
return controlFlow.copy(outputValues = outputValues, boxerFactory = boxerFactory) to null
}
if (jumpExits.isNotEmpty()) {
if (!jumpExits.checkEquivalence(true)) return multipleExitsError
val elements = jumpExits.map { it.element }
if (defaultExits.isNotEmpty()) return Pair(ConditionalJump(elements, elements.first!!, declarationsToCopy), null)
return Pair(UnconditionalJump(elements, elements.first!!, declarationsToCopy), null)
return controlFlow.copy(
outputValues = Collections.singletonList(Jump(elements, elements.first(), defaultExits.isNotEmpty()))
) to null
}
return Pair(defaultControlFlow, null)
return controlFlow to null
}
fun ExtractionData.createTemporaryDeclaration(functionText: String): JetNamedDeclaration {
@@ -545,20 +604,18 @@ private fun ExtractionData.checkDeclarationsMovingOutOfScope(
bindingContext: BindingContext
): ErrorMessage? {
val declarationsOutOfScope = HashSet<JetNamedDeclaration>()
if (controlFlow is JumpBasedControlFlow) {
controlFlow.elementToInsertAfterCall.accept(
object: JetTreeVisitorVoid() {
override fun visitSimpleNameExpression(expression: JetSimpleNameExpression) {
val target = expression.getReference()?.resolve()
if (target is JetNamedDeclaration
&& target.isInsideOf(originalElements)
&& target.getParentByType(javaClass<JetDeclaration>(), true) == enclosingDeclaration) {
declarationsOutOfScope.add(target)
}
controlFlow.jumpOutputValue?.elementToInsertAfterCall?.accept(
object : JetTreeVisitorVoid() {
override fun visitSimpleNameExpression(expression: JetSimpleNameExpression) {
val target = expression.getReference()?.resolve()
if (target is JetNamedDeclaration
&& target.isInsideOf(originalElements)
&& target.getParentByType(javaClass<JetDeclaration>(), true) == enclosingDeclaration) {
declarationsOutOfScope.add(target)
}
}
)
}
}
)
if (declarationsOutOfScope.isNotEmpty()) {
val declStr = declarationsOutOfScope.map { it.renderForMessage(bindingContext)!! }.sort()
@@ -607,7 +664,8 @@ fun ExtractionData.performAnalysis(): AnalysisResult {
}
else -> return noContainerError
}
val bindingContext = originalFile.getLazyResolveSession().resolveToElement(bodyElement)
val resolveSession = originalFile.getLazyResolveSession()
val bindingContext = resolveSession.resolveToElement(bodyElement)
val pseudocodeDeclaration = PsiTreeUtil.getParentOfType(
commonParent, javaClass<JetDeclarationWithBody>(), javaClass<JetClassOrObject>()
@@ -626,10 +684,18 @@ fun ExtractionData.performAnalysis(): AnalysisResult {
val messages = ArrayList<ErrorMessage>()
val (controlFlow, controlFlowMessage) =
analyzeControlFlow(localInstructions, pseudocode, bindingContext, modifiedVarDescriptors, options, paramsInfo.parameters)
analyzeControlFlow(
localInstructions,
pseudocode,
resolveSession.getModuleDescriptor(),
bindingContext,
modifiedVarDescriptors intersect localInstructions.getVarDescriptorsAccessedAfterwards(bindingContext),
options,
paramsInfo.parameters
)
controlFlowMessage?.let { messages.add(it) }
controlFlow.returnType.processTypeIfExtractable(paramsInfo.typeParameters, paramsInfo.nonDenotableTypes)
controlFlow.outputValueBoxer.returnType.processTypeIfExtractable(paramsInfo.typeParameters, paramsInfo.nonDenotableTypes)
if (paramsInfo.nonDenotableTypes.isNotEmpty()) {
val typeStr = paramsInfo.nonDenotableTypes.map {it.renderForMessage()}.sort()
@@ -648,17 +714,18 @@ fun ExtractionData.performAnalysis(): AnalysisResult {
if (targetSibling is JetClassInitializer) targetSibling.getParent() else targetSibling,
JetNameValidatorImpl.Target.FUNCTIONS_AND_CLASSES
)
val functionName = JetNameSuggester.suggestNames(controlFlow.returnType, functionNameValidator, DEFAULT_FUNCTION_NAME).first()
val functionName = JetNameSuggester.suggestNames(
controlFlow.outputValueBoxer.returnType,
functionNameValidator, DEFAULT_FUNCTION_NAME
).first()
if (controlFlow is JumpBasedControlFlow) {
controlFlow.elementToInsertAfterCall.accept(
object: JetTreeVisitorVoid() {
override fun visitSimpleNameExpression(expression: JetSimpleNameExpression) {
paramsInfo.originalRefToParameter[expression]?.let { it.refCount-- }
}
controlFlow.jumpOutputValue?.elementToInsertAfterCall?.accept(
object : JetTreeVisitorVoid() {
override fun visitSimpleNameExpression(expression: JetSimpleNameExpression) {
paramsInfo.originalRefToParameter[expression]?.let { it.refCount-- }
}
)
}
}
)
val adjustedParameters = paramsInfo.parameters.filterTo(HashSet<Parameter>()) { it.refCount > 0 }
val receiverCandidates = adjustedParameters.filterTo(HashSet<Parameter>()) { it.receiverCandidate }
@@ -43,6 +43,15 @@ import org.jetbrains.jet.lang.psi.psiUtil.prependElement
import org.jetbrains.jet.lang.psi.psiUtil.appendElement
import org.jetbrains.jet.lang.psi.psiUtil.replaced
import org.jetbrains.jet.plugin.intentions.declarations.DeclarationUtils
import org.jetbrains.jet.lang.psi.psiUtil.getParentByTypesAndPredicate
import org.jetbrains.jet.lang.psi.JetBlockExpression
import org.jetbrains.jet.plugin.refactoring.extractFunction.OutputValue.ParameterUpdate
import org.jetbrains.jet.plugin.refactoring.extractFunction.OutputValue.Jump
import org.jetbrains.jet.plugin.refactoring.extractFunction.OutputValue.Initializer
import org.jetbrains.jet.plugin.refactoring.extractFunction.OutputValue.ExpressionValue
import org.jetbrains.jet.lang.psi.JetReturnExpression
import org.jetbrains.jet.plugin.refactoring.JetNameValidatorImpl
import org.jetbrains.jet.plugin.refactoring.JetNameSuggester
fun ExtractableCodeDescriptor.getDeclarationText(
options: ExtractionGeneratorOptions = ExtractionGeneratorOptions.DEFAULT,
@@ -67,7 +76,7 @@ fun ExtractableCodeDescriptor.getDeclarationText(
builder.param(parameter.name, descriptorRenderer.renderType(parameter.parameterType))
}
with(controlFlow.returnType) {
with(controlFlow.outputValueBoxer.returnType) {
if (isDefault() || isError()) builder.noReturnType() else builder.returnType(descriptorRenderer.renderType(this))
}
@@ -115,6 +124,40 @@ fun ExtractableCodeDescriptor.generateDeclaration(options: ExtractionGeneratorOp
}
}
fun getReturnArguments(resultExpression: JetExpression?): List<String> {
return controlFlow.outputValues
.map {
when (it) {
is ExpressionValue -> resultExpression?.getText()
is Jump -> if (it.conditional) "false" else null
is ParameterUpdate -> it.parameter.nameForRef
is Initializer -> it.initializedDeclaration.getName()
else -> throw IllegalArgumentException("Unknown output value: $it")
}
}
.filterNotNull()
}
fun replaceWithReturn(
originalExpression: JetExpression,
replacingExpression: JetReturnExpression
) {
val currentResultExpression =
if (originalExpression is JetReturnExpression) originalExpression.getReturnedExpression() else originalExpression
if (currentResultExpression == null) return
val newResultExpression = controlFlow.defaultOutputValue?.let {
val boxedExpression = originalExpression.replaced(replacingExpression).getReturnedExpression()!!
controlFlow.outputValueBoxer.extractExpressionByValue(boxedExpression, it)
}
if (newResultExpression == null) {
throw AssertionError("Can' replace '${originalExpression.getText()}' with '${replacingExpression.getText()}'")
}
val counterpartMap = createNameCounterpartMap(currentResultExpression, newResultExpression)
nameByOffset.entrySet().forEach { e -> counterpartMap[e.getValue()]?.let { e.setValue(it) } }
}
fun adjustDeclarationBody(declaration: JetNamedDeclaration) {
val body = declaration.getGeneratedBlockBody()
@@ -143,9 +186,11 @@ fun ExtractableCodeDescriptor.generateDeclaration(options: ExtractionGeneratorOp
val replacingReturn: JetExpression?
val expressionsToReplaceWithReturn: List<JetElement>
if (controlFlow is JumpBasedControlFlow) {
replacingReturn = psiFactory.createExpression(if (controlFlow is ConditionalJump) "return true" else "return")
expressionsToReplaceWithReturn = controlFlow.elementsToReplace.map { jumpElement ->
val jumpValue = controlFlow.jumpOutputValue
if (jumpValue != null) {
replacingReturn = psiFactory.createExpression(if (jumpValue.conditional) "return true" else "return")
expressionsToReplaceWithReturn = jumpValue.elementsToReplace.map { jumpElement ->
val offsetInBody = jumpElement.getTextRange()!!.getStartOffset() - extractionData.originalStartOffset!!
val expr = file.findElementAt(bodyOffset + offsetInBody)?.getParentByType(jumpElement.javaClass)
assert(expr != null, "Couldn't find expression at $offsetInBody in '${body.getText()}'")
@@ -170,32 +215,29 @@ fun ExtractableCodeDescriptor.generateDeclaration(options: ExtractionGeneratorOp
}
}
for (param in parameters) {
param.mirrorVarName?.let { varName ->
body.prependElement(psiFactory.createProperty(varName, null, true, param.name))
val firstExpression = body.getStatements().firstOrNull()
if (firstExpression != null) {
for (param in parameters) {
param.mirrorVarName?.let { varName ->
body.addBefore(psiFactory.createProperty(varName, null, true, param.name), firstExpression)
body.addBefore(psiFactory.createNewLine(), firstExpression)
}
}
}
when (controlFlow) {
is ParameterUpdate ->
body.appendElement(psiFactory.createReturn(controlFlow.parameter.nameForRef))
val lastExpression = body.getStatements().lastOrNull() as? JetExpression
if (lastExpression is JetReturnExpression) return
is Initializer ->
body.appendElement(psiFactory.createReturn(controlFlow.initializedDeclaration.getName()!!))
val returnExpression = controlFlow.outputValueBoxer.getReturnExpression(getReturnArguments(lastExpression), psiFactory)
if (returnExpression == null) return
is ConditionalJump ->
body.appendElement(psiFactory.createReturn("false"))
val defaultValue = controlFlow.defaultOutputValue
when {
defaultValue == null ->
body.appendElement(returnExpression)
is ExpressionEvaluation ->
body.getStatements().last?.let {
val newExpr = it.replaced(
psiFactory.createReturn(
it.getText() ?: throw AssertionError("Return expression shouldn't be empty: code fragment = ${body.getText()}")
)
).getReturnedExpression()!!
val counterpartMap = createNameCounterpartMap(it, newExpr)
nameByOffset.entrySet().forEach { e -> counterpartMap[e.getValue()]?.let { e.setValue(it) } }
}
!defaultValue.callSiteReturn ->
replaceWithReturn(lastExpression!!, returnExpression)
}
}
@@ -218,12 +260,7 @@ fun ExtractableCodeDescriptor.generateDeclaration(options: ExtractionGeneratorOp
}
}
fun insertCall(anchor: PsiElement, wrappedCall: JetExpression?) {
if (wrappedCall == null) {
anchor.delete()
return
}
fun insertCall(anchor: PsiElement, wrappedCall: JetExpression) {
val firstExpression = extractionData.getExpressions().firstOrNull()
if (firstExpression?.isFunctionLiteralOutsideParentheses() ?: false) {
val functionLiteralArgument = PsiTreeUtil.getParentOfType(firstExpression, javaClass<JetFunctionLiteralArgument>())!!
@@ -234,9 +271,9 @@ fun ExtractableCodeDescriptor.generateDeclaration(options: ExtractionGeneratorOp
anchor.replace(wrappedCall)
}
fun makeCall(declaration: JetNamedDeclaration): JetNamedDeclaration {
fun makeCall(declaration: JetNamedDeclaration) {
val anchor = extractionData.originalElements.first
if (anchor == null) return declaration
if (anchor == null) return
val anchorParent = anchor.getParent()!!
@@ -255,45 +292,98 @@ fun ExtractableCodeDescriptor.generateDeclaration(options: ExtractionGeneratorOp
else -> name
}
val anchorInBlock = stream(anchor) { it.getParent() }.firstOrNull { it.getParent() is JetBlockExpression }
val block = (anchorInBlock?.getParent() as? JetBlockExpression) ?: anchorParent
val newLine = psiFactory.createNewLine()
val inlinableCall = controlFlow.outputValues.size <= 1
val unboxingExpressions =
if (inlinableCall) {
controlFlow.outputValueBoxer.getUnboxingExpressions(callText)
}
else {
val varNameValidator = JetNameValidatorImpl(block, anchorInBlock, JetNameValidatorImpl.Target.PROPERTIES)
val resultVal = JetNameSuggester.suggestNames(controlFlow.outputValueBoxer.returnType, varNameValidator, null).first()
block.addBefore(psiFactory.createDeclaration("val $resultVal = $callText"), anchorInBlock)
block.addBefore(newLine, anchorInBlock)
controlFlow.outputValueBoxer.getUnboxingExpressions(resultVal)
}
val copiedDeclarations = HashMap<JetDeclaration, JetDeclaration>()
for (decl in controlFlow.declarationsToCopy) {
val declCopy = psiFactory.createDeclaration<JetDeclaration>(decl.getText()!!)
copiedDeclarations[decl] = anchorParent.addBefore(declCopy, anchor) as JetDeclaration
anchorParent.addBefore(psiFactory.createNewLine(), anchor)
copiedDeclarations[decl] = block.addBefore(declCopy, anchorInBlock) as JetDeclaration
block.addBefore(newLine, anchorInBlock)
}
val wrappedCall = when (controlFlow) {
is ExpressionEvaluationWithCallSiteReturn ->
psiFactory.createReturn(callText)
is ParameterUpdate ->
psiFactory.createExpression("${controlFlow.parameter.argumentText} = $callText")
is Initializer -> {
val newDecl = copiedDeclarations[controlFlow.initializedDeclaration] as JetProperty
newDecl.replace(DeclarationUtils.changePropertyInitializer(newDecl, psiFactory.createExpression(callText)))
null
}
is ConditionalJump ->
psiFactory.createExpression("if ($callText) ${controlFlow.elementToInsertAfterCall.getText()}")
is UnconditionalJump -> {
anchorParent.addAfter(
psiFactory.createExpression(controlFlow.elementToInsertAfterCall.getText()!!),
anchor
)
anchorParent.addAfter(psiFactory.createNewLine(), anchor)
psiFactory.createExpression(callText)
}
else ->
psiFactory.createExpression(callText)
if (controlFlow.outputValues.isEmpty()) {
anchor.replace(psiFactory.createExpression(callText))
return
}
insertCall(anchor, wrappedCall)
return declaration
fun wrapCall(outputValue: OutputValue, callText: String): List<PsiElement> {
return when (outputValue) {
is OutputValue.ExpressionValue ->
Collections.singletonList(
if (outputValue.callSiteReturn) psiFactory.createReturn(callText) else psiFactory.createExpression(callText)
)
is ParameterUpdate ->
Collections.singletonList(
psiFactory.createExpression("${outputValue.parameter.argumentText} = $callText")
)
is Jump -> {
if (outputValue.conditional) {
Collections.singletonList(
psiFactory.createExpression("if ($callText) ${outputValue.elementToInsertAfterCall.getText()}")
)
}
else {
listOf(
psiFactory.createExpression(callText),
newLine,
psiFactory.createExpression(outputValue.elementToInsertAfterCall.getText()!!)
)
}
}
is Initializer -> {
val newProperty = copiedDeclarations[outputValue.initializedDeclaration] as JetProperty
newProperty.replace(DeclarationUtils.changePropertyInitializer(newProperty, psiFactory.createExpression(callText)))
Collections.emptyList()
}
else -> throw IllegalArgumentException("Unknown output value: $outputValue")
}
}
val defaultValue = controlFlow.defaultOutputValue
controlFlow.outputValues
.filter { it != defaultValue }
.flatMap { wrapCall(it, unboxingExpressions[it]!!) }
.withIndices()
.forEach {
val (i, e) = it
if (i > 0) {
block.addBefore(newLine, anchorInBlock)
}
block.addBefore(e, anchorInBlock)
}
defaultValue?.let {
if (!inlinableCall) {
block.addBefore(newLine, anchorInBlock)
}
insertCall(anchor, wrapCall(it, unboxingExpressions[it]!!).first() as JetExpression)
}
if (anchor.isValid()) {
anchor.delete()
}
}
val declaration = createDeclaration()
@@ -301,7 +391,8 @@ fun ExtractableCodeDescriptor.generateDeclaration(options: ExtractionGeneratorOp
if (options.inTempFile) return ExtractionResult(declaration, nameByOffset)
val declarationInPlace = makeCall(insertDeclaration(declaration))
val declarationInPlace = insertDeclaration(declaration)
makeCall(declarationInPlace)
ShortenReferences.process(declarationInPlace)
return ExtractionResult(declaration, nameByOffset)
}
@@ -40,6 +40,7 @@ import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.ItemEvent;
import java.awt.event.ItemListener;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
@@ -226,13 +227,15 @@ public class KotlinExtractFunctionDialog extends DialogWrapper {
}
ControlFlow controlFlow = descriptor.getControlFlow();
if (controlFlow instanceof ParameterUpdate) {
ParameterUpdate parameterUpdate = (ParameterUpdate) controlFlow;
controlFlow = new ParameterUpdate(
oldToNewParameters.get(parameterUpdate.getParameter()),
parameterUpdate.getDeclarationsToCopy()
);
List<OutputValue> outputValues = new ArrayList<OutputValue>(controlFlow.getOutputValues());
for (int i = 0; i < outputValues.size(); i++) {
OutputValue outputValue = outputValues.get(i);
if (outputValue instanceof OutputValue.ParameterUpdate) {
OutputValue.ParameterUpdate parameterUpdate = (OutputValue.ParameterUpdate) outputValue;
outputValues.set(i, new OutputValue.ParameterUpdate(oldToNewParameters.get(parameterUpdate.getParameter())));
}
}
controlFlow = new ControlFlow(outputValues, controlFlow.getBoxerFactory(), controlFlow.getDeclarationsToCopy());
Map<Integer, Replacement> replacementMap = ContainerUtil.newHashMap();
for (Map.Entry<Integer, Replacement> e : descriptor.getReplacementMap().entrySet()) {
@@ -2,6 +2,11 @@ LineBreakpoint created at errors.kt:13
!JDK_HOME!\bin\java -agentlib:jdwp=transport=dt_socket,address=!HOST_NAME!:!HOST_PORT!,suspend=y,server=n -Dfile.encoding=!FILE_ENCODING! -classpath !APP_PATH!\classes;!KOTLIN_RUNTIME!;!CUSTOM_LIBRARY!;!RT_JAR! errors.ErrorsPackage
Connected to the target VM, address: '!HOST_NAME!:PORT_NAME!', transport: 'socket'
errors.kt:12
Compile bytecode for prop += 1
prop2 +=2
prop + prop2
// RESULT: instance of kotlin.Triple(id=ID): Lkotlin/Triple;
Disconnected from the target VM, address: '!HOST_NAME!:PORT_NAME!', transport: 'socket'
Process finished with exit code 0
@@ -4,6 +4,9 @@ Connected to the target VM, address: '!HOST_NAME!:PORT_NAME!', transport: 'socke
vars.kt:6
Compile bytecode for a
Compile bytecode for a += 1
a
// RESULT: 3: I
Disconnected from the target VM, address: '!HOST_NAME!:PORT_NAME!', transport: 'socket'
Process finished with exit code 0
@@ -2,4 +2,4 @@ prop += 1
prop2 +=2
prop + prop2
// RESULT: Cannot perform an action because this code fragment changes more than one variable: var prop2: Int, var prop: Int
// RESULT: instance of kotlin.Triple(id=ID): Lkotlin/Triple;
@@ -10,8 +10,5 @@ fun main(args: Array<String>) {
// EXPRESSION: a
// RESULT: 2: I
// EXPRESSION: a += 1
// RESULT: 3: I
// EXPRESSION: a
// RESULT: 2: I
@@ -0,0 +1,4 @@
a += 1
a
// RESULT: 3: I
@@ -1,7 +1,8 @@
// PARAM_TYPES: kotlin.Int, Comparable<Int>
// PARAM_TYPES: kotlin.Int, Number, Comparable<Int>, Any
// PARAM_TYPES: kotlin.Int
// PARAM_TYPES: kotlin.Int
// PARAM_DESCRIPTOR: value-parameter val a: kotlin.Int defined in foo
// PARAM_DESCRIPTOR: val b: kotlin.Int defined in foo
// WITH_RUNTIME
// SIBLING:
fun foo(a: Int) {
val b: Int = 1
@@ -1,7 +1,8 @@
// PARAM_TYPES: kotlin.Int, Comparable<Int>
// PARAM_TYPES: kotlin.Int, Number, Comparable<Int>, Any
// PARAM_TYPES: kotlin.Int
// PARAM_TYPES: kotlin.Int
// PARAM_DESCRIPTOR: value-parameter val a: kotlin.Int defined in foo
// PARAM_DESCRIPTOR: val b: kotlin.Int defined in foo
// WITH_RUNTIME
// SIBLING:
fun foo(a: Int) {
val b: Int = 1
@@ -1,7 +1,8 @@
// PARAM_TYPES: kotlin.Int, Comparable<Int>
// PARAM_TYPES: kotlin.Int, Number, Comparable<Int>, Any
// PARAM_TYPES: kotlin.Int
// PARAM_TYPES: kotlin.Int
// PARAM_DESCRIPTOR: value-parameter val a: kotlin.Int defined in foo
// PARAM_DESCRIPTOR: val b: kotlin.Int defined in foo
// WITH_RUNTIME
// SIBLING:
fun foo(a: Int) {
val b: Int = 1
@@ -1,7 +1,8 @@
// PARAM_TYPES: kotlin.Int, Comparable<Int>
// PARAM_TYPES: kotlin.Int, Number, Comparable<Int>, Any
// PARAM_TYPES: kotlin.Int
// PARAM_TYPES: kotlin.Int
// PARAM_DESCRIPTOR: value-parameter val a: kotlin.Int defined in foo
// PARAM_DESCRIPTOR: val b: kotlin.Int defined in foo
// WITH_RUNTIME
// SIBLING:
fun foo(a: Int) {
val b: Int = 1
@@ -0,0 +1,19 @@
// WITH_RUNTIME
// PARAM_TYPES: A?, kotlin.Any?
// PARAM_TYPES: B, A
// PARAM_TYPES: kotlin.Int
// PARAM_DESCRIPTOR: var a: A? defined in foo
// PARAM_DESCRIPTOR: value-parameter val b: B defined in foo
// PARAM_DESCRIPTOR: var c: kotlin.Int defined in foo
// SIBLING:
fun foo<A: Any, B: A>(b: B): Int {
var a: A? = null
var c: Int = 1
<selection>a = b
c += 2
println(a)
println(c)</selection>
return a.hashCode() ?: 0 + c
}
@@ -0,0 +1,28 @@
// WITH_RUNTIME
// PARAM_TYPES: A?, kotlin.Any?
// PARAM_TYPES: B, A
// PARAM_TYPES: kotlin.Int
// PARAM_DESCRIPTOR: var a: A? defined in foo
// PARAM_DESCRIPTOR: value-parameter val b: B defined in foo
// PARAM_DESCRIPTOR: var c: kotlin.Int defined in foo
// SIBLING:
fun foo<A: Any, B: A>(b: B): Int {
var a: A? = null
var c: Int = 1
val pair = pair(a, b, c)
a = pair.first
c = pair.second
return a.hashCode() ?: 0 + c
}
private fun <A : Any, B : A> pair(a: A?, b: B, c: Int): Pair<A?, Int> {
var a1 = a
var c1 = c
a1 = b
c1 += 2
println(a1)
println(c1)
return Pair(a1, c1)
}
@@ -0,0 +1,11 @@
// WITH_RUNTIME
// PARAM_TYPES: kotlin.Int
// PARAM_DESCRIPTOR: var k: kotlin.Int defined in foo
// SIBLING:
fun foo() {
var k = 0
<selection>val a = 1
k++
val b = 2</selection>
println(a + b - k)
}
@@ -0,0 +1,20 @@
// WITH_RUNTIME
// PARAM_TYPES: kotlin.Int
// PARAM_DESCRIPTOR: var k: kotlin.Int defined in foo
// SIBLING:
fun foo() {
var k = 0
val triple = triple(k)
val a = triple.first
val b = triple.second
k = triple.third
println(a + b - k)
}
private fun triple(k: Int): Triple<Int, Int, Int> {
var k1 = k
val a = 1
k1++
val b = 2
return Triple(a, b, k1)
}
@@ -1,12 +0,0 @@
// SIBLING:
fun foo(a: Int): Int {
var b: Int = 1
var c: Int = 1
<selection>b += a
c -= a
println(b)
println(c)</selection>
return b
}
@@ -1 +0,0 @@
Selected code fragment has multiple output values: var b: Int var c: Int
@@ -1,16 +0,0 @@
// SIBLING:
fun foo(a: Int): Int {
var b: Int = 1
var c: Int = 1
<selection>if (a > 0) {
b += a
}
else {
c -= a
}
println(b)
println(c)</selection>
return b
}
@@ -1 +0,0 @@
Selected code fragment has multiple output values: var b: Int var c: Int
@@ -1,18 +0,0 @@
// SIBLING:
fun foo(a: Int): Int {
var b: Int = 1
var c: Int = 1
<selection>when {
a > 0 -> {
b += a
}
else -> {
c -= a
}
}
println(b)
println(c)</selection>
return b
}
@@ -1 +0,0 @@
Selected code fragment has multiple output values: var b: Int var c: Int
@@ -0,0 +1,18 @@
// WITH_RUNTIME
// PARAM_TYPES: kotlin.Int
// PARAM_TYPES: kotlin.Int
// PARAM_DESCRIPTOR: value-parameter val a: kotlin.Int defined in foo
// PARAM_DESCRIPTOR: var b: kotlin.Int defined in foo
// SIBLING:
fun foo(a: Int): Int {
var b: Int = 1
var c: Int = 1
val t = 1 + <selection>if (a > 0) {
b += a
a + 1
}
else a</selection>
return b + c
}
@@ -0,0 +1,24 @@
// WITH_RUNTIME
// PARAM_TYPES: kotlin.Int
// PARAM_TYPES: kotlin.Int
// PARAM_DESCRIPTOR: value-parameter val a: kotlin.Int defined in foo
// PARAM_DESCRIPTOR: var b: kotlin.Int defined in foo
// SIBLING:
fun foo(a: Int): Int {
var b: Int = 1
var c: Int = 1
val pair = pair(a, b)
b = pair.second
val t = 1 + pair.first
return b + c
}
private fun pair(a: Int, b: Int): Pair<Int, Int> {
var b1 = b
return Pair(if (a > 0) {
b1 += a
a + 1
} else a, b1)
}
@@ -0,0 +1,20 @@
// WITH_RUNTIME
// PARAM_TYPES: kotlin.Int
// PARAM_TYPES: kotlin.Int
// PARAM_DESCRIPTOR: value-parameter val a: kotlin.Int defined in foo
// PARAM_DESCRIPTOR: var b: kotlin.Int defined in foo
// SIBLING:
fun foo(a: Int): Int {
var b: Int = 1
val t = <selection>if (a > 0) {
b += a
b
}
else {
a
}</selection>
println(b)
return t
}
@@ -0,0 +1,26 @@
// WITH_RUNTIME
// PARAM_TYPES: kotlin.Int
// PARAM_TYPES: kotlin.Int
// PARAM_DESCRIPTOR: value-parameter val a: kotlin.Int defined in foo
// PARAM_DESCRIPTOR: var b: kotlin.Int defined in foo
// SIBLING:
fun foo(a: Int): Int {
var b: Int = 1
val pair = pair(a, b)
b = pair.second
val t = pair.first
println(b)
return t
}
private fun pair(a: Int, b: Int): Pair<Int, Int> {
var b1 = b
return Pair(if (a > 0) {
b1 += a
b1
} else {
a
}, b1)
}
@@ -0,0 +1,13 @@
// WITH_RUNTIME
// PARAM_TYPES: kotlin.Int
// PARAM_TYPES: kotlin.Int
// PARAM_DESCRIPTOR: value-parameter val a: kotlin.Int defined in foo
// PARAM_DESCRIPTOR: var b: kotlin.Int defined in foo
// SIBLING:
fun foo(a: Int): Int {
var b: Int = 1
<selection>b += a
println(b)
return b</selection>
}
@@ -0,0 +1,18 @@
// WITH_RUNTIME
// PARAM_TYPES: kotlin.Int
// PARAM_TYPES: kotlin.Int
// PARAM_DESCRIPTOR: value-parameter val a: kotlin.Int defined in foo
// PARAM_DESCRIPTOR: var b: kotlin.Int defined in foo
// SIBLING:
fun foo(a: Int): Int {
var b: Int = 1
return i(a, b)
}
private fun i(a: Int, b: Int): Int {
var b1 = b
b1 += a
println(b1)
return b1
}
@@ -0,0 +1,24 @@
// WITH_RUNTIME
// PARAM_TYPES: kotlin.Int
// PARAM_TYPES: kotlin.Int
// PARAM_TYPES: kotlin.Int
// PARAM_DESCRIPTOR: value-parameter val a: kotlin.Int defined in foo
// PARAM_DESCRIPTOR: var b: kotlin.Int defined in foo
// PARAM_DESCRIPTOR: var c: kotlin.Int defined in foo
// SIBLING:
fun foo(a: Int): Int {
var b: Int = 1
var c: Int = 2
val t = <selection>if (a > 0) {
b += a
c -= b
b
}
else {
a
}</selection>
println(b + c)
return t
}
@@ -0,0 +1,32 @@
// WITH_RUNTIME
// PARAM_TYPES: kotlin.Int
// PARAM_TYPES: kotlin.Int
// PARAM_TYPES: kotlin.Int
// PARAM_DESCRIPTOR: value-parameter val a: kotlin.Int defined in foo
// PARAM_DESCRIPTOR: var b: kotlin.Int defined in foo
// PARAM_DESCRIPTOR: var c: kotlin.Int defined in foo
// SIBLING:
fun foo(a: Int): Int {
var b: Int = 1
var c: Int = 2
val triple = triple(a, b, c)
b = triple.second
c = triple.third
val t = triple.first
println(b + c)
return t
}
private fun triple(a: Int, b: Int, c: Int): Triple<Int, Int, Int> {
var b1 = b
var c1 = c
return Triple(if (a > 0) {
b1 += a
c1 -= b1
b1
} else {
a
}, b1, c1)
}
@@ -0,0 +1,19 @@
// WITH_RUNTIME
// PARAM_TYPES: kotlin.Int
// PARAM_TYPES: kotlin.Int
// PARAM_TYPES: kotlin.Int
// PARAM_DESCRIPTOR: value-parameter val a: kotlin.Int defined in foo
// PARAM_DESCRIPTOR: var b: kotlin.Int defined in foo
// PARAM_DESCRIPTOR: var c: kotlin.Int defined in foo
// SIBLING:
fun foo(a: Int): Int {
var b: Int = 1
var c: Int = 1
<selection>b += a
c -= a
println(b)
println(c)</selection>
return b + c
}
@@ -0,0 +1,28 @@
// WITH_RUNTIME
// PARAM_TYPES: kotlin.Int
// PARAM_TYPES: kotlin.Int
// PARAM_TYPES: kotlin.Int
// PARAM_DESCRIPTOR: value-parameter val a: kotlin.Int defined in foo
// PARAM_DESCRIPTOR: var b: kotlin.Int defined in foo
// PARAM_DESCRIPTOR: var c: kotlin.Int defined in foo
// SIBLING:
fun foo(a: Int): Int {
var b: Int = 1
var c: Int = 1
val pair = pair(a, b, c)
b = pair.first
c = pair.second
return b + c
}
private fun pair(a: Int, b: Int, c: Int): Pair<Int, Int> {
var b1 = b
var c1 = c
b1 += a
c1 -= a
println(b1)
println(c1)
return Pair(b1, c1)
}
@@ -0,0 +1,14 @@
// WITH_RUNTIME
// SIBLING:
fun foo() {
val pair = pair()
val a = pair.first
val b = pair.second
println(a + b)
}
private fun pair(): Pair<Int, Int> {
val a = 1
val b = 2
return Pair(a, b)
}
@@ -0,0 +1,23 @@
// WITH_RUNTIME
// PARAM_TYPES: kotlin.Int
// PARAM_TYPES: kotlin.Int
// PARAM_TYPES: kotlin.Int
// PARAM_DESCRIPTOR: value-parameter val a: kotlin.Int defined in foo
// PARAM_DESCRIPTOR: var b: kotlin.Int defined in foo
// PARAM_DESCRIPTOR: var c: kotlin.Int defined in foo
// SIBLING:
fun foo(a: Int): Int {
var b: Int = 1
var c: Int = 1
<selection>if (a > 0) {
b += a
}
else {
c -= a
}
println(b)
println(c)</selection>
return b + c
}
@@ -0,0 +1,31 @@
// WITH_RUNTIME
// PARAM_TYPES: kotlin.Int
// PARAM_TYPES: kotlin.Int
// PARAM_TYPES: kotlin.Int
// PARAM_DESCRIPTOR: value-parameter val a: kotlin.Int defined in foo
// PARAM_DESCRIPTOR: var b: kotlin.Int defined in foo
// PARAM_DESCRIPTOR: var c: kotlin.Int defined in foo
// SIBLING:
fun foo(a: Int): Int {
var b: Int = 1
var c: Int = 1
val pair = pair(a, b, c)
b = pair.first
c = pair.second
return b + c
}
private fun pair(a: Int, b: Int, c: Int): Pair<Int, Int> {
var b1 = b
var c1 = c
if (a > 0) {
b1 += a
} else {
c1 -= a
}
println(b1)
println(c1)
return Pair(b1, c1)
}
@@ -0,0 +1,20 @@
// WITH_RUNTIME
// PARAM_TYPES: kotlin.Int
// PARAM_TYPES: kotlin.Int
// PARAM_TYPES: kotlin.Int
// PARAM_DESCRIPTOR: value-parameter val a: kotlin.Int defined in foo
// PARAM_DESCRIPTOR: var b: kotlin.Int defined in foo
// PARAM_DESCRIPTOR: var c: kotlin.Int defined in foo
// SIBLING:
fun foo(a: Int): Int {
var b: Int = 1
var c: Int = 1
val pair = 2
<selection>b += a
c -= a
println(b)
println(c)</selection>
return b + c
}
@@ -0,0 +1,29 @@
// WITH_RUNTIME
// PARAM_TYPES: kotlin.Int
// PARAM_TYPES: kotlin.Int
// PARAM_TYPES: kotlin.Int
// PARAM_DESCRIPTOR: value-parameter val a: kotlin.Int defined in foo
// PARAM_DESCRIPTOR: var b: kotlin.Int defined in foo
// PARAM_DESCRIPTOR: var c: kotlin.Int defined in foo
// SIBLING:
fun foo(a: Int): Int {
var b: Int = 1
var c: Int = 1
val pair = 2
val pair1 = pair(a, b, c)
b = pair1.first
c = pair1.second
return b + c
}
private fun pair(a: Int, b: Int, c: Int): Pair<Int, Int> {
var b1 = b
var c1 = c
b1 += a
c1 -= a
println(b1)
println(c1)
return Pair(b1, c1)
}
@@ -0,0 +1,25 @@
// WITH_RUNTIME
// PARAM_TYPES: kotlin.Int
// PARAM_TYPES: kotlin.Int
// PARAM_TYPES: kotlin.Int
// PARAM_DESCRIPTOR: value-parameter val a: kotlin.Int defined in foo
// PARAM_DESCRIPTOR: var b: kotlin.Int defined in foo
// PARAM_DESCRIPTOR: var c: kotlin.Int defined in foo
// SIBLING:
fun foo(a: Int): Int {
var b: Int = 1
var c: Int = 1
<selection>when {
a > 0 -> {
b += a
}
else -> {
c -= a
}
}
println(b)
println(c)</selection>
return b + c
}
@@ -0,0 +1,34 @@
// WITH_RUNTIME
// PARAM_TYPES: kotlin.Int
// PARAM_TYPES: kotlin.Int
// PARAM_TYPES: kotlin.Int
// PARAM_DESCRIPTOR: value-parameter val a: kotlin.Int defined in foo
// PARAM_DESCRIPTOR: var b: kotlin.Int defined in foo
// PARAM_DESCRIPTOR: var c: kotlin.Int defined in foo
// SIBLING:
fun foo(a: Int): Int {
var b: Int = 1
var c: Int = 1
val pair = pair(a, b, c)
b = pair.first
c = pair.second
return b + c
}
private fun pair(a: Int, b: Int, c: Int): Pair<Int, Int> {
var b1 = b
var c1 = c
when {
a > 0 -> {
b1 += a
}
else -> {
c1 -= a
}
}
println(b1)
println(c1)
return Pair(b1, c1)
}
@@ -0,0 +1,23 @@
// WITH_RUNTIME
// PARAM_TYPES: kotlin.Int
// PARAM_TYPES: kotlin.Int
// PARAM_TYPES: kotlin.Int
// PARAM_DESCRIPTOR: value-parameter val a: kotlin.Int defined in foo
// PARAM_DESCRIPTOR: var b: kotlin.Int defined in foo
// PARAM_DESCRIPTOR: var c: kotlin.Int defined in foo
// SIBLING:
fun foo(a: Int): Int {
var b: Int = 1
var c: Int = 1
var d: Int = 1
var e: Int = 1
<selection>b += a
c -= a
d += c
e -= d
println(b)
println(c)</selection>
return b + c + d + e
}
@@ -0,0 +1 @@
Selected code fragment has more than 3 output values: var b: Int var c: Int var d: Int var e: Int
@@ -0,0 +1,28 @@
// WITH_RUNTIME
// OPTIONS: true, true
// PARAM_TYPES: kotlin.Int
// PARAM_TYPES: kotlin.Int
// PARAM_TYPES: kotlin.Int
// PARAM_TYPES: kotlin.Int
// PARAM_TYPES: kotlin.Int
// PARAM_DESCRIPTOR: value-parameter val a: kotlin.Int defined in foo
// PARAM_DESCRIPTOR: var b: kotlin.Int defined in foo
// PARAM_DESCRIPTOR: var c: kotlin.Int defined in foo
// PARAM_DESCRIPTOR: var d: kotlin.Int defined in foo
// PARAM_DESCRIPTOR: var e: kotlin.Int defined in foo
// SIBLING:
fun foo(a: Int): Int {
var b: Int = 1
var c: Int = 1
var d: Int = 1
var e: Int = 1
<selection>b += a
c -= a
d += c
e -= d
println(b)
println(c)</selection>
return b + c + d + e
}
@@ -0,0 +1,41 @@
// WITH_RUNTIME
// OPTIONS: true, true
// PARAM_TYPES: kotlin.Int
// PARAM_TYPES: kotlin.Int
// PARAM_TYPES: kotlin.Int
// PARAM_TYPES: kotlin.Int
// PARAM_TYPES: kotlin.Int
// PARAM_DESCRIPTOR: value-parameter val a: kotlin.Int defined in foo
// PARAM_DESCRIPTOR: var b: kotlin.Int defined in foo
// PARAM_DESCRIPTOR: var c: kotlin.Int defined in foo
// PARAM_DESCRIPTOR: var d: kotlin.Int defined in foo
// PARAM_DESCRIPTOR: var e: kotlin.Int defined in foo
// SIBLING:
fun foo(a: Int): Int {
var b: Int = 1
var c: Int = 1
var d: Int = 1
var e: Int = 1
val list = list(a, b, c, d, e)
b = list[0]
c = list[1]
d = list[2]
e = list[3]
return b + c + d + e
}
private fun list(a: Int, b: Int, c: Int, d: Int, e: Int): List<Int> {
var b1 = b
var c1 = c
var d1 = d
var e1 = e
b1 += a
c1 -= a
d1 += c1
e1 -= d1
println(b1)
println(c1)
return listOf(b1, c1, d1, e1)
}
@@ -0,0 +1,23 @@
// WITH_RUNTIME
// PARAM_TYPES: kotlin.Int
// PARAM_TYPES: kotlin.Int
// PARAM_TYPES: kotlin.Int
// PARAM_TYPES: kotlin.Int
// PARAM_DESCRIPTOR: value-parameter val a: kotlin.Int defined in foo
// PARAM_DESCRIPTOR: var b: kotlin.Int defined in foo
// PARAM_DESCRIPTOR: var c: kotlin.Int defined in foo
// PARAM_DESCRIPTOR: var d: kotlin.Int defined in foo
// SIBLING:
fun foo(a: Int): Int {
var b: Int = 1
var c: Int = 1
var d: Int = 1
<selection>b += a
c -= a
d += c
println(b)
println(c)</selection>
return b + c + d
}
@@ -0,0 +1,34 @@
// WITH_RUNTIME
// PARAM_TYPES: kotlin.Int
// PARAM_TYPES: kotlin.Int
// PARAM_TYPES: kotlin.Int
// PARAM_TYPES: kotlin.Int
// PARAM_DESCRIPTOR: value-parameter val a: kotlin.Int defined in foo
// PARAM_DESCRIPTOR: var b: kotlin.Int defined in foo
// PARAM_DESCRIPTOR: var c: kotlin.Int defined in foo
// PARAM_DESCRIPTOR: var d: kotlin.Int defined in foo
// SIBLING:
fun foo(a: Int): Int {
var b: Int = 1
var c: Int = 1
var d: Int = 1
val triple = triple(a, b, c, d)
b = triple.first
c = triple.second
d = triple.third
return b + c + d
}
private fun triple(a: Int, b: Int, c: Int, d: Int): Triple<Int, Int, Int> {
var b1 = b
var c1 = c
var d1 = d
b1 += a
c1 -= a
d1 += c1
println(b1)
println(c1)
return Triple(b1, c1, d1)
}
@@ -0,0 +1,19 @@
// WITH_RUNTIME
// PARAM_TYPES: kotlin.Int
// PARAM_TYPES: kotlin.Int
// PARAM_TYPES: kotlin.Int
// PARAM_DESCRIPTOR: value-parameter val a: kotlin.Int defined in foo
// PARAM_DESCRIPTOR: var b: kotlin.Int defined in foo
// PARAM_DESCRIPTOR: var c: kotlin.Int defined in foo
// SIBLING:
fun foo(a: Int): Int {
var b: Int = 1
var c: Int = 1
<selection>b += a
c -= a
println(b)
println(c)</selection>
return b
}
@@ -0,0 +1,26 @@
// WITH_RUNTIME
// PARAM_TYPES: kotlin.Int
// PARAM_TYPES: kotlin.Int
// PARAM_TYPES: kotlin.Int
// PARAM_DESCRIPTOR: value-parameter val a: kotlin.Int defined in foo
// PARAM_DESCRIPTOR: var b: kotlin.Int defined in foo
// PARAM_DESCRIPTOR: var c: kotlin.Int defined in foo
// SIBLING:
fun foo(a: Int): Int {
var b: Int = 1
var c: Int = 1
b = i(a, b, c)
return b
}
private fun i(a: Int, b: Int, c: Int): Int {
var b1 = b
var c1 = c
b1 += a
c1 -= a
println(b1)
println(c1)
return b1
}
@@ -1 +0,0 @@
Selected code fragment has multiple output values: val a: Int val b: Int
@@ -1,15 +0,0 @@
// SIBLING:
fun foo(a: Int): Int {
var b: Int = 1
val t = <selection>if (a > 0) {
b += a
b
}
else {
a
}</selection>
println(b)
return t
}
@@ -1 +0,0 @@
Selected code fragment has output values as well as alternative exit points
@@ -1,8 +0,0 @@
// SIBLING:
fun foo(a: Int): Int {
var b: Int = 1
<selection>b += a
println(b)
return b</selection>
}
@@ -1 +0,0 @@
Selected code fragment has output values as well as alternative exit points
@@ -1,6 +1,4 @@
data class Pair<A, B>(val a: A, val b: B)
fun <A, B> A.to(b: B) = Pair(this, b)
// WITH_RUNTIME
// SIBLING:
fun foo() {
val (a, b) = <selection>1 to 2</selection>
@@ -1,6 +1,4 @@
data class Pair<A, B>(val a: A, val b: B)
fun <A, B> A.to(b: B) = Pair(this, b)
// WITH_RUNTIME
// SIBLING:
fun foo() {
val (a, b) = pair()
@@ -1,6 +1,4 @@
data class Pair<A, B>(val a: A, val b: B)
fun <A, B> A.to(b: B) = Pair(this, b)
// WITH_RUNTIME
// SIBLING:
fun foo() {
val (a, b) =
@@ -1,6 +1,4 @@
data class Pair<A, B>(val a: A, val b: B)
fun <A, B> A.to(b: B) = Pair(this, b)
// WITH_RUNTIME
// SIBLING:
fun foo() {
val (a, b) =
@@ -1,3 +1,4 @@
// WITH_RUNTIME
fun foo(c : Collection<String>){
c.filter<selection>{it; false}</selection>
}
@@ -1,4 +1,5 @@
// WITH_RUNTIME
fun foo(c : Collection<String>){
val function = { it; false }
val function: (String) -> Boolean = { it; false }
c.filter(function)
}
@@ -1,3 +1,4 @@
// WITH_RUNTIME
fun main(args: Array<String>) {
with(A()) {
println(<selection>prop</selection>)
@@ -7,6 +8,4 @@ fun main(args: Array<String>) {
class A {
val prop = 1
}
public inline fun <T, R> with(receiver: T, f: T.() -> R): R = receiver.f()
}
@@ -1,3 +1,4 @@
// WITH_RUNTIME
fun main(args: Array<String>) {
with(A()) {
val i = prop
@@ -8,6 +9,4 @@ fun main(args: Array<String>) {
class A {
val prop = 1
}
public inline fun <T, R> with(receiver: T, f: T.() -> R): R = receiver.f()
}
@@ -41,6 +41,11 @@ import com.intellij.testFramework.fixtures.LightCodeInsightFixtureTestCase
import org.jetbrains.jet.plugin.refactoring.extractFunction.ExtractKotlinFunctionHandlerHelper
import org.jetbrains.jet.plugin.refactoring.extractFunction.ExtractionGeneratorOptions
import org.jetbrains.jet.plugin.refactoring.extractFunction.ExtractableCodeDescriptor
import com.intellij.testFramework.LightPlatformTestCase
import org.jetbrains.jet.testing.ConfigLibraryUtil
import org.jetbrains.jet.plugin.PluginTestCaseBase
import org.jetbrains.jet.plugin.refactoring.extractFunction.ExtractionData
import org.jetbrains.jet.plugin.refactoring.extractFunction.ExtractionOptions
public abstract class AbstractJetExtractionTest() : JetLightCodeInsightFixtureTestCase() {
override fun getProjectDescriptor() = LightCodeInsightFixtureTestCase.JAVA_LATEST
@@ -76,19 +81,31 @@ public abstract class AbstractJetExtractionTest() : JetLightCodeInsightFixtureTe
}
)
val fileText = file.getText()
val fileText = file.getText() ?: ""
val expectedDescriptors =
InTextDirectivesUtils.findLinesWithPrefixesRemoved(fileText, "// PARAM_DESCRIPTOR: ").joinToString()
val expectedTypes =
InTextDirectivesUtils.findLinesWithPrefixesRemoved(fileText, "// PARAM_TYPES: ").map { "[$it]" }.joinToString()
val extractAsProperty = InTextDirectivesUtils.isDirectiveDefined(fileText, "// EXTRACT_AS_PROPERTY")
val extractionOptions = InTextDirectivesUtils.findListWithPrefixes(fileText, "// OPTIONS: ").let {
if (it.isNotEmpty()) {
[suppress("CAST_NEVER_SUCCEEDS")]
val args = it.map { it.toBoolean() }.copyToArray() as Array<Any?>
javaClass<ExtractionOptions>().getConstructors()[0].newInstance(*args) as ExtractionOptions
} else ExtractionOptions.DEFAULT
}
val renderer = DescriptorRenderer.DEBUG_TEXT
val editor = fixture.getEditor()
selectElements(editor, file) {(elements, previousSibling) ->
ExtractKotlinFunctionHandler(
helper = object : ExtractKotlinFunctionHandlerHelper() {
override fun adjustExtractionData(data: ExtractionData): ExtractionData {
return data.copy(options = extractionOptions)
}
override fun adjustGeneratorOptions(options: ExtractionGeneratorOptions): ExtractionGeneratorOptions {
return options.copy(extractAsProperty = extractAsProperty)
}
@@ -120,6 +137,10 @@ public abstract class AbstractJetExtractionTest() : JetLightCodeInsightFixtureTe
val file = fixture.configureByFile(mainFile.getName()) as JetFile
if (InTextDirectivesUtils.findStringWithPrefixes(file.getText(), "// WITH_RUNTIME") != null) {
ConfigLibraryUtil.configureKotlinRuntime(myModule, PluginTestCaseBase.fullJdk())
}
try {
action(file)
@@ -715,21 +715,69 @@ public class JetExtractionTestGenerated extends AbstractJetExtractionTest {
JetTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("idea/testData/refactoring/extractFunction/controlFlow/outputValues"), Pattern.compile("^(.+)\\.kt$"), true);
}
@TestMetadata("multipleOutputValues.kt")
public void testMultipleOutputValues() throws Exception {
String fileName = JetTestUtils.navigationMetadata("idea/testData/refactoring/extractFunction/controlFlow/outputValues/multipleOutputValues.kt");
@TestMetadata("genericPair.kt")
public void testGenericPair() throws Exception {
String fileName = JetTestUtils.navigationMetadata("idea/testData/refactoring/extractFunction/controlFlow/outputValues/genericPair.kt");
doExtractFunctionTest(fileName);
}
@TestMetadata("multipleOutputValuesWithIf.kt")
public void testMultipleOutputValuesWithIf() throws Exception {
String fileName = JetTestUtils.navigationMetadata("idea/testData/refactoring/extractFunction/controlFlow/outputValues/multipleOutputValuesWithIf.kt");
@TestMetadata("initializersAndUpdate.kt")
public void testInitializersAndUpdate() throws Exception {
String fileName = JetTestUtils.navigationMetadata("idea/testData/refactoring/extractFunction/controlFlow/outputValues/initializersAndUpdate.kt");
doExtractFunctionTest(fileName);
}
@TestMetadata("multipleOutputValuesWithWhen.kt")
public void testMultipleOutputValuesWithWhen() throws Exception {
String fileName = JetTestUtils.navigationMetadata("idea/testData/refactoring/extractFunction/controlFlow/outputValues/multipleOutputValuesWithWhen.kt");
@TestMetadata("nestedNonInlinableCall.kt")
public void testNestedNonInlinableCall() throws Exception {
String fileName = JetTestUtils.navigationMetadata("idea/testData/refactoring/extractFunction/controlFlow/outputValues/nestedNonInlinableCall.kt");
doExtractFunctionTest(fileName);
}
@TestMetadata("outputValueWithExpression.kt")
public void testOutputValueWithExpression() throws Exception {
String fileName = JetTestUtils.navigationMetadata("idea/testData/refactoring/extractFunction/controlFlow/outputValues/outputValueWithExpression.kt");
doExtractFunctionTest(fileName);
}
@TestMetadata("outputValueWithReturn.kt")
public void testOutputValueWithReturn() throws Exception {
String fileName = JetTestUtils.navigationMetadata("idea/testData/refactoring/extractFunction/controlFlow/outputValues/outputValueWithReturn.kt");
doExtractFunctionTest(fileName);
}
@TestMetadata("outputValuesWithExpression.kt")
public void testOutputValuesWithExpression() throws Exception {
String fileName = JetTestUtils.navigationMetadata("idea/testData/refactoring/extractFunction/controlFlow/outputValues/outputValuesWithExpression.kt");
doExtractFunctionTest(fileName);
}
@TestMetadata("pair.kt")
public void testPair() throws Exception {
String fileName = JetTestUtils.navigationMetadata("idea/testData/refactoring/extractFunction/controlFlow/outputValues/pair.kt");
doExtractFunctionTest(fileName);
}
@TestMetadata("pairOfInitalizersWithNonLocalUsages.kt")
public void testPairOfInitalizersWithNonLocalUsages() throws Exception {
String fileName = JetTestUtils.navigationMetadata("idea/testData/refactoring/extractFunction/controlFlow/outputValues/pairOfInitalizersWithNonLocalUsages.kt");
doExtractFunctionTest(fileName);
}
@TestMetadata("pairWithIf.kt")
public void testPairWithIf() throws Exception {
String fileName = JetTestUtils.navigationMetadata("idea/testData/refactoring/extractFunction/controlFlow/outputValues/pairWithIf.kt");
doExtractFunctionTest(fileName);
}
@TestMetadata("pairWithNameClash.kt")
public void testPairWithNameClash() throws Exception {
String fileName = JetTestUtils.navigationMetadata("idea/testData/refactoring/extractFunction/controlFlow/outputValues/pairWithNameClash.kt");
doExtractFunctionTest(fileName);
}
@TestMetadata("pairWithWhen.kt")
public void testPairWithWhen() throws Exception {
String fileName = JetTestUtils.navigationMetadata("idea/testData/refactoring/extractFunction/controlFlow/outputValues/pairWithWhen.kt");
doExtractFunctionTest(fileName);
}
@@ -763,6 +811,30 @@ public class JetExtractionTestGenerated extends AbstractJetExtractionTest {
doExtractFunctionTest(fileName);
}
@TestMetadata("tooManyOutputValues.kt")
public void testTooManyOutputValues() throws Exception {
String fileName = JetTestUtils.navigationMetadata("idea/testData/refactoring/extractFunction/controlFlow/outputValues/tooManyOutputValues.kt");
doExtractFunctionTest(fileName);
}
@TestMetadata("tooManyOutputValuesAsList.kt")
public void testTooManyOutputValuesAsList() throws Exception {
String fileName = JetTestUtils.navigationMetadata("idea/testData/refactoring/extractFunction/controlFlow/outputValues/tooManyOutputValuesAsList.kt");
doExtractFunctionTest(fileName);
}
@TestMetadata("triple.kt")
public void testTriple() throws Exception {
String fileName = JetTestUtils.navigationMetadata("idea/testData/refactoring/extractFunction/controlFlow/outputValues/triple.kt");
doExtractFunctionTest(fileName);
}
@TestMetadata("usedAndUnusedOutputValues.kt")
public void testUsedAndUnusedOutputValues() throws Exception {
String fileName = JetTestUtils.navigationMetadata("idea/testData/refactoring/extractFunction/controlFlow/outputValues/usedAndUnusedOutputValues.kt");
doExtractFunctionTest(fileName);
}
@TestMetadata("valuesUsedInLambdaOnly.kt")
public void testValuesUsedInLambdaOnly() throws Exception {
String fileName = JetTestUtils.navigationMetadata("idea/testData/refactoring/extractFunction/controlFlow/outputValues/valuesUsedInLambdaOnly.kt");
@@ -849,12 +921,6 @@ public class JetExtractionTestGenerated extends AbstractJetExtractionTest {
doExtractFunctionTest(fileName);
}
@TestMetadata("multipleInitalizersWithNonLocalUsages.kt")
public void testMultipleInitalizersWithNonLocalUsages() throws Exception {
String fileName = JetTestUtils.navigationMetadata("idea/testData/refactoring/extractFunction/controlFlow/unextractable/multipleInitalizersWithNonLocalUsages.kt");
doExtractFunctionTest(fileName);
}
@TestMetadata("multipleJumps.kt")
public void testMultipleJumps() throws Exception {
String fileName = JetTestUtils.navigationMetadata("idea/testData/refactoring/extractFunction/controlFlow/unextractable/multipleJumps.kt");
@@ -867,18 +933,6 @@ public class JetExtractionTestGenerated extends AbstractJetExtractionTest {
doExtractFunctionTest(fileName);
}
@TestMetadata("outputValueWithExpression.kt")
public void testOutputValueWithExpression() throws Exception {
String fileName = JetTestUtils.navigationMetadata("idea/testData/refactoring/extractFunction/controlFlow/unextractable/outputValueWithExpression.kt");
doExtractFunctionTest(fileName);
}
@TestMetadata("outputValueWithReturn.kt")
public void testOutputValueWithReturn() throws Exception {
String fileName = JetTestUtils.navigationMetadata("idea/testData/refactoring/extractFunction/controlFlow/unextractable/outputValueWithReturn.kt");
doExtractFunctionTest(fileName);
}
@TestMetadata("variablesOutOfScope.kt")
public void testVariablesOutOfScope() throws Exception {
String fileName = JetTestUtils.navigationMetadata("idea/testData/refactoring/extractFunction/controlFlow/unextractable/variablesOutOfScope.kt");