Extract Function: Implement duplicate search
This commit is contained in:
@@ -0,0 +1,10 @@
|
||||
<root>
|
||||
<item
|
||||
name='com.intellij.codeInsight.folding.CodeFoldingManager com.intellij.codeInsight.folding.CodeFoldingManager getInstance(com.intellij.openapi.project.Project)'>
|
||||
<annotation name='org.jetbrains.annotations.NotNull'/>
|
||||
</item>
|
||||
<item
|
||||
name='com.intellij.codeInsight.folding.CodeFoldingManager com.intellij.openapi.editor.FoldRegion[] getFoldRegionsAtOffset(com.intellij.openapi.editor.Editor, int)'>
|
||||
<annotation name='org.jetbrains.annotations.NotNull'/>
|
||||
</item>
|
||||
</root>
|
||||
@@ -0,0 +1,10 @@
|
||||
<root>
|
||||
<item
|
||||
name='com.intellij.codeInsight.folding.impl.CodeFoldingManagerImpl com.intellij.openapi.editor.FoldRegion[] getFoldRegionsAtOffset(com.intellij.openapi.editor.Editor, int)'>
|
||||
<annotation name='org.jetbrains.annotations.NotNull'/>
|
||||
</item>
|
||||
<item
|
||||
name='com.intellij.codeInsight.folding.impl.FoldingUtil com.intellij.openapi.editor.FoldRegion[] getFoldRegionsAtOffset(com.intellij.openapi.editor.Editor, int)'>
|
||||
<annotation name='org.jetbrains.annotations.NotNull'/>
|
||||
</item>
|
||||
</root>
|
||||
@@ -0,0 +1,6 @@
|
||||
<root>
|
||||
<item
|
||||
name='com.intellij.codeInsight.highlighting.HighlightManager com.intellij.codeInsight.highlighting.HighlightManager getInstance(com.intellij.openapi.project.Project)'>
|
||||
<annotation name='org.jetbrains.annotations.NotNull'/>
|
||||
</item>
|
||||
</root>
|
||||
-2
@@ -49,7 +49,5 @@ public class ReturnValueInstruction(
|
||||
return ReturnValueInstruction((element as JetExpression), lexicalScope, newLabel, returnedValue)
|
||||
}
|
||||
|
||||
public val resultExpression: JetExpression =
|
||||
element.let{ if (it is JetReturnExpression) it.getReturnedExpression()!! else element as JetExpression }
|
||||
public val returnExpressionIfAny: JetReturnExpression? = element as? JetReturnExpression
|
||||
}
|
||||
|
||||
+2
-2
@@ -24,7 +24,6 @@ import org.jetbrains.jet.plugin.refactoring.createTempCopy
|
||||
import org.jetbrains.jet.lang.psi.codeFragmentUtil.skipVisibilityCheck
|
||||
import com.intellij.psi.PsiElement
|
||||
import org.jetbrains.jet.plugin.refactoring.extractFunction.ExtractionData
|
||||
import java.util.Collections
|
||||
import org.jetbrains.jet.plugin.refactoring.extractFunction.performAnalysis
|
||||
import org.jetbrains.jet.plugin.refactoring.extractFunction.AnalysisResult.Status
|
||||
import com.intellij.debugger.engine.evaluation.EvaluateExceptionUtil
|
||||
@@ -37,6 +36,7 @@ import org.jetbrains.jet.lang.psi.*
|
||||
import org.jetbrains.jet.plugin.intentions.InsertExplicitTypeArguments
|
||||
import org.jetbrains.jet.plugin.refactoring.extractFunction.ExtractionGeneratorOptions
|
||||
import org.jetbrains.jet.plugin.refactoring.extractFunction.generateDeclaration
|
||||
import org.jetbrains.jet.plugin.util.psi.patternMatching.toRange
|
||||
import org.jetbrains.jet.plugin.actions.internal.KotlinInternalMode
|
||||
import org.jetbrains.jet.lang.psi.psiUtil.replaced
|
||||
|
||||
@@ -97,7 +97,7 @@ fun getFunctionForExtractedFragment(
|
||||
if (targetSibling == null) return null
|
||||
|
||||
val analysisResult = ExtractionData(
|
||||
tmpFile, Collections.singletonList(newDebugExpression), targetSibling, ExtractionOptions(false, true)
|
||||
tmpFile, newDebugExpression.toRange(), targetSibling, ExtractionOptions(false, true)
|
||||
).performAnalysis()
|
||||
if (analysisResult.status != Status.SUCCESS) {
|
||||
throw EvaluateExceptionUtil.createEvaluateException(getErrorMessageForExtractFunctionResult(analysisResult))
|
||||
|
||||
@@ -20,6 +20,8 @@ cannot.refactor.expression.should.have.inferred.type=Expression should have infe
|
||||
cannot.refactor.synthesized.function=Cannot refactor synthesized function ''{0}''
|
||||
error.types.in.generated.function=Cannot generate function with erroneous return type
|
||||
|
||||
0.has.detected.1.code.fragments.in.this.file.that.can.be.replaced.with.a.call.to.extracted.declaration={0} has detected {1} code {1,choice,1#fragment|2#fragments} in this file that can be replaced with a call to extracted declaration. Would you like to review and replace {1,choice,1#it|2#them}?
|
||||
|
||||
error.wrong.caret.position.function.or.constructor.name=The caret should be positioned at the name of the function or constructor to be refactored.
|
||||
error.cant.refactor.vararg.functions=Can't refactor the function with variable arguments
|
||||
function.name.is.invalid=Function name is invalid
|
||||
|
||||
+15
-2
@@ -66,6 +66,7 @@ import org.jetbrains.jet.lang.descriptors.FunctionDescriptor
|
||||
import org.jetbrains.jet.renderer.DescriptorRenderer
|
||||
import org.jetbrains.jet.lang.psi.JetPropertyAccessor
|
||||
import org.jetbrains.jet.lang.psi.JetClassOrObject
|
||||
import org.jetbrains.jet.plugin.util.psi.patternMatching.toRange
|
||||
import org.jetbrains.jet.lang.psi.JetMultiDeclaration
|
||||
|
||||
public open class ExtractKotlinFunctionHandlerHelper {
|
||||
@@ -81,6 +82,15 @@ public open class ExtractKotlinFunctionHandlerHelper {
|
||||
public class ExtractKotlinFunctionHandler(
|
||||
public val allContainersEnabled: Boolean = false,
|
||||
private val helper: ExtractKotlinFunctionHandlerHelper = ExtractKotlinFunctionHandlerHelper.DEFAULT) : RefactoringActionHandler {
|
||||
private fun adjustElements(elements: List<PsiElement>): List<PsiElement> {
|
||||
if (elements.size != 1) return elements
|
||||
|
||||
val e = elements.first()
|
||||
if (e is JetBlockExpression && e.getParent() is JetFunctionLiteral) return e.getStatements()
|
||||
|
||||
return elements
|
||||
}
|
||||
|
||||
fun doInvoke(
|
||||
editor: Editor,
|
||||
file: JetFile,
|
||||
@@ -89,7 +99,9 @@ public class ExtractKotlinFunctionHandler(
|
||||
) {
|
||||
val project = file.getProject()
|
||||
|
||||
val analysisResult = helper.adjustExtractionData(ExtractionData(file, elements, targetSibling)).performAnalysis()
|
||||
val analysisResult = helper.adjustExtractionData(
|
||||
ExtractionData(file, adjustElements(elements).toRange(false), targetSibling)
|
||||
).performAnalysis()
|
||||
|
||||
if (ApplicationManager.getApplication()!!.isUnitTestMode() && analysisResult.status != Status.SUCCESS) {
|
||||
throw ConflictsInTestsException(analysisResult.messages.map { it.renderMessage() })
|
||||
@@ -98,7 +110,8 @@ public class ExtractKotlinFunctionHandler(
|
||||
fun doRefactor(descriptor: ExtractableCodeDescriptor, generatorOptions: ExtractionGeneratorOptions) {
|
||||
val adjustedDescriptor = helper.adjustDescriptor(descriptor)
|
||||
val adjustedGeneratorOptions = helper.adjustGeneratorOptions(generatorOptions)
|
||||
project.executeWriteCommand(EXTRACT_FUNCTION) { adjustedDescriptor.generateDeclaration(adjustedGeneratorOptions) }
|
||||
val result = project.executeWriteCommand<ExtractionResult>(EXTRACT_FUNCTION) { adjustedDescriptor.generateDeclaration(adjustedGeneratorOptions) }
|
||||
processDuplicates(result.duplicateReplacers, project, editor)
|
||||
}
|
||||
|
||||
fun validateAndRefactor() {
|
||||
|
||||
+14
-3
@@ -64,6 +64,8 @@ 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
|
||||
import org.jetbrains.jet.plugin.util.psi.patternMatching.JetPsiRange
|
||||
import org.jetbrains.jet.lang.resolve.BindingContext
|
||||
|
||||
trait Parameter {
|
||||
val argumentText: String
|
||||
@@ -123,29 +125,37 @@ class FqNameReplacement(val fqName: FqName): Replacement {
|
||||
}
|
||||
|
||||
trait OutputValue {
|
||||
val originalExpressions: List<JetExpression>
|
||||
val valueType: JetType
|
||||
|
||||
class ExpressionValue(
|
||||
val callSiteReturn: Boolean,
|
||||
override val originalExpressions: List<JetExpression>,
|
||||
override val valueType: JetType
|
||||
): OutputValue
|
||||
|
||||
class Jump(
|
||||
val elementsToReplace: List<JetElement>,
|
||||
val elementsToReplace: List<JetExpression>,
|
||||
val elementToInsertAfterCall: JetElement,
|
||||
val conditional: Boolean
|
||||
): OutputValue {
|
||||
override val originalExpressions: List<JetExpression> get() = elementsToReplace
|
||||
override val valueType: JetType = with(KotlinBuiltIns.getInstance()) { if (conditional) getBooleanType() else getUnitType() }
|
||||
}
|
||||
|
||||
class ParameterUpdate(val parameter: Parameter): OutputValue {
|
||||
class ParameterUpdate(
|
||||
val parameter: Parameter,
|
||||
override val originalExpressions: List<JetExpression>
|
||||
): OutputValue {
|
||||
override val valueType: JetType get() = parameter.parameterType
|
||||
}
|
||||
|
||||
class Initializer(
|
||||
val initializedDeclaration: JetProperty,
|
||||
override val valueType: JetType
|
||||
): OutputValue
|
||||
): OutputValue {
|
||||
override val originalExpressions: List<JetExpression> get() = Collections.singletonList(initializedDeclaration)
|
||||
}
|
||||
}
|
||||
|
||||
abstract class OutputValueBoxer(val outputValues: List<OutputValue>) {
|
||||
@@ -315,6 +325,7 @@ data class ExtractionGeneratorOptions(
|
||||
|
||||
data class ExtractionResult(
|
||||
val declaration: JetNamedDeclaration,
|
||||
val duplicateReplacers: Map<JetPsiRange, () -> Unit>,
|
||||
val nameByOffset: Map<Int, JetElement>
|
||||
)
|
||||
|
||||
|
||||
@@ -51,6 +51,7 @@ import org.jetbrains.jet.lang.psi.JetFunctionLiteral
|
||||
import org.jetbrains.jet.lang.psi.JetClassInitializer
|
||||
import org.jetbrains.jet.lang.resolve.calls.callUtil.getResolvedCall
|
||||
import org.jetbrains.jet.lang.resolve.DescriptorToSourceUtils
|
||||
import org.jetbrains.jet.plugin.util.psi.patternMatching.JetPsiRange
|
||||
|
||||
data class ExtractionOptions(
|
||||
val inferUnitTypeForUnusedValues: Boolean,
|
||||
@@ -76,11 +77,12 @@ data class ResolvedReferenceInfo(
|
||||
|
||||
data class ExtractionData(
|
||||
val originalFile: JetFile,
|
||||
val originalElements: List<PsiElement>,
|
||||
val originalRange: JetPsiRange,
|
||||
val targetSibling: PsiElement,
|
||||
val options: ExtractionOptions = ExtractionOptions.DEFAULT
|
||||
) {
|
||||
val project: Project = originalFile.getProject()
|
||||
val originalElements: List<PsiElement> = originalRange.elements
|
||||
|
||||
val insertBefore: Boolean = targetSibling.getParentByType(javaClass<JetDeclaration>(), true)?.let {
|
||||
it is JetDeclarationWithBody || it is JetClassInitializer
|
||||
|
||||
@@ -0,0 +1,114 @@
|
||||
/*
|
||||
* Copyright 2010-2014 JetBrains s.r.o.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.jetbrains.jet.plugin.refactoring.extractFunction
|
||||
|
||||
import org.jetbrains.jet.plugin.util.psi.patternMatching.JetPsiRange
|
||||
import com.intellij.openapi.application.ApplicationManager
|
||||
import com.intellij.openapi.ui.Messages
|
||||
import com.intellij.openapi.project.Project
|
||||
import com.intellij.openapi.editor.Editor
|
||||
import java.util.ArrayList
|
||||
import com.intellij.openapi.editor.markup.RangeHighlighter
|
||||
import com.intellij.openapi.editor.ScrollType
|
||||
import org.jetbrains.jet.plugin.refactoring.JetRefactoringBundle
|
||||
import com.intellij.openapi.application.ApplicationNamesInfo
|
||||
import com.intellij.openapi.util.Ref
|
||||
import com.intellij.openapi.editor.colors.EditorColorsManager
|
||||
import com.intellij.openapi.editor.colors.EditorColors
|
||||
import com.intellij.codeInsight.highlighting.HighlightManager
|
||||
import com.intellij.codeInsight.folding.CodeFoldingManager
|
||||
import com.intellij.ui.ReplacePromptDialog
|
||||
import com.intellij.find.FindManager
|
||||
import org.jetbrains.jet.plugin.refactoring.executeWriteCommand
|
||||
import com.intellij.refactoring.util.duplicates.MethodDuplicatesHandler
|
||||
import com.intellij.refactoring.RefactoringBundle
|
||||
|
||||
public fun JetPsiRange.highlight(project: Project, editor: Editor): RangeHighlighter? {
|
||||
val textRange = getTextRange()
|
||||
val highlighters = ArrayList<RangeHighlighter>()
|
||||
val attributes = EditorColorsManager.getInstance().getGlobalScheme().getAttributes(EditorColors.SEARCH_RESULT_ATTRIBUTES)!!
|
||||
HighlightManager.getInstance(project).addRangeHighlight(
|
||||
editor, textRange.getStartOffset(), textRange.getEndOffset(), attributes, true, highlighters
|
||||
)
|
||||
return highlighters.firstOrNull()
|
||||
}
|
||||
|
||||
public fun JetPsiRange.preview(project: Project, editor: Editor): RangeHighlighter? {
|
||||
return highlight(project, editor)?.let {
|
||||
val startOffset = getTextRange().getStartOffset()
|
||||
val foldedRegions =
|
||||
CodeFoldingManager.getInstance(project)
|
||||
.getFoldRegionsAtOffset(editor, startOffset)
|
||||
.filter { !it.isExpanded() }
|
||||
if (!foldedRegions.empty) {
|
||||
editor.getFoldingModel().runBatchFoldingOperation { foldedRegions.forEach { it.setExpanded(true) } }
|
||||
}
|
||||
editor.getScrollingModel().scrollTo(editor.offsetToLogicalPosition(startOffset), ScrollType.MAKE_VISIBLE)
|
||||
|
||||
it
|
||||
}
|
||||
}
|
||||
|
||||
public fun processDuplicates(
|
||||
duplicateReplacers: Map<JetPsiRange, () -> Unit>,
|
||||
project: Project,
|
||||
editor: Editor
|
||||
) {
|
||||
val size = duplicateReplacers.size
|
||||
if (size == 0) return
|
||||
|
||||
if (size == 1) {
|
||||
duplicateReplacers.keySet().first().preview(project, editor)
|
||||
}
|
||||
|
||||
val answer = if (ApplicationManager.getApplication()!!.isUnitTestMode())
|
||||
Messages.YES
|
||||
else
|
||||
Messages.showYesNoDialog(
|
||||
project,
|
||||
JetRefactoringBundle.message(
|
||||
"0.has.detected.1.code.fragments.in.this.file.that.can.be.replaced.with.a.call.to.extracted.declaration",
|
||||
ApplicationNamesInfo.getInstance()!!.getProductName(),
|
||||
duplicateReplacers.size()
|
||||
),
|
||||
"Process Duplicates",
|
||||
Messages.getQuestionIcon()
|
||||
)
|
||||
if (answer != Messages.YES) return
|
||||
|
||||
var showAll = false
|
||||
for ((i, entry) in duplicateReplacers.entrySet().withIndices()) {
|
||||
val (pattern, replacer) = entry
|
||||
if (!pattern.isValid()) continue
|
||||
|
||||
val highlighter = pattern.preview(project, editor)
|
||||
if (!ApplicationManager.getApplication()!!.isUnitTestMode()) {
|
||||
if (size > 1 && !showAll) {
|
||||
val promptDialog = ReplacePromptDialog(false, RefactoringBundle.message("process.duplicates.title", i + 1, size), project)
|
||||
promptDialog.show()
|
||||
when(promptDialog.getExitCode()) {
|
||||
FindManager.PromptResult.ALL -> showAll = true
|
||||
FindManager.PromptResult.SKIP -> continue
|
||||
FindManager.PromptResult.CANCEL -> return
|
||||
}
|
||||
}
|
||||
}
|
||||
highlighter?.let { HighlightManager.getInstance(project).removeSegmentHighlighter(editor, it) }
|
||||
|
||||
project.executeWriteCommand(MethodDuplicatesHandler.REFACTORING_NAME, replacer)
|
||||
}
|
||||
}
|
||||
+44
-38
@@ -37,7 +37,6 @@ 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
|
||||
@@ -62,24 +61,12 @@ 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()
|
||||
@@ -96,10 +83,17 @@ private fun JetDeclaration.renderForMessage(bindingContext: BindingContext): Str
|
||||
|
||||
private fun JetType.isDefault(): Boolean = KotlinBuiltIns.getInstance().isUnit(this)
|
||||
|
||||
private fun List<Instruction>.getModifiedVarDescriptors(bindingContext: BindingContext): Set<VariableDescriptor> {
|
||||
return this
|
||||
.map {if (it is WriteValueInstruction) PseudocodeUtil.extractVariableDescriptorIfAny(it, false, bindingContext) else null}
|
||||
.filterNotNullTo(HashSet<VariableDescriptor>())
|
||||
private fun List<Instruction>.getModifiedVarDescriptors(bindingContext: BindingContext): Map<VariableDescriptor, List<JetExpression>> {
|
||||
val result = HashMap<VariableDescriptor, MutableList<JetExpression>>()
|
||||
for (instruction in filterIsInstance(javaClass<WriteValueInstruction>())) {
|
||||
val expression = instruction.element as? JetExpression
|
||||
val descriptor = PseudocodeUtil.extractVariableDescriptorIfAny(instruction, false, bindingContext)
|
||||
if (expression != null && descriptor != null) {
|
||||
result.getOrPut(descriptor) { ArrayList() }.add(expression)
|
||||
}
|
||||
}
|
||||
|
||||
return result
|
||||
}
|
||||
|
||||
private fun List<Instruction>.getVarDescriptorsAccessedAfterwards(bindingContext: BindingContext): Set<VariableDescriptor> {
|
||||
@@ -127,17 +121,21 @@ private fun List<Instruction>.getVarDescriptorsAccessedAfterwards(bindingContext
|
||||
private fun List<Instruction>.getExitPoints(): List<Instruction> =
|
||||
filter { localInstruction -> localInstruction.nextInstructions.any { it !in this } }
|
||||
|
||||
private fun List<Instruction>.getResultType(
|
||||
private fun List<Instruction>.getResultTypeAndExpressions(
|
||||
bindingContext: BindingContext,
|
||||
options: ExtractionOptions): JetType {
|
||||
fun instructionToType(instruction: Instruction): JetType? {
|
||||
val expression = when (instruction) {
|
||||
options: ExtractionOptions): Pair<JetType, List<JetExpression>> {
|
||||
fun instructionToExpression(instruction: Instruction, unwrapReturn: Boolean): JetExpression? {
|
||||
return when (instruction) {
|
||||
is ReturnValueInstruction ->
|
||||
instruction.resultExpression
|
||||
(if (unwrapReturn) null else instruction.returnExpressionIfAny) ?: instruction.returnedValue.element as? JetExpression
|
||||
is InstructionWithValue ->
|
||||
instruction.outputValue?.element as? JetExpression
|
||||
else -> null
|
||||
}
|
||||
}
|
||||
|
||||
fun instructionToType(instruction: Instruction): JetType? {
|
||||
val expression = instructionToExpression(instruction, true)
|
||||
|
||||
if (expression == null) return null
|
||||
if (options.inferUnitTypeForUnusedValues && expression.isUsedAsStatement(bindingContext)) return null
|
||||
@@ -146,7 +144,10 @@ private fun List<Instruction>.getResultType(
|
||||
}
|
||||
|
||||
val resultTypes = map(::instructionToType).filterNotNull()
|
||||
return if (resultTypes.isNotEmpty()) CommonSupertypes.commonSupertype(resultTypes) else DEFAULT_RETURN_TYPE
|
||||
val resultType = if (resultTypes.isNotEmpty()) CommonSupertypes.commonSupertype(resultTypes) else DEFAULT_RETURN_TYPE
|
||||
val expressions = map { instructionToExpression(it, false) }.filterNotNull()
|
||||
|
||||
return resultType to expressions
|
||||
}
|
||||
|
||||
private fun List<AbstractJumpInstruction>.checkEquivalence(checkPsi: Boolean): Boolean {
|
||||
@@ -182,7 +183,7 @@ private fun ExtractionData.analyzeControlFlow(
|
||||
pseudocode: Pseudocode,
|
||||
module: ModuleDescriptor,
|
||||
bindingContext: BindingContext,
|
||||
modifiedVarDescriptors: Set<VariableDescriptor>,
|
||||
modifiedVarDescriptors: Map<VariableDescriptor, List<JetExpression>>,
|
||||
options: ExtractionOptions,
|
||||
parameters: Set<Parameter>
|
||||
): Pair<ControlFlow, ErrorMessage?> {
|
||||
@@ -213,7 +214,10 @@ private fun ExtractionData.analyzeControlFlow(
|
||||
is ReturnValueInstruction -> {
|
||||
val returnExpression = insn.returnExpressionIfAny
|
||||
if (returnExpression == null) {
|
||||
defaultExits.add(insn)
|
||||
val containingDeclaration = insn.returnedValue.element?.getParentByType(javaClass<JetDeclarationWithBody>())
|
||||
if (containingDeclaration == pseudocode.getCorrespondingElement()) {
|
||||
defaultExits.add(insn)
|
||||
}
|
||||
}
|
||||
else if (isCurrentFunctionReturn(returnExpression)) {
|
||||
valuedReturnExits.add(insn)
|
||||
@@ -241,8 +245,8 @@ private fun ExtractionData.analyzeControlFlow(
|
||||
val nonLocallyUsedDeclarations = getLocalDeclarationsWithNonLocalUsages(pseudocode, localInstructions, bindingContext)
|
||||
val (declarationsToCopy, declarationsToReport) = nonLocallyUsedDeclarations.partition { it is JetProperty && it.isLocal() }
|
||||
|
||||
val typeOfDefaultFlow = defaultExits.getResultType(bindingContext, options)
|
||||
val returnValueType = valuedReturnExits.getResultType(bindingContext, options)
|
||||
val (typeOfDefaultFlow, defaultResultExpressions) = defaultExits.getResultTypeAndExpressions(bindingContext, options)
|
||||
val (returnValueType, valuedReturnExpressions) = valuedReturnExits.getResultTypeAndExpressions(bindingContext, options)
|
||||
|
||||
val emptyControlFlow =
|
||||
ControlFlow(Collections.emptyList(), { OutputValueBoxer.AsTuple(it, module) }, declarationsToCopy)
|
||||
@@ -251,7 +255,7 @@ private fun ExtractionData.analyzeControlFlow(
|
||||
if (defaultReturnType.isError()) return emptyControlFlow to ErrorMessage.ERROR_TYPES
|
||||
|
||||
val controlFlow = if (defaultReturnType.isMeaningful()) {
|
||||
emptyControlFlow.copy(outputValues = Collections.singletonList(ExpressionValue(false, defaultReturnType)))
|
||||
emptyControlFlow.copy(outputValues = Collections.singletonList(ExpressionValue(false, defaultResultExpressions, defaultReturnType)))
|
||||
}
|
||||
else {
|
||||
emptyControlFlow
|
||||
@@ -263,9 +267,9 @@ private fun ExtractionData.analyzeControlFlow(
|
||||
}
|
||||
|
||||
val outParameters =
|
||||
parameters.filter { it.mirrorVarName != null && it.originalDescriptor in modifiedVarDescriptors }.sortBy { it.nameForRef }
|
||||
parameters.filter { it.mirrorVarName != null && modifiedVarDescriptors[it.originalDescriptor] != null }.sortBy { it.nameForRef }
|
||||
val outDeclarations =
|
||||
declarationsToCopy.filter { bindingContext[BindingContext.DECLARATION_TO_DESCRIPTOR, it] in modifiedVarDescriptors }
|
||||
declarationsToCopy.filter { modifiedVarDescriptors[bindingContext[BindingContext.DECLARATION_TO_DESCRIPTOR, it]] != null }
|
||||
val modifiedValueCount = outParameters.size + outDeclarations.size
|
||||
|
||||
val outputValues = ArrayList<OutputValue>()
|
||||
@@ -276,7 +280,7 @@ private fun ExtractionData.analyzeControlFlow(
|
||||
if (typeOfDefaultFlow.isMeaningful()) {
|
||||
if (valuedReturnExits.isNotEmpty() || jumpExits.isNotEmpty()) return multipleExitsError
|
||||
|
||||
outputValues.add(ExpressionValue(false, typeOfDefaultFlow))
|
||||
outputValues.add(ExpressionValue(false, defaultResultExpressions, typeOfDefaultFlow))
|
||||
}
|
||||
else if (valuedReturnExits.isNotEmpty()) {
|
||||
if (jumpExits.isNotEmpty()) return multipleExitsError
|
||||
@@ -285,19 +289,19 @@ private fun ExtractionData.analyzeControlFlow(
|
||||
if (modifiedValueCount != 0) return outputAndExitsError
|
||||
if (valuedReturnExits.size != 1) return multipleExitsError
|
||||
|
||||
val element = valuedReturnExits.first!!.element
|
||||
val element = valuedReturnExits.first!!.element as JetExpression
|
||||
return controlFlow.copy(outputValues = Collections.singletonList(Jump(listOf(element), element, true))) to null
|
||||
}
|
||||
|
||||
if (!valuedReturnExits.checkEquivalence(false)) return multipleExitsError
|
||||
outputValues.add(ExpressionValue(true, returnValueType))
|
||||
outputValues.add(ExpressionValue(true, valuedReturnExpressions, 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) }
|
||||
outParameters.mapTo(outputValues) { ParameterUpdate(it, modifiedVarDescriptors[it.originalDescriptor]!!) }
|
||||
|
||||
if (outputValues.isNotEmpty()) {
|
||||
if (jumpExits.isNotEmpty()) return outputAndExitsError
|
||||
@@ -322,7 +326,7 @@ private fun ExtractionData.analyzeControlFlow(
|
||||
if (jumpExits.isNotEmpty()) {
|
||||
if (!jumpExits.checkEquivalence(true)) return multipleExitsError
|
||||
|
||||
val elements = jumpExits.map { it.element }
|
||||
val elements = jumpExits.map { it.element as JetExpression }
|
||||
return controlFlow.copy(
|
||||
outputValues = Collections.singletonList(Jump(elements, elements.first(), defaultExits.isNotEmpty()))
|
||||
) to null
|
||||
@@ -674,22 +678,24 @@ fun ExtractionData.performAnalysis(): AnalysisResult {
|
||||
val pseudocode = PseudocodeUtil.generatePseudocode(pseudocodeDeclaration, bindingContext)
|
||||
val localInstructions = getLocalInstructions(pseudocode)
|
||||
|
||||
val modifiedVarDescriptors = localInstructions.getModifiedVarDescriptors(bindingContext)
|
||||
val modifiedVarDescriptorsWithExpressions = localInstructions.getModifiedVarDescriptors(bindingContext)
|
||||
|
||||
val paramsInfo = inferParametersInfo(commonParent, pseudocode, bindingContext, modifiedVarDescriptors)
|
||||
val paramsInfo = inferParametersInfo(commonParent, pseudocode, bindingContext, modifiedVarDescriptorsWithExpressions.keySet())
|
||||
if (paramsInfo.errorMessage != null) {
|
||||
return AnalysisResult(null, Status.CRITICAL_ERROR, listOf(paramsInfo.errorMessage!!))
|
||||
}
|
||||
|
||||
val messages = ArrayList<ErrorMessage>()
|
||||
|
||||
val modifiedVarDescriptorsForControlFlow = HashMap(modifiedVarDescriptorsWithExpressions)
|
||||
modifiedVarDescriptorsForControlFlow.keySet().retainAll(localInstructions.getVarDescriptorsAccessedAfterwards(bindingContext))
|
||||
val (controlFlow, controlFlowMessage) =
|
||||
analyzeControlFlow(
|
||||
localInstructions,
|
||||
pseudocode,
|
||||
resolveSession.getModuleDescriptor(),
|
||||
bindingContext,
|
||||
modifiedVarDescriptors intersect localInstructions.getVarDescriptorsAccessedAfterwards(bindingContext),
|
||||
modifiedVarDescriptorsForControlFlow,
|
||||
options,
|
||||
paramsInfo.parameters
|
||||
)
|
||||
|
||||
@@ -33,7 +33,6 @@ import org.jetbrains.jet.lang.psi.JetNamedDeclaration
|
||||
import org.jetbrains.jet.lang.psi.JetPsiFactory
|
||||
import java.util.LinkedHashMap
|
||||
import java.util.Collections
|
||||
import org.jetbrains.jet.plugin.codeInsight.ShortenReferences
|
||||
import org.jetbrains.jet.lang.psi.psiUtil.isFunctionLiteralOutsideParentheses
|
||||
import com.intellij.psi.util.PsiTreeUtil
|
||||
import org.jetbrains.jet.lang.psi.JetFunctionLiteralArgument
|
||||
@@ -53,6 +52,16 @@ import org.jetbrains.jet.plugin.refactoring.JetNameValidatorImpl
|
||||
import org.jetbrains.jet.plugin.refactoring.JetNameSuggester
|
||||
import org.jetbrains.jet.plugin.refactoring.isMultiLine
|
||||
import org.jetbrains.jet.plugin.refactoring.extractFunction.OutputValueBoxer.AsTuple
|
||||
import org.jetbrains.jet.plugin.util.psi.patternMatching.JetPsiUnifier
|
||||
import org.jetbrains.jet.plugin.util.psi.patternMatching.UnifierParameter
|
||||
import org.jetbrains.jet.plugin.util.psi.patternMatching.toRange
|
||||
import org.jetbrains.jet.plugin.codeInsight.ShortenReferences
|
||||
import org.jetbrains.jet.plugin.util.psi.patternMatching.JetPsiRange
|
||||
import org.jetbrains.jet.plugin.util.psi.patternMatching.UnificationResult
|
||||
import org.jetbrains.jet.plugin.util.psi.patternMatching.JetPsiRange.Match
|
||||
import org.jetbrains.jet.plugin.util.psi.patternMatching.UnificationResult.Status
|
||||
import org.jetbrains.jet.plugin.util.psi.patternMatching.UnificationResult.WeaklyMatched
|
||||
import org.jetbrains.jet.plugin.util.psi.patternMatching.UnificationResult.StronglyMatched
|
||||
|
||||
fun ExtractableCodeDescriptor.getDeclarationText(
|
||||
options: ExtractionGeneratorOptions = ExtractionGeneratorOptions.DEFAULT,
|
||||
@@ -110,6 +119,227 @@ fun createNameCounterpartMap(from: JetElement, to: JetElement): Map<JetSimpleNam
|
||||
return map
|
||||
}
|
||||
|
||||
class DuplicateInfo(
|
||||
val range: JetPsiRange,
|
||||
val controlFlow: ControlFlow,
|
||||
val arguments: List<String>
|
||||
)
|
||||
|
||||
fun ExtractableCodeDescriptor.findDuplicates(): List<DuplicateInfo> {
|
||||
fun processWeakMatch(match: Match, newControlFlow: ControlFlow): Boolean {
|
||||
val valueCount = controlFlow.outputValues.size
|
||||
|
||||
val weakMatches = HashMap((match.result as WeaklyMatched).weakMatches)
|
||||
val currentValuesToNew = HashMap<OutputValue, OutputValue>()
|
||||
|
||||
fun matchValues(currentValue: OutputValue, newValue: OutputValue): Boolean {
|
||||
if ((currentValue is Jump) != (newValue is Jump)) return false
|
||||
if (currentValue.originalExpressions.zip(newValue.originalExpressions).all { weakMatches[it.first] == it.second }) {
|
||||
currentValuesToNew[currentValue] = newValue
|
||||
weakMatches.keySet().removeAll(currentValue.originalExpressions)
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
if (valueCount == 1) {
|
||||
matchValues(controlFlow.outputValues.first(), newControlFlow.outputValues.first())
|
||||
} else {
|
||||
@outer
|
||||
for (currentValue in controlFlow.outputValues)
|
||||
for (newValue in newControlFlow.outputValues) {
|
||||
if ((currentValue is ExpressionValue) != (newValue is ExpressionValue)) continue
|
||||
if (matchValues(currentValue, newValue)) continue @outer
|
||||
}
|
||||
}
|
||||
|
||||
return currentValuesToNew.size == valueCount && weakMatches.isEmpty()
|
||||
}
|
||||
|
||||
fun getControlFlowIfMatched(match: Match): ControlFlow? {
|
||||
val analysisResult = extractionData.copy(originalRange = match.range).performAnalysis()
|
||||
if (analysisResult.status != AnalysisResult.Status.SUCCESS) return null
|
||||
|
||||
val newControlFlow = analysisResult.descriptor!!.controlFlow
|
||||
if (newControlFlow.outputValues.isEmpty()) return newControlFlow
|
||||
if (controlFlow.outputValues.size != newControlFlow.outputValues.size) return null
|
||||
|
||||
val matched = when (match.result) {
|
||||
is StronglyMatched -> true
|
||||
is WeaklyMatched -> processWeakMatch(match, newControlFlow)
|
||||
else -> throw AssertionError("Unexpected unification result: ${match.result}")
|
||||
}
|
||||
|
||||
return if (matched) newControlFlow else null
|
||||
}
|
||||
|
||||
val unifierParameters = parameters.map { UnifierParameter(it.originalDescriptor, it.parameterType) }
|
||||
|
||||
val unifier = JetPsiUnifier(unifierParameters, true)
|
||||
|
||||
val scopeElement = extractionData.targetSibling.getParent() ?: return Collections.emptyList()
|
||||
val originalTextRange = extractionData.originalRange.getTextRange()
|
||||
return extractionData
|
||||
.originalRange
|
||||
.match(scopeElement, unifier)
|
||||
.stream()
|
||||
.filter { !(it.range.getTextRange() intersects originalTextRange) }
|
||||
.map { match ->
|
||||
val controlFlow = getControlFlowIfMatched(match)
|
||||
controlFlow?.let { DuplicateInfo(match.range, it, unifierParameters.map { match.result.substitution[it]!!.getText()!! }) }
|
||||
}
|
||||
.filterNotNull()
|
||||
.toList()
|
||||
}
|
||||
|
||||
private fun makeCall(
|
||||
name: String,
|
||||
declaration: JetNamedDeclaration,
|
||||
controlFlow: ControlFlow,
|
||||
rangeToReplace: JetPsiRange,
|
||||
arguments: List<String>) {
|
||||
fun insertCall(anchor: PsiElement, wrappedCall: JetExpression) {
|
||||
val firstExpression = rangeToReplace.elements.firstOrNull { it is JetExpression } as? JetExpression
|
||||
if (firstExpression?.isFunctionLiteralOutsideParentheses() ?: false) {
|
||||
val functionLiteralArgument = PsiTreeUtil.getParentOfType(firstExpression, javaClass<JetFunctionLiteralArgument>())!!
|
||||
//todo use the right binding context
|
||||
functionLiteralArgument.moveInsideParenthesesAndReplaceWith(wrappedCall, BindingContext.EMPTY)
|
||||
return
|
||||
}
|
||||
anchor.replace(wrappedCall)
|
||||
}
|
||||
|
||||
if (rangeToReplace !is JetPsiRange.ListRange) return
|
||||
|
||||
val anchor = rangeToReplace.startElement
|
||||
val anchorParent = anchor.getParent()!!
|
||||
|
||||
anchor.getNextSibling()?.let { from ->
|
||||
val to = rangeToReplace.endElement
|
||||
if (to != anchor) {
|
||||
anchorParent.deleteChildRange(from, to);
|
||||
}
|
||||
}
|
||||
|
||||
val callText = when (declaration) {
|
||||
is JetNamedFunction ->
|
||||
arguments.joinToString(separator = ", ", prefix = "${name}(", postfix = ")")
|
||||
else -> name
|
||||
}
|
||||
|
||||
val anchorInBlock = stream(anchor) { it.getParent() }.firstOrNull { it.getParent() is JetBlockExpression }
|
||||
val block = (anchorInBlock?.getParent() as? JetBlockExpression) ?: anchorParent
|
||||
|
||||
val psiFactory = JetPsiFactory(anchor.getProject())
|
||||
val newLine = psiFactory.createNewLine()
|
||||
|
||||
if (controlFlow.outputValueBoxer is AsTuple && controlFlow.outputValues.size > 1 && controlFlow.outputValues.all { it is Initializer }) {
|
||||
val declarationsToMerge = controlFlow.outputValues.map { (it as Initializer).initializedDeclaration }
|
||||
val isVar = declarationsToMerge.first().isVar()
|
||||
if (declarationsToMerge.all { it.isVar() == isVar }) {
|
||||
controlFlow.declarationsToCopy.subtract(declarationsToMerge).forEach {
|
||||
block.addBefore(psiFactory.createDeclaration<JetDeclaration>(it.getText()!!), anchorInBlock) as JetDeclaration
|
||||
block.addBefore(newLine, anchorInBlock)
|
||||
}
|
||||
|
||||
val entries = declarationsToMerge.map { p -> p.getName() + (p.getTypeRef()?.let { ": ${it.getText()}" } ?: "") }
|
||||
anchorInBlock?.replace(
|
||||
psiFactory.createDeclaration("${if (isVar) "var" else "val"} (${entries.joinToString()}) = $callText")
|
||||
)
|
||||
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
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] = block.addBefore(declCopy, anchorInBlock) as JetDeclaration
|
||||
block.addBefore(newLine, anchorInBlock)
|
||||
}
|
||||
|
||||
if (controlFlow.outputValues.isEmpty()) {
|
||||
anchor.replace(psiFactory.createExpression(callText))
|
||||
return
|
||||
}
|
||||
|
||||
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()
|
||||
}
|
||||
}
|
||||
|
||||
fun ExtractableCodeDescriptor.generateDeclaration(options: ExtractionGeneratorOptions): ExtractionResult{
|
||||
val psiFactory = JetPsiFactory(extractionData.originalFile)
|
||||
val nameByOffset = HashMap<Int, JetElement>()
|
||||
@@ -275,156 +505,16 @@ fun ExtractableCodeDescriptor.generateDeclaration(options: ExtractionGeneratorOp
|
||||
}
|
||||
}
|
||||
|
||||
fun insertCall(anchor: PsiElement, wrappedCall: JetExpression) {
|
||||
val firstExpression = extractionData.getExpressions().firstOrNull()
|
||||
if (firstExpression?.isFunctionLiteralOutsideParentheses() ?: false) {
|
||||
val functionLiteralArgument = PsiTreeUtil.getParentOfType(firstExpression, javaClass<JetFunctionLiteralArgument>())!!
|
||||
//todo use the right binding context
|
||||
functionLiteralArgument.moveInsideParenthesesAndReplaceWith(wrappedCall, BindingContext.EMPTY)
|
||||
return
|
||||
}
|
||||
anchor.replace(wrappedCall)
|
||||
}
|
||||
|
||||
fun makeCall(declaration: JetNamedDeclaration) {
|
||||
val anchor = extractionData.originalElements.first
|
||||
if (anchor == null) return
|
||||
|
||||
val anchorParent = anchor.getParent()!!
|
||||
|
||||
anchor.getNextSibling()?.let { from ->
|
||||
val to = extractionData.originalElements.last
|
||||
if (to != anchor) {
|
||||
anchorParent.deleteChildRange(from, to);
|
||||
}
|
||||
}
|
||||
|
||||
val callText = when (declaration) {
|
||||
is JetNamedFunction ->
|
||||
parameters
|
||||
.map { it.argumentText }
|
||||
.joinToString(separator = ", ", prefix = "${name}(", postfix = ")")
|
||||
else -> name
|
||||
}
|
||||
|
||||
val anchorInBlock = stream(anchor) { it.getParent() }.firstOrNull { it.getParent() is JetBlockExpression }
|
||||
val block = (anchorInBlock?.getParent() as? JetBlockExpression) ?: anchorParent
|
||||
|
||||
val newLine = psiFactory.createNewLine()
|
||||
|
||||
if (controlFlow.outputValueBoxer is AsTuple && controlFlow.outputValues.size > 1 && controlFlow.outputValues.all { it is Initializer }) {
|
||||
val declarationsToMerge = controlFlow.outputValues.map { (it as Initializer).initializedDeclaration }
|
||||
val isVar = declarationsToMerge.first().isVar()
|
||||
if (declarationsToMerge.all { it.isVar() == isVar }) {
|
||||
controlFlow.declarationsToCopy.subtract(declarationsToMerge).forEach {
|
||||
block.addBefore(psiFactory.createDeclaration<JetDeclaration>(it.getText()!!), anchorInBlock) as JetDeclaration
|
||||
block.addBefore(newLine, anchorInBlock)
|
||||
}
|
||||
|
||||
val entries = declarationsToMerge.map { p -> p.getName() + (p.getTypeRef()?.let { ": ${it.getText()}" } ?: "") }
|
||||
anchorInBlock?.replace(
|
||||
psiFactory.createDeclaration("${if (isVar) "var" else "val"} (${entries.joinToString()}) = $callText")
|
||||
)
|
||||
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
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] = block.addBefore(declCopy, anchorInBlock) as JetDeclaration
|
||||
block.addBefore(newLine, anchorInBlock)
|
||||
}
|
||||
|
||||
if (controlFlow.outputValues.isEmpty()) {
|
||||
anchor.replace(psiFactory.createExpression(callText))
|
||||
return
|
||||
}
|
||||
|
||||
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 duplicates = if (options.inTempFile) Collections.emptyList() else findDuplicates()
|
||||
|
||||
val declaration = createDeclaration().let { if (options.inTempFile) it else insertDeclaration(it) }
|
||||
adjustDeclarationBody(declaration)
|
||||
|
||||
if (options.inTempFile) return ExtractionResult(declaration, nameByOffset)
|
||||
if (options.inTempFile) return ExtractionResult(declaration, Collections.emptyMap(), nameByOffset)
|
||||
|
||||
makeCall(declaration)
|
||||
ShortenReferences.process(declaration)
|
||||
return ExtractionResult(declaration, nameByOffset)
|
||||
makeCall(name, declaration, controlFlow, extractionData.originalRange, parameters.map { it.argumentText })
|
||||
ShortenReferences.process(declaration)
|
||||
|
||||
val duplicateReplacers = duplicates.map { it.range to { makeCall(name, declaration, it.controlFlow, it.range, it.arguments) } }.toMap()
|
||||
return ExtractionResult(declaration, duplicateReplacers, nameByOffset)
|
||||
}
|
||||
+1
-1
@@ -232,7 +232,7 @@ public class KotlinExtractFunctionDialog extends DialogWrapper {
|
||||
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())));
|
||||
outputValues.set(i, new OutputValue.ParameterUpdate(oldToNewParameters.get(parameterUpdate.getParameter()), parameterUpdate.getOriginalExpressions()));
|
||||
}
|
||||
}
|
||||
controlFlow = new ControlFlow(outputValues, controlFlow.getBoxerFactory(), controlFlow.getDeclarationsToCopy());
|
||||
|
||||
@@ -66,6 +66,8 @@ import com.intellij.openapi.editor.Document
|
||||
import com.intellij.psi.PsiDocumentManager
|
||||
import com.intellij.psi.PsiElementVisitor
|
||||
import com.intellij.psi.PsiWhiteSpace
|
||||
import com.intellij.openapi.util.Computable
|
||||
import com.intellij.openapi.ui.DialogWrapper
|
||||
|
||||
/**
|
||||
* Replace [[JetSimpleNameExpression]] (and its enclosing qualifier) with qualified element given by FqName
|
||||
@@ -190,6 +192,12 @@ public fun Project.executeWriteCommand(name: String, command: () -> Unit) {
|
||||
CommandProcessor.getInstance().executeCommand(this, { runWriteAction(command) }, name, null)
|
||||
}
|
||||
|
||||
public fun <T: Any> Project.executeWriteCommand(name: String, command: () -> T): T {
|
||||
var result: T? = null
|
||||
CommandProcessor.getInstance().executeCommand(this, { result = runWriteAction(command) }, name, null)
|
||||
return result!!
|
||||
}
|
||||
|
||||
public fun <T : PsiElement> getPsiElementPopup(
|
||||
editor: Editor,
|
||||
elements: Array<T>,
|
||||
|
||||
+1
-1
@@ -1,6 +1,6 @@
|
||||
// SIBLING:
|
||||
fun foo(a: Int): Int {
|
||||
val b: Int = 1
|
||||
val b: Int = i()
|
||||
return if (a + b > 0) i() else if (a - b < 0) 2 else b
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,87 @@
|
||||
// WITH_RUNTIME
|
||||
// PARAM_TYPES: kotlin.Int
|
||||
// PARAM_TYPES: kotlin.Int
|
||||
// PARAM_DESCRIPTOR: value-parameter val a: kotlin.Int defined in test
|
||||
// PARAM_DESCRIPTOR: var b: kotlin.Int defined in test
|
||||
|
||||
// SIBLING:
|
||||
fun test(a: Int): Int {
|
||||
var b: Int = 1
|
||||
val t = <selection>if (a > 0) {
|
||||
b++
|
||||
b + a
|
||||
}
|
||||
else {
|
||||
b--
|
||||
b - a
|
||||
}</selection>
|
||||
return t
|
||||
}
|
||||
|
||||
fun foo1() {
|
||||
val x = 1
|
||||
var y: Int = x
|
||||
println(
|
||||
if (x > 0) {
|
||||
y++
|
||||
y + x
|
||||
}
|
||||
else {
|
||||
y--
|
||||
y - x
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
fun foo2(x: Int) {
|
||||
var p: Int = 1
|
||||
var q: Int
|
||||
if (x > 0) {
|
||||
p++
|
||||
q = p + x
|
||||
}
|
||||
else {
|
||||
p--
|
||||
q = p - x
|
||||
}
|
||||
println(q)
|
||||
}
|
||||
|
||||
fun foo3(x: Int): Int {
|
||||
var p: Int = 1
|
||||
if (x > 0) {
|
||||
p++
|
||||
return p + x
|
||||
}
|
||||
else {
|
||||
p--
|
||||
return p - x
|
||||
}
|
||||
}
|
||||
|
||||
fun foo4() {
|
||||
val t: (Int) -> (Int) = {
|
||||
var n = it
|
||||
if (it > 0) {
|
||||
n++
|
||||
n + it
|
||||
}
|
||||
else {
|
||||
n--
|
||||
n - it
|
||||
}
|
||||
}
|
||||
println(t(1))
|
||||
}
|
||||
|
||||
fun foo5(x: Int): Int {
|
||||
var p: Int = 1
|
||||
if (x > 0) {
|
||||
p++
|
||||
val t = p + x
|
||||
}
|
||||
else {
|
||||
p--
|
||||
val u = p - x
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,70 @@
|
||||
// WITH_RUNTIME
|
||||
// PARAM_TYPES: kotlin.Int
|
||||
// PARAM_TYPES: kotlin.Int
|
||||
// PARAM_DESCRIPTOR: value-parameter val a: kotlin.Int defined in test
|
||||
// PARAM_DESCRIPTOR: var b: kotlin.Int defined in test
|
||||
|
||||
// SIBLING:
|
||||
fun test(a: Int): Int {
|
||||
var b: Int = 1
|
||||
val t = i(a, b)
|
||||
return t
|
||||
}
|
||||
|
||||
private fun i(a: Int, b: Int): Int {
|
||||
var b1 = b
|
||||
return if (a > 0) {
|
||||
b1++
|
||||
b1 + a
|
||||
} else {
|
||||
b1--
|
||||
b1 - a
|
||||
}
|
||||
}
|
||||
|
||||
fun foo1() {
|
||||
val x = 1
|
||||
var y: Int = x
|
||||
println(
|
||||
i(x, y)
|
||||
)
|
||||
}
|
||||
|
||||
fun foo2(x: Int) {
|
||||
var p: Int = 1
|
||||
var q: Int
|
||||
if (x > 0) {
|
||||
p++
|
||||
q = p + x
|
||||
}
|
||||
else {
|
||||
p--
|
||||
q = p - x
|
||||
}
|
||||
println(q)
|
||||
}
|
||||
|
||||
fun foo3(x: Int): Int {
|
||||
var p: Int = 1
|
||||
if (x > 0) {
|
||||
p++
|
||||
return p + x
|
||||
}
|
||||
else {
|
||||
p--
|
||||
return p - x
|
||||
}
|
||||
}
|
||||
|
||||
fun foo4() {
|
||||
val t: (Int) -> (Int) = {
|
||||
var n = it
|
||||
i(it, n)
|
||||
}
|
||||
println(t(1))
|
||||
}
|
||||
|
||||
fun foo5(x: Int): Int {
|
||||
var p: Int = 1
|
||||
i(x, p)
|
||||
}
|
||||
@@ -0,0 +1,74 @@
|
||||
// WITH_RUNTIME
|
||||
// PARAM_TYPES: kotlin.Int
|
||||
// PARAM_TYPES: kotlin.Int
|
||||
// PARAM_DESCRIPTOR: value-parameter val a: kotlin.Int defined in test
|
||||
// PARAM_DESCRIPTOR: var b: kotlin.Int defined in test
|
||||
|
||||
// SIBLING:
|
||||
fun test(a: Int): Int {
|
||||
var b: Int = 1
|
||||
<selection>if (a > 0) {
|
||||
b = b + a
|
||||
}
|
||||
else {
|
||||
b = b - a
|
||||
}</selection>
|
||||
return b
|
||||
}
|
||||
|
||||
fun foo1() {
|
||||
val x = 1
|
||||
var y: Int = x
|
||||
println(
|
||||
if (x > 0) {
|
||||
y + x
|
||||
}
|
||||
else {
|
||||
y - x
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
fun foo2(x: Int) {
|
||||
var p: Int = 1
|
||||
if (x > 0) {
|
||||
p = p + x
|
||||
}
|
||||
else {
|
||||
p = p - x
|
||||
}
|
||||
println(p)
|
||||
}
|
||||
|
||||
fun foo3(x: Int): Int {
|
||||
var p: Int = 1
|
||||
if (x > 0) {
|
||||
return p + x
|
||||
}
|
||||
else {
|
||||
return p - x
|
||||
}
|
||||
}
|
||||
|
||||
fun foo4() {
|
||||
val t: (Int) -> (Int) = {
|
||||
var n = it
|
||||
if (it > 0) {
|
||||
n + it
|
||||
}
|
||||
else {
|
||||
n - it
|
||||
}
|
||||
}
|
||||
println(t(1))
|
||||
}
|
||||
|
||||
fun foo5(x: Int): Int {
|
||||
var p: Int = 1
|
||||
if (x > 0) {
|
||||
val t = p + x
|
||||
}
|
||||
else {
|
||||
val u = p - x
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,64 @@
|
||||
// WITH_RUNTIME
|
||||
// PARAM_TYPES: kotlin.Int
|
||||
// PARAM_TYPES: kotlin.Int
|
||||
// PARAM_DESCRIPTOR: value-parameter val a: kotlin.Int defined in test
|
||||
// PARAM_DESCRIPTOR: var b: kotlin.Int defined in test
|
||||
|
||||
// SIBLING:
|
||||
fun test(a: Int): Int {
|
||||
var b: Int = 1
|
||||
b = i(a, b)
|
||||
return b
|
||||
}
|
||||
|
||||
private fun i(a: Int, b: Int): Int {
|
||||
var b1 = b
|
||||
if (a > 0) {
|
||||
b1 = b1 + a
|
||||
} else {
|
||||
b1 = b1 - a
|
||||
}
|
||||
return b1
|
||||
}
|
||||
|
||||
fun foo1() {
|
||||
val x = 1
|
||||
var y: Int = x
|
||||
println(
|
||||
if (x > 0) {
|
||||
y + x
|
||||
}
|
||||
else {
|
||||
y - x
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
fun foo2(x: Int) {
|
||||
var p: Int = 1
|
||||
p = i(x, p)
|
||||
println(p)
|
||||
}
|
||||
|
||||
fun foo3(x: Int): Int {
|
||||
var p: Int = 1
|
||||
return i(x, p)
|
||||
}
|
||||
|
||||
fun foo4() {
|
||||
val t: (Int) -> (Int) = {
|
||||
var n = it
|
||||
if (it > 0) {
|
||||
n + it
|
||||
}
|
||||
else {
|
||||
n - it
|
||||
}
|
||||
}
|
||||
println(t(1))
|
||||
}
|
||||
|
||||
fun foo5(x: Int): Int {
|
||||
var p: Int = 1
|
||||
i(x, p)
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
// PARAM_TYPES: kotlin.Int
|
||||
// PARAM_TYPES: kotlin.Int
|
||||
// PARAM_DESCRIPTOR: value-parameter val a: kotlin.Int defined in foo
|
||||
// PARAM_DESCRIPTOR: value-parameter val b: kotlin.Int defined in foo
|
||||
|
||||
// SIBLING:
|
||||
fun foo(a: Int, b: Int) {
|
||||
<selection>println("a = $a")
|
||||
println("b = $b")
|
||||
println(a + b*a)</selection>
|
||||
}
|
||||
|
||||
fun bar() {
|
||||
val x = 1
|
||||
val y = 2
|
||||
|
||||
println("a = $x")
|
||||
println("b = $y")
|
||||
println(x + y*x)
|
||||
}
|
||||
@@ -0,0 +1,22 @@
|
||||
// PARAM_TYPES: kotlin.Int
|
||||
// PARAM_TYPES: kotlin.Int
|
||||
// PARAM_DESCRIPTOR: value-parameter val a: kotlin.Int defined in foo
|
||||
// PARAM_DESCRIPTOR: value-parameter val b: kotlin.Int defined in foo
|
||||
|
||||
// SIBLING:
|
||||
fun foo(a: Int, b: Int) {
|
||||
unit(a, b)
|
||||
}
|
||||
|
||||
private fun unit(a: Int, b: Int) {
|
||||
println("a = $a")
|
||||
println("b = $b")
|
||||
println(a + b * a)
|
||||
}
|
||||
|
||||
fun bar() {
|
||||
val x = 1
|
||||
val y = 2
|
||||
|
||||
unit(x, y)
|
||||
}
|
||||
@@ -0,0 +1,30 @@
|
||||
// WITH_RUNTIME
|
||||
// PARAM_TYPES: kotlin.Int
|
||||
// PARAM_DESCRIPTOR: value-parameter val a: kotlin.Int defined in test
|
||||
|
||||
// SIBLING:
|
||||
fun test(a: Int): Int {
|
||||
<selection>val b = a + 1
|
||||
val c = a - 1</selection>
|
||||
return b*c
|
||||
}
|
||||
|
||||
fun foo1(a: Int) {
|
||||
var x = a + 1
|
||||
var y = a - 1
|
||||
println(x + y)
|
||||
}
|
||||
|
||||
fun foo2(): Int {
|
||||
var p: Int = 1
|
||||
var q: Int
|
||||
p = p + 1
|
||||
q = p - 1
|
||||
return p + q
|
||||
}
|
||||
|
||||
fun foo4(a: Int) {
|
||||
var b = a
|
||||
b = b + 1
|
||||
return b - 1
|
||||
}
|
||||
+35
@@ -0,0 +1,35 @@
|
||||
// WITH_RUNTIME
|
||||
// PARAM_TYPES: kotlin.Int
|
||||
// PARAM_DESCRIPTOR: value-parameter val a: kotlin.Int defined in test
|
||||
|
||||
// SIBLING:
|
||||
fun test(a: Int): Int {
|
||||
val (b, c) = pair(a)
|
||||
return b*c
|
||||
}
|
||||
|
||||
private fun pair(a: Int): Pair<Int, Int> {
|
||||
val b = a + 1
|
||||
val c = a - 1
|
||||
return Pair(b, c)
|
||||
}
|
||||
|
||||
fun foo1(a: Int) {
|
||||
var (x, y) = pair(a)
|
||||
println(x + y)
|
||||
}
|
||||
|
||||
fun foo2(): Int {
|
||||
var p: Int = 1
|
||||
var q: Int
|
||||
val pair = pair(p)
|
||||
p = pair.first
|
||||
q = pair.second
|
||||
return p + q
|
||||
}
|
||||
|
||||
fun foo4(a: Int) {
|
||||
var b = a
|
||||
b = b + 1
|
||||
return b - 1
|
||||
}
|
||||
@@ -0,0 +1,22 @@
|
||||
// WITH_RUNTIME
|
||||
// PARAM_TYPES: kotlin.Int
|
||||
// PARAM_DESCRIPTOR: val z: kotlin.Int defined in test
|
||||
|
||||
// SIBLING:
|
||||
fun test(): () -> Int {
|
||||
val z = 1
|
||||
return {
|
||||
<selection>println(z)
|
||||
z + 1</selection>
|
||||
}
|
||||
}
|
||||
|
||||
fun foo1(a: Int): Int {
|
||||
val t = println(a)
|
||||
return a + 1
|
||||
}
|
||||
|
||||
fun foo2(a: Int) {
|
||||
println(a)
|
||||
a + 1
|
||||
}
|
||||
+25
@@ -0,0 +1,25 @@
|
||||
// WITH_RUNTIME
|
||||
// PARAM_TYPES: kotlin.Int
|
||||
// PARAM_DESCRIPTOR: val z: kotlin.Int defined in test
|
||||
|
||||
// SIBLING:
|
||||
fun test(): () -> Int {
|
||||
val z = 1
|
||||
return {
|
||||
i(z)
|
||||
}
|
||||
}
|
||||
|
||||
private fun i(z: Int): Int {
|
||||
println(z)
|
||||
return z + 1
|
||||
}
|
||||
|
||||
fun foo1(a: Int): Int {
|
||||
val t = println(a)
|
||||
return a + 1
|
||||
}
|
||||
|
||||
fun foo2(a: Int) {
|
||||
i(a)
|
||||
}
|
||||
@@ -0,0 +1,41 @@
|
||||
// WITH_RUNTIME
|
||||
// PARAM_TYPES: kotlin.Int
|
||||
// PARAM_DESCRIPTOR: value-parameter val a: kotlin.Int defined in test
|
||||
|
||||
// SIBLING:
|
||||
fun test(a: Int): Int {
|
||||
<selection>println(a)
|
||||
return a + 1</selection>
|
||||
}
|
||||
|
||||
fun foo1(): Int {
|
||||
val x = 1
|
||||
println(x)
|
||||
val y = x + 1
|
||||
return y
|
||||
}
|
||||
|
||||
fun foo2(): () -> Int {
|
||||
val z = 1
|
||||
return {
|
||||
println(z)
|
||||
z + 1
|
||||
}
|
||||
}
|
||||
|
||||
fun foo3() {
|
||||
var t = 1
|
||||
println(t)
|
||||
t = t + 1
|
||||
println(t)
|
||||
}
|
||||
|
||||
fun foo4(a: Int): Int {
|
||||
val t = println(a)
|
||||
return a + 1
|
||||
}
|
||||
|
||||
fun foo5(a: Int) {
|
||||
println(a)
|
||||
a + 1
|
||||
}
|
||||
@@ -0,0 +1,41 @@
|
||||
// WITH_RUNTIME
|
||||
// PARAM_TYPES: kotlin.Int
|
||||
// PARAM_DESCRIPTOR: value-parameter val a: kotlin.Int defined in test
|
||||
|
||||
// SIBLING:
|
||||
fun test(a: Int): Int {
|
||||
return i(a)
|
||||
}
|
||||
|
||||
private fun i(a: Int): Int {
|
||||
println(a)
|
||||
return a + 1
|
||||
}
|
||||
|
||||
fun foo1(): Int {
|
||||
val x = 1
|
||||
val y = i(x)
|
||||
return y
|
||||
}
|
||||
|
||||
fun foo2(): () -> Int {
|
||||
val z = 1
|
||||
return {
|
||||
i(z)
|
||||
}
|
||||
}
|
||||
|
||||
fun foo3() {
|
||||
var t = 1
|
||||
t = i(t)
|
||||
println(t)
|
||||
}
|
||||
|
||||
fun foo4(a: Int): Int {
|
||||
val t = println(a)
|
||||
return a + 1
|
||||
}
|
||||
|
||||
fun foo5(a: Int) {
|
||||
i(a)
|
||||
}
|
||||
@@ -0,0 +1,25 @@
|
||||
// PARAM_TYPES: kotlin.Int
|
||||
// PARAM_TYPES: kotlin.Int
|
||||
// PARAM_DESCRIPTOR: value-parameter val a: kotlin.Int defined in foo
|
||||
// PARAM_DESCRIPTOR: value-parameter val b: kotlin.Int defined in foo
|
||||
|
||||
// SIBLING:
|
||||
fun foo(a: Int, b: Int): Int {
|
||||
return <selection>a + b*a</selection> + 1
|
||||
}
|
||||
|
||||
fun bar() {
|
||||
fun f() = 1
|
||||
|
||||
val a = 1
|
||||
val b = 2
|
||||
val c = 3
|
||||
|
||||
c + b*c
|
||||
b + a*b
|
||||
a plus a*a
|
||||
a + a*b
|
||||
a + c
|
||||
f() + a*f()
|
||||
f() + f().times(f())
|
||||
}
|
||||
@@ -0,0 +1,29 @@
|
||||
// PARAM_TYPES: kotlin.Int
|
||||
// PARAM_TYPES: kotlin.Int
|
||||
// PARAM_DESCRIPTOR: value-parameter val a: kotlin.Int defined in foo
|
||||
// PARAM_DESCRIPTOR: value-parameter val b: kotlin.Int defined in foo
|
||||
|
||||
// SIBLING:
|
||||
fun foo(a: Int, b: Int): Int {
|
||||
return i(a, b) + 1
|
||||
}
|
||||
|
||||
private fun i(a: Int, b: Int): Int {
|
||||
return a + b * a
|
||||
}
|
||||
|
||||
fun bar() {
|
||||
fun f() = 1
|
||||
|
||||
val a = 1
|
||||
val b = 2
|
||||
val c = 3
|
||||
|
||||
i(c, b)
|
||||
i(b, a)
|
||||
i(a, a)
|
||||
a + a*b
|
||||
a + c
|
||||
i(f(), a)
|
||||
i(f(), f())
|
||||
}
|
||||
+57
-3
@@ -175,7 +175,8 @@ public class JetExtractionTestGenerated extends AbstractJetExtractionTest {
|
||||
|
||||
@TestMetadata("OccurrencesInStringTemplate.kt")
|
||||
public void testOccurrencesInStringTemplate() throws Exception {
|
||||
doIntroduceVariableTest("idea/testData/refactoring/introduceVariable/OccurrencesInStringTemplate.kt");
|
||||
String fileName = JetTestUtils.navigationMetadata("idea/testData/refactoring/introduceVariable/OccurrencesInStringTemplate.kt");
|
||||
doIntroduceVariableTest(fileName);
|
||||
}
|
||||
|
||||
@TestMetadata("OneExplicitReceiver.kt")
|
||||
@@ -216,7 +217,8 @@ public class JetExtractionTestGenerated extends AbstractJetExtractionTest {
|
||||
|
||||
@TestMetadata("UnresolvedOccurrences.kt")
|
||||
public void testUnresolvedOccurrences() throws Exception {
|
||||
doIntroduceVariableTest("idea/testData/refactoring/introduceVariable/UnresolvedOccurrences.kt");
|
||||
String fileName = JetTestUtils.navigationMetadata("idea/testData/refactoring/introduceVariable/UnresolvedOccurrences.kt");
|
||||
doIntroduceVariableTest(fileName);
|
||||
}
|
||||
|
||||
@TestMetadata("WhenAddBlock.kt")
|
||||
@@ -259,7 +261,7 @@ public class JetExtractionTestGenerated extends AbstractJetExtractionTest {
|
||||
|
||||
@TestMetadata("idea/testData/refactoring/extractFunction")
|
||||
@TestDataPath("$PROJECT_ROOT")
|
||||
@InnerTestClasses({ExtractFunction.AsProperty.class, ExtractFunction.Basic.class, ExtractFunction.ControlFlow.class, ExtractFunction.DefaultContainer.class, ExtractFunction.Delegation.class, ExtractFunction.Initializers.class, ExtractFunction.Parameters.class, ExtractFunction.TypeParameters.class})
|
||||
@InnerTestClasses({ExtractFunction.AsProperty.class, ExtractFunction.Basic.class, ExtractFunction.ControlFlow.class, ExtractFunction.DefaultContainer.class, ExtractFunction.Delegation.class, ExtractFunction.Duplicates.class, ExtractFunction.Initializers.class, ExtractFunction.Parameters.class, ExtractFunction.TypeParameters.class})
|
||||
@RunWith(org.jetbrains.jet.JUnit3RunnerWithInners.class)
|
||||
public static class ExtractFunction extends AbstractJetExtractionTest {
|
||||
public void testAllFilesPresentInExtractFunction() throws Exception {
|
||||
@@ -1051,6 +1053,58 @@ public class JetExtractionTestGenerated extends AbstractJetExtractionTest {
|
||||
|
||||
}
|
||||
|
||||
@TestMetadata("idea/testData/refactoring/extractFunction/duplicates")
|
||||
@TestDataPath("$PROJECT_ROOT")
|
||||
@RunWith(org.jetbrains.jet.JUnit3RunnerWithInners.class)
|
||||
public static class Duplicates extends AbstractJetExtractionTest {
|
||||
public void testAllFilesPresentInDuplicates() throws Exception {
|
||||
JetTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("idea/testData/refactoring/extractFunction/duplicates"), Pattern.compile("^(.+)\\.kt$"), true);
|
||||
}
|
||||
|
||||
@TestMetadata("branchingMatch1.kt")
|
||||
public void testBranchingMatch1() throws Exception {
|
||||
String fileName = JetTestUtils.navigationMetadata("idea/testData/refactoring/extractFunction/duplicates/branchingMatch1.kt");
|
||||
doExtractFunctionTest(fileName);
|
||||
}
|
||||
|
||||
@TestMetadata("branchingMatch2.kt")
|
||||
public void testBranchingMatch2() throws Exception {
|
||||
String fileName = JetTestUtils.navigationMetadata("idea/testData/refactoring/extractFunction/duplicates/branchingMatch2.kt");
|
||||
doExtractFunctionTest(fileName);
|
||||
}
|
||||
|
||||
@TestMetadata("defaultCF.kt")
|
||||
public void testDefaultCF() throws Exception {
|
||||
String fileName = JetTestUtils.navigationMetadata("idea/testData/refactoring/extractFunction/duplicates/defaultCF.kt");
|
||||
doExtractFunctionTest(fileName);
|
||||
}
|
||||
|
||||
@TestMetadata("multipleOutputValuesMatching.kt")
|
||||
public void testMultipleOutputValuesMatching() throws Exception {
|
||||
String fileName = JetTestUtils.navigationMetadata("idea/testData/refactoring/extractFunction/duplicates/multipleOutputValuesMatching.kt");
|
||||
doExtractFunctionTest(fileName);
|
||||
}
|
||||
|
||||
@TestMetadata("outputValueAndUnitMatching.kt")
|
||||
public void testOutputValueAndUnitMatching() throws Exception {
|
||||
String fileName = JetTestUtils.navigationMetadata("idea/testData/refactoring/extractFunction/duplicates/outputValueAndUnitMatching.kt");
|
||||
doExtractFunctionTest(fileName);
|
||||
}
|
||||
|
||||
@TestMetadata("outputValueMatching.kt")
|
||||
public void testOutputValueMatching() throws Exception {
|
||||
String fileName = JetTestUtils.navigationMetadata("idea/testData/refactoring/extractFunction/duplicates/outputValueMatching.kt");
|
||||
doExtractFunctionTest(fileName);
|
||||
}
|
||||
|
||||
@TestMetadata("singleExpression.kt")
|
||||
public void testSingleExpression() throws Exception {
|
||||
String fileName = JetTestUtils.navigationMetadata("idea/testData/refactoring/extractFunction/duplicates/singleExpression.kt");
|
||||
doExtractFunctionTest(fileName);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@TestMetadata("idea/testData/refactoring/extractFunction/initializers")
|
||||
@TestDataPath("$PROJECT_ROOT")
|
||||
@InnerTestClasses({Initializers.Accessors.class, Initializers.Classes.class, Initializers.Functions.class, Initializers.Properties.class})
|
||||
|
||||
Reference in New Issue
Block a user