Minor: Reformat extraction engine files
This commit is contained in:
+108
-105
@@ -66,25 +66,25 @@ interface Parameter {
|
||||
val Parameter.nameForRef: String get() = mirrorVarName ?: name
|
||||
|
||||
data class TypeParameter(
|
||||
val originalDeclaration: KtTypeParameter,
|
||||
val originalConstraints: List<KtTypeConstraint>
|
||||
val originalDeclaration: KtTypeParameter,
|
||||
val originalConstraints: List<KtTypeConstraint>
|
||||
)
|
||||
|
||||
interface Replacement: Function2<ExtractableCodeDescriptor, KtElement, KtElement>
|
||||
interface Replacement : Function2<ExtractableCodeDescriptor, KtElement, KtElement>
|
||||
|
||||
interface ParameterReplacement : Replacement {
|
||||
val parameter: Parameter
|
||||
fun copy(parameter: Parameter): ParameterReplacement
|
||||
}
|
||||
|
||||
class RenameReplacement(override val parameter: Parameter): ParameterReplacement {
|
||||
class RenameReplacement(override val parameter: Parameter) : ParameterReplacement {
|
||||
override fun copy(parameter: Parameter) = RenameReplacement(parameter)
|
||||
|
||||
override fun invoke(descriptor: ExtractableCodeDescriptor, e: KtElement): KtElement {
|
||||
var expressionToReplace = (e.parent as? KtThisExpression ?: e).let { it.getQualifiedExpressionForSelector() ?: it }
|
||||
val expressionToReplace = (e.parent as? KtThisExpression ?: e).let { it.getQualifiedExpressionForSelector() ?: it }
|
||||
val parameterName = KtPsiUtil.unquoteIdentifier(parameter.nameForRef)
|
||||
val replacingName =
|
||||
if (e.text.startsWith('`') || !parameterName.isIdentifier()) "`$parameterName`" else parameterName
|
||||
if (e.text.startsWith('`') || !parameterName.isIdentifier()) "`$parameterName`" else parameterName
|
||||
val psiFactory = KtPsiFactory(e)
|
||||
val replacement = when {
|
||||
parameter == descriptor.receiverParameter -> psiFactory.createExpression("this")
|
||||
@@ -106,19 +106,19 @@ abstract class WrapInWithReplacement : Replacement {
|
||||
}
|
||||
}
|
||||
|
||||
class WrapParameterInWithReplacement(override val parameter: Parameter): WrapInWithReplacement(), ParameterReplacement {
|
||||
class WrapParameterInWithReplacement(override val parameter: Parameter) : WrapInWithReplacement(), ParameterReplacement {
|
||||
override val argumentText: String
|
||||
get() = parameter.name
|
||||
|
||||
override fun copy(parameter: Parameter) = WrapParameterInWithReplacement(parameter)
|
||||
}
|
||||
|
||||
class WrapObjectInWithReplacement(val descriptor: ClassDescriptor): WrapInWithReplacement() {
|
||||
class WrapObjectInWithReplacement(val descriptor: ClassDescriptor) : WrapInWithReplacement() {
|
||||
override val argumentText: String
|
||||
get() = IdeDescriptorRenderers.SOURCE_CODE.renderClassifierName(descriptor)
|
||||
}
|
||||
|
||||
class AddPrefixReplacement(override val parameter: Parameter): ParameterReplacement {
|
||||
class AddPrefixReplacement(override val parameter: Parameter) : ParameterReplacement {
|
||||
override fun copy(parameter: Parameter) = AddPrefixReplacement(parameter)
|
||||
|
||||
override fun invoke(descriptor: ExtractableCodeDescriptor, e: KtElement): KtElement {
|
||||
@@ -131,7 +131,7 @@ class AddPrefixReplacement(override val parameter: Parameter): ParameterReplacem
|
||||
}
|
||||
}
|
||||
|
||||
class FqNameReplacement(val fqName: FqName): Replacement {
|
||||
class FqNameReplacement(val fqName: FqName) : Replacement {
|
||||
override fun invoke(descriptor: ExtractableCodeDescriptor, e: KtElement): KtElement {
|
||||
val thisExpr = e.parent as? KtThisExpression
|
||||
if (thisExpr != null) {
|
||||
@@ -148,32 +148,32 @@ interface OutputValue {
|
||||
val valueType: KotlinType
|
||||
|
||||
class ExpressionValue(
|
||||
val callSiteReturn: Boolean,
|
||||
override val originalExpressions: List<KtExpression>,
|
||||
override val valueType: KotlinType
|
||||
): OutputValue
|
||||
val callSiteReturn: Boolean,
|
||||
override val originalExpressions: List<KtExpression>,
|
||||
override val valueType: KotlinType
|
||||
) : OutputValue
|
||||
|
||||
class Jump(
|
||||
val elementsToReplace: List<KtExpression>,
|
||||
val elementToInsertAfterCall: KtElement?,
|
||||
val conditional: Boolean,
|
||||
builtIns: KotlinBuiltIns
|
||||
): OutputValue {
|
||||
val elementsToReplace: List<KtExpression>,
|
||||
val elementToInsertAfterCall: KtElement?,
|
||||
val conditional: Boolean,
|
||||
builtIns: KotlinBuiltIns
|
||||
) : OutputValue {
|
||||
override val originalExpressions: List<KtExpression> get() = elementsToReplace
|
||||
override val valueType: KotlinType = with(builtIns) { if (conditional) booleanType else unitType }
|
||||
}
|
||||
|
||||
class ParameterUpdate(
|
||||
val parameter: Parameter,
|
||||
override val originalExpressions: List<KtExpression>
|
||||
): OutputValue {
|
||||
val parameter: Parameter,
|
||||
override val originalExpressions: List<KtExpression>
|
||||
) : OutputValue {
|
||||
override val valueType: KotlinType get() = parameter.getParameterType(false)
|
||||
}
|
||||
|
||||
class Initializer(
|
||||
val initializedDeclaration: KtProperty,
|
||||
override val valueType: KotlinType
|
||||
): OutputValue {
|
||||
val initializedDeclaration: KtProperty,
|
||||
override val valueType: KotlinType
|
||||
) : OutputValue {
|
||||
override val originalExpressions: List<KtExpression> get() = Collections.singletonList(initializedDeclaration)
|
||||
}
|
||||
}
|
||||
@@ -216,8 +216,8 @@ abstract class OutputValueBoxer(val outputValues: List<OutputValue>) {
|
||||
abstract fun getUnboxingExpressions(boxedText: String): Map<OutputValue, String>
|
||||
|
||||
class AsTuple(
|
||||
outputValues: List<OutputValue>,
|
||||
val module: ModuleDescriptor
|
||||
outputValues: List<OutputValue>,
|
||||
val module: ModuleDescriptor
|
||||
) : OutputValueBoxer(outputValues) {
|
||||
init {
|
||||
assert(outputValues.size <= 3) { "At most 3 output values are supported" }
|
||||
@@ -271,17 +271,17 @@ abstract class OutputValueBoxer(val outputValues: List<OutputValue>) {
|
||||
}
|
||||
}
|
||||
|
||||
class AsList(outputValues: List<OutputValue>): OutputValueBoxer(outputValues) {
|
||||
class AsList(outputValues: List<OutputValue>) : OutputValueBoxer(outputValues) {
|
||||
override val returnType: KotlinType by lazy {
|
||||
assert(outputValues.isNotEmpty())
|
||||
val builtIns = outputValues.first().valueType.builtIns
|
||||
TypeUtils.substituteParameters(
|
||||
builtIns.list,
|
||||
Collections.singletonList(CommonSupertypes.commonSupertype(outputValues.map { it.valueType }))
|
||||
builtIns.list,
|
||||
Collections.singletonList(CommonSupertypes.commonSupertype(outputValues.map { it.valueType }))
|
||||
)
|
||||
}
|
||||
|
||||
override val boxingRequired: Boolean = outputValues.size > 0
|
||||
override val boxingRequired: Boolean = outputValues.isNotEmpty()
|
||||
|
||||
override fun getBoxingExpressionPattern(arguments: List<KtExpression>): String? {
|
||||
if (arguments.isEmpty()) return null
|
||||
@@ -300,9 +300,9 @@ abstract class OutputValueBoxer(val outputValues: List<OutputValue>) {
|
||||
}
|
||||
|
||||
data class ControlFlow(
|
||||
val outputValues: List<OutputValue>,
|
||||
val boxerFactory: (List<OutputValue>) -> OutputValueBoxer,
|
||||
val declarationsToCopy: List<KtDeclaration>
|
||||
val outputValues: List<OutputValue>,
|
||||
val boxerFactory: (List<OutputValue>) -> OutputValueBoxer,
|
||||
val declarationsToCopy: List<KtDeclaration>
|
||||
) {
|
||||
val outputValueBoxer = boxerFactory(outputValues)
|
||||
|
||||
@@ -337,7 +337,7 @@ val ControlFlow.possibleReturnTypes: List<KotlinType>
|
||||
}
|
||||
|
||||
fun ControlFlow.toDefault(): ControlFlow =
|
||||
copy(outputValues = outputValues.filterNot { it is Jump || it is ExpressionValue })
|
||||
copy(outputValues = outputValues.filterNot { it is Jump || it is ExpressionValue })
|
||||
|
||||
fun ControlFlow.copy(oldToNewParameters: Map<Parameter, Parameter>): ControlFlow {
|
||||
val newOutputValues = outputValues.map {
|
||||
@@ -347,28 +347,28 @@ fun ControlFlow.copy(oldToNewParameters: Map<Parameter, Parameter>): ControlFlow
|
||||
}
|
||||
|
||||
data class ExtractableCodeDescriptor(
|
||||
val extractionData: ExtractionData,
|
||||
val originalContext: BindingContext,
|
||||
val suggestedNames: List<String>,
|
||||
val visibility: KtModifierKeywordToken?,
|
||||
val parameters: List<Parameter>,
|
||||
val receiverParameter: Parameter?,
|
||||
val typeParameters: List<TypeParameter>,
|
||||
val replacementMap: MultiMap<KtSimpleNameExpression, Replacement>,
|
||||
val controlFlow: ControlFlow,
|
||||
val returnType: KotlinType,
|
||||
val modifiers: List<KtKeywordToken> = emptyList()
|
||||
val extractionData: ExtractionData,
|
||||
val originalContext: BindingContext,
|
||||
val suggestedNames: List<String>,
|
||||
val visibility: KtModifierKeywordToken?,
|
||||
val parameters: List<Parameter>,
|
||||
val receiverParameter: Parameter?,
|
||||
val typeParameters: List<TypeParameter>,
|
||||
val replacementMap: MultiMap<KtSimpleNameExpression, Replacement>,
|
||||
val controlFlow: ControlFlow,
|
||||
val returnType: KotlinType,
|
||||
val modifiers: List<KtKeywordToken> = emptyList()
|
||||
) {
|
||||
val name: String get() = suggestedNames.firstOrNull() ?: ""
|
||||
val duplicates: List<DuplicateInfo> by lazy { findDuplicates() }
|
||||
}
|
||||
|
||||
fun ExtractableCodeDescriptor.copy(
|
||||
newName: String,
|
||||
newVisibility: KtModifierKeywordToken?,
|
||||
oldToNewParameters: Map<Parameter, Parameter>,
|
||||
newReceiver: Parameter?,
|
||||
returnType: KotlinType?
|
||||
newName: String,
|
||||
newVisibility: KtModifierKeywordToken?,
|
||||
oldToNewParameters: Map<Parameter, Parameter>,
|
||||
newReceiver: Parameter?,
|
||||
returnType: KotlinType?
|
||||
): ExtractableCodeDescriptor {
|
||||
val newReplacementMap = MultiMap.create<KtSimpleNameExpression, Replacement>()
|
||||
for ((ref, replacements) in replacementMap.entrySet()) {
|
||||
@@ -377,24 +377,24 @@ fun ExtractableCodeDescriptor.copy(
|
||||
val parameter = it.parameter
|
||||
val newParameter = oldToNewParameters[parameter] ?: return@map it
|
||||
it.copy(newParameter)
|
||||
}
|
||||
else it
|
||||
} else it
|
||||
}
|
||||
newReplacementMap.putValues(ref, newReplacements)
|
||||
}
|
||||
|
||||
return ExtractableCodeDescriptor(
|
||||
extractionData,
|
||||
originalContext,
|
||||
listOf(newName),
|
||||
newVisibility,
|
||||
oldToNewParameters.values.filter { it != newReceiver },
|
||||
newReceiver,
|
||||
typeParameters,
|
||||
newReplacementMap,
|
||||
controlFlow.copy(oldToNewParameters),
|
||||
returnType ?: this.returnType,
|
||||
modifiers)
|
||||
extractionData,
|
||||
originalContext,
|
||||
listOf(newName),
|
||||
newVisibility,
|
||||
oldToNewParameters.values.filter { it != newReceiver },
|
||||
newReceiver,
|
||||
typeParameters,
|
||||
newReplacementMap,
|
||||
controlFlow.copy(oldToNewParameters),
|
||||
returnType ?: this.returnType,
|
||||
modifiers
|
||||
)
|
||||
}
|
||||
|
||||
enum class ExtractionTarget(val targetName: String) {
|
||||
@@ -411,10 +411,10 @@ enum class ExtractionTarget(val targetName: String) {
|
||||
PROPERTY_WITH_INITIALIZER("property with initializer") {
|
||||
override fun isAvailable(descriptor: ExtractableCodeDescriptor): Boolean {
|
||||
return checkSignatureAndParent(descriptor)
|
||||
&& checkSimpleControlFlow(descriptor)
|
||||
&& checkSimpleBody(descriptor)
|
||||
&& checkNotTrait(descriptor)
|
||||
&& descriptor.receiverParameter == null
|
||||
&& checkSimpleControlFlow(descriptor)
|
||||
&& checkSimpleBody(descriptor)
|
||||
&& checkNotTrait(descriptor)
|
||||
&& descriptor.receiverParameter == null
|
||||
}
|
||||
},
|
||||
|
||||
@@ -427,9 +427,9 @@ enum class ExtractionTarget(val targetName: String) {
|
||||
LAZY_PROPERTY("lazy property") {
|
||||
override fun isAvailable(descriptor: ExtractableCodeDescriptor): Boolean {
|
||||
return checkSignatureAndParent(descriptor)
|
||||
&& checkSimpleControlFlow(descriptor)
|
||||
&& checkNotTrait(descriptor)
|
||||
&& descriptor.receiverParameter == null
|
||||
&& checkSimpleControlFlow(descriptor)
|
||||
&& checkNotTrait(descriptor)
|
||||
&& descriptor.receiverParameter == null
|
||||
}
|
||||
};
|
||||
|
||||
@@ -461,39 +461,42 @@ enum class ExtractionTarget(val targetName: String) {
|
||||
}
|
||||
}
|
||||
|
||||
val propertyTargets: List<ExtractionTarget> = listOf(ExtractionTarget.PROPERTY_WITH_INITIALIZER,
|
||||
ExtractionTarget.PROPERTY_WITH_GETTER,
|
||||
ExtractionTarget.LAZY_PROPERTY)
|
||||
val propertyTargets: List<ExtractionTarget> = listOf(
|
||||
ExtractionTarget.PROPERTY_WITH_INITIALIZER,
|
||||
ExtractionTarget.PROPERTY_WITH_GETTER,
|
||||
ExtractionTarget.LAZY_PROPERTY
|
||||
)
|
||||
|
||||
data class ExtractionGeneratorOptions(
|
||||
val inTempFile: Boolean = false,
|
||||
val target: ExtractionTarget = ExtractionTarget.FUNCTION,
|
||||
val dummyName: String? = null,
|
||||
val allowExpressionBody: Boolean = true,
|
||||
val delayInitialOccurrenceReplacement: Boolean = false
|
||||
val inTempFile: Boolean = false,
|
||||
val target: ExtractionTarget = ExtractionTarget.FUNCTION,
|
||||
val dummyName: String? = null,
|
||||
val allowExpressionBody: Boolean = true,
|
||||
val delayInitialOccurrenceReplacement: Boolean = false
|
||||
) {
|
||||
companion object {
|
||||
@JvmField val DEFAULT = ExtractionGeneratorOptions()
|
||||
@JvmField
|
||||
val DEFAULT = ExtractionGeneratorOptions()
|
||||
}
|
||||
}
|
||||
|
||||
data class ExtractionGeneratorConfiguration(
|
||||
val descriptor: ExtractableCodeDescriptor,
|
||||
val generatorOptions: ExtractionGeneratorOptions
|
||||
val descriptor: ExtractableCodeDescriptor,
|
||||
val generatorOptions: ExtractionGeneratorOptions
|
||||
)
|
||||
|
||||
data class ExtractionResult(
|
||||
val config: ExtractionGeneratorConfiguration,
|
||||
val declaration: KtNamedDeclaration,
|
||||
val duplicateReplacers: Map<KotlinPsiRange, () -> Unit>
|
||||
val config: ExtractionGeneratorConfiguration,
|
||||
val declaration: KtNamedDeclaration,
|
||||
val duplicateReplacers: Map<KotlinPsiRange, () -> Unit>
|
||||
) : Disposable {
|
||||
override fun dispose() = unmarkReferencesInside(declaration)
|
||||
}
|
||||
|
||||
class AnalysisResult (
|
||||
val descriptor: ExtractableCodeDescriptor?,
|
||||
val status: Status,
|
||||
val messages: List<ErrorMessage>
|
||||
class AnalysisResult(
|
||||
val descriptor: ExtractableCodeDescriptor?,
|
||||
val status: Status,
|
||||
val messages: List<ErrorMessage>
|
||||
) {
|
||||
enum class Status {
|
||||
SUCCESS,
|
||||
@@ -523,27 +526,27 @@ class AnalysisResult (
|
||||
|
||||
fun renderMessage(): String {
|
||||
val message = KotlinRefactoringBundle.message(
|
||||
when (this) {
|
||||
NO_EXPRESSION -> "cannot.refactor.no.expression"
|
||||
NO_CONTAINER -> "cannot.refactor.no.container"
|
||||
SYNTAX_ERRORS -> "cannot.refactor.syntax.errors"
|
||||
SUPER_CALL -> "cannot.extract.super.call"
|
||||
DENOTABLE_TYPES -> "parameter.types.are.not.denotable"
|
||||
ERROR_TYPES -> "error.types.in.generated.function"
|
||||
MULTIPLE_OUTPUT -> "selected.code.fragment.has.multiple.output.values"
|
||||
OUTPUT_AND_EXIT_POINT -> "selected.code.fragment.has.output.values.and.exit.points"
|
||||
MULTIPLE_EXIT_POINTS -> "selected.code.fragment.has.multiple.exit.points"
|
||||
DECLARATIONS_ARE_USED_OUTSIDE -> "declarations.are.used.outside.of.selected.code.fragment"
|
||||
DECLARATIONS_OUT_OF_SCOPE -> "declarations.will.move.out.of.scope"
|
||||
}
|
||||
when (this) {
|
||||
NO_EXPRESSION -> "cannot.refactor.no.expression"
|
||||
NO_CONTAINER -> "cannot.refactor.no.container"
|
||||
SYNTAX_ERRORS -> "cannot.refactor.syntax.errors"
|
||||
SUPER_CALL -> "cannot.extract.super.call"
|
||||
DENOTABLE_TYPES -> "parameter.types.are.not.denotable"
|
||||
ERROR_TYPES -> "error.types.in.generated.function"
|
||||
MULTIPLE_OUTPUT -> "selected.code.fragment.has.multiple.output.values"
|
||||
OUTPUT_AND_EXIT_POINT -> "selected.code.fragment.has.output.values.and.exit.points"
|
||||
MULTIPLE_EXIT_POINTS -> "selected.code.fragment.has.multiple.exit.points"
|
||||
DECLARATIONS_ARE_USED_OUTSIDE -> "declarations.are.used.outside.of.selected.code.fragment"
|
||||
DECLARATIONS_OUT_OF_SCOPE -> "declarations.will.move.out.of.scope"
|
||||
}
|
||||
)
|
||||
|
||||
return additionalInfo?.let { "$message\n\n${it.joinToString("\n") { StringUtil.htmlEmphasize(it) }}" } ?: message
|
||||
return additionalInfo?.let { "$message\n\n${it.joinToString("\n") { msg -> StringUtil.htmlEmphasize(msg) }}" } ?: message
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class ExtractableCodeDescriptorWithConflicts(
|
||||
val descriptor: ExtractableCodeDescriptor,
|
||||
val conflicts: MultiMap<PsiElement, String>
|
||||
val descriptor: ExtractableCodeDescriptor,
|
||||
val conflicts: MultiMap<PsiElement, String>
|
||||
)
|
||||
|
||||
+75
-44
@@ -55,12 +55,12 @@ import org.jetbrains.kotlin.types.KotlinType
|
||||
import java.util.*
|
||||
|
||||
data class ExtractionOptions(
|
||||
val inferUnitTypeForUnusedValues: Boolean = true,
|
||||
val enableListBoxing: Boolean = false,
|
||||
val extractAsProperty: Boolean = false,
|
||||
val allowSpecialClassNames: Boolean = false,
|
||||
val captureLocalFunctions: Boolean = false,
|
||||
val canWrapInWith: Boolean = false
|
||||
val inferUnitTypeForUnusedValues: Boolean = true,
|
||||
val enableListBoxing: Boolean = false,
|
||||
val extractAsProperty: Boolean = false,
|
||||
val allowSpecialClassNames: Boolean = false,
|
||||
val captureLocalFunctions: Boolean = false,
|
||||
val canWrapInWith: Boolean = false
|
||||
) {
|
||||
companion object {
|
||||
val DEFAULT = ExtractionOptions()
|
||||
@@ -68,28 +68,28 @@ data class ExtractionOptions(
|
||||
}
|
||||
|
||||
data class ResolveResult(
|
||||
val originalRefExpr: KtSimpleNameExpression,
|
||||
val declaration: PsiElement,
|
||||
val descriptor: DeclarationDescriptor,
|
||||
val resolvedCall: ResolvedCall<*>?
|
||||
val originalRefExpr: KtSimpleNameExpression,
|
||||
val declaration: PsiElement,
|
||||
val descriptor: DeclarationDescriptor,
|
||||
val resolvedCall: ResolvedCall<*>?
|
||||
)
|
||||
|
||||
data class ResolvedReferenceInfo(
|
||||
val refExpr: KtSimpleNameExpression,
|
||||
val resolveResult: ResolveResult,
|
||||
val smartCast: KotlinType?,
|
||||
val possibleTypes: Set<KotlinType>,
|
||||
val shouldSkipPrimaryReceiver: Boolean
|
||||
val refExpr: KtSimpleNameExpression,
|
||||
val resolveResult: ResolveResult,
|
||||
val smartCast: KotlinType?,
|
||||
val possibleTypes: Set<KotlinType>,
|
||||
val shouldSkipPrimaryReceiver: Boolean
|
||||
)
|
||||
|
||||
internal var KtSimpleNameExpression.resolveResult: ResolveResult? by CopyablePsiUserDataProperty(Key.create("RESOLVE_RESULT"))
|
||||
|
||||
data class ExtractionData(
|
||||
val originalFile: KtFile,
|
||||
val originalRange: KotlinPsiRange,
|
||||
val targetSibling: PsiElement,
|
||||
val duplicateContainer: PsiElement? = null,
|
||||
val options: ExtractionOptions = ExtractionOptions.DEFAULT
|
||||
val originalFile: KtFile,
|
||||
val originalRange: KotlinPsiRange,
|
||||
val targetSibling: PsiElement,
|
||||
val duplicateContainer: PsiElement? = null,
|
||||
val options: ExtractionOptions = ExtractionOptions.DEFAULT
|
||||
) : Disposable {
|
||||
val project: Project = originalFile.project
|
||||
val originalElements: List<PsiElement> = originalRange.elements
|
||||
@@ -99,9 +99,9 @@ data class ExtractionData(
|
||||
get() = (originalElements.singleOrNull() as? KtExpression)?.extractableSubstringInfo
|
||||
|
||||
val insertBefore: Boolean = options.extractAsProperty
|
||||
|| targetSibling.getStrictParentOfType<KtDeclaration>()?.let {
|
||||
it is KtDeclarationWithBody || it is KtAnonymousInitializer
|
||||
} ?: false
|
||||
|| targetSibling.getStrictParentOfType<KtDeclaration>()?.let {
|
||||
it is KtDeclarationWithBody || it is KtAnonymousInitializer
|
||||
} ?: false
|
||||
|
||||
val expressions = originalElements.filterIsInstance<KtExpression>()
|
||||
|
||||
@@ -126,7 +126,7 @@ data class ExtractionData(
|
||||
}
|
||||
|
||||
private fun isExtractableIt(descriptor: DeclarationDescriptor, context: BindingContext): Boolean {
|
||||
if (!(descriptor is ValueParameterDescriptor && (context[BindingContext.AUTO_CREATED_IT, descriptor] ?: false))) return false
|
||||
if (!(descriptor is ValueParameterDescriptor && (context[BindingContext.AUTO_CREATED_IT, descriptor] == true))) return false
|
||||
val function = DescriptorToSourceUtils.descriptorToDeclaration(descriptor.containingDeclaration) as? KtFunctionLiteral
|
||||
return function == null || !function.isInsideOf(physicalElements)
|
||||
}
|
||||
@@ -183,12 +183,15 @@ data class ExtractionData(
|
||||
expressions.forEach { it.accept(visitor) }
|
||||
}
|
||||
|
||||
fun getPossibleTypes(expression: KtExpression, resolvedCall: ResolvedCall<*>?, context: BindingContext): Set<KotlinType> {
|
||||
private fun getPossibleTypes(expression: KtExpression, resolvedCall: ResolvedCall<*>?, context: BindingContext): Set<KotlinType> {
|
||||
val dataFlowValueFactory = expression.getResolutionFacade().frontendService<DataFlowValueFactory>()
|
||||
val dataFlowInfo = context.getDataFlowInfoAfter(expression)
|
||||
|
||||
resolvedCall?.getImplicitReceiverValue()?.let {
|
||||
return dataFlowInfo.getCollectedTypes(dataFlowValueFactory.createDataFlowValueForStableReceiver(it), expression.languageVersionSettings)
|
||||
return dataFlowInfo.getCollectedTypes(
|
||||
dataFlowValueFactory.createDataFlowValueForStableReceiver(it),
|
||||
expression.languageVersionSettings
|
||||
)
|
||||
}
|
||||
|
||||
val type = resolvedCall?.resultingDescriptor?.returnType ?: return emptySet()
|
||||
@@ -219,13 +222,12 @@ data class ExtractionData(
|
||||
smartCast = originalContext[BindingContext.SMARTCAST, smartCastTarget]?.defaultType
|
||||
possibleTypes = getPossibleTypes(smartCastTarget, originalResolveResult.resolvedCall, originalContext)
|
||||
val receiverDescriptor =
|
||||
(originalResolveResult.resolvedCall?.dispatchReceiver as? ImplicitReceiver)?.declarationDescriptor
|
||||
(originalResolveResult.resolvedCall?.dispatchReceiver as? ImplicitReceiver)?.declarationDescriptor
|
||||
shouldSkipPrimaryReceiver = smartCast == null
|
||||
&& !DescriptorUtils.isCompanionObject(receiverDescriptor)
|
||||
&& qualifiedExpression.receiverExpression !is KtSuperExpression
|
||||
if (shouldSkipPrimaryReceiver && !(originalResolveResult.resolvedCall?.hasBothReceivers() ?: false)) continue
|
||||
}
|
||||
else {
|
||||
&& !DescriptorUtils.isCompanionObject(receiverDescriptor)
|
||||
&& qualifiedExpression.receiverExpression !is KtSuperExpression
|
||||
if (shouldSkipPrimaryReceiver && originalResolveResult.resolvedCall?.hasBothReceivers() != true) continue
|
||||
} else {
|
||||
if (newRef.getParentOfTypeAndBranch<KtCallableReferenceExpression> { callableReference } != null) continue
|
||||
smartCast = originalContext[BindingContext.SMARTCAST, originalResolveResult.originalRefExpr]?.defaultType
|
||||
possibleTypes = getPossibleTypes(originalResolveResult.originalRefExpr, originalResolveResult.resolvedCall, originalContext)
|
||||
@@ -239,8 +241,10 @@ data class ExtractionData(
|
||||
|
||||
val descriptor = context[BindingContext.REFERENCE_TARGET, newRef]
|
||||
val isBadRef = !(compareDescriptors(project, originalResolveResult.descriptor, descriptor)
|
||||
&& originalContext.diagnostics.forElement(originalResolveResult.originalRefExpr) == context.diagnostics.forElement(newRef))
|
||||
|| smartCast != null
|
||||
&& originalContext.diagnostics.forElement(originalResolveResult.originalRefExpr) == context.diagnostics.forElement(
|
||||
newRef
|
||||
))
|
||||
|| smartCast != null
|
||||
if (isBadRef && !originalResolveResult.declaration.isInsideOf(physicalElements)) {
|
||||
val originalResolvedCall = originalResolveResult.resolvedCall as? VariableAsFunctionResolvedCall
|
||||
val originalFunctionCall = originalResolvedCall?.functionCall
|
||||
@@ -248,16 +252,43 @@ data class ExtractionData(
|
||||
val invokeDescriptor = originalFunctionCall?.resultingDescriptor
|
||||
if (invokeDescriptor != null) {
|
||||
val invokeDeclaration = getDeclaration(invokeDescriptor, context) ?: synthesizedInvokeDeclaration
|
||||
val variableResolveResult = originalResolveResult.copy(resolvedCall = originalVariableCall!!,
|
||||
descriptor = originalVariableCall.resultingDescriptor)
|
||||
val functionResolveResult = originalResolveResult.copy(resolvedCall = originalFunctionCall,
|
||||
descriptor = originalFunctionCall.resultingDescriptor,
|
||||
declaration = invokeDeclaration)
|
||||
referencesInfo.add(ResolvedReferenceInfo(newRef, variableResolveResult, smartCast, possibleTypes, shouldSkipPrimaryReceiver))
|
||||
referencesInfo.add(ResolvedReferenceInfo(newRef, functionResolveResult, smartCast, possibleTypes, shouldSkipPrimaryReceiver))
|
||||
}
|
||||
else {
|
||||
referencesInfo.add(ResolvedReferenceInfo(newRef, originalResolveResult, smartCast, possibleTypes, shouldSkipPrimaryReceiver))
|
||||
val variableResolveResult = originalResolveResult.copy(
|
||||
resolvedCall = originalVariableCall!!,
|
||||
descriptor = originalVariableCall.resultingDescriptor
|
||||
)
|
||||
val functionResolveResult = originalResolveResult.copy(
|
||||
resolvedCall = originalFunctionCall,
|
||||
descriptor = originalFunctionCall.resultingDescriptor,
|
||||
declaration = invokeDeclaration
|
||||
)
|
||||
referencesInfo.add(
|
||||
ResolvedReferenceInfo(
|
||||
newRef,
|
||||
variableResolveResult,
|
||||
smartCast,
|
||||
possibleTypes,
|
||||
shouldSkipPrimaryReceiver
|
||||
)
|
||||
)
|
||||
referencesInfo.add(
|
||||
ResolvedReferenceInfo(
|
||||
newRef,
|
||||
functionResolveResult,
|
||||
smartCast,
|
||||
possibleTypes,
|
||||
shouldSkipPrimaryReceiver
|
||||
)
|
||||
)
|
||||
} else {
|
||||
referencesInfo.add(
|
||||
ResolvedReferenceInfo(
|
||||
newRef,
|
||||
originalResolveResult,
|
||||
smartCast,
|
||||
possibleTypes,
|
||||
shouldSkipPrimaryReceiver
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+25
-26
@@ -40,19 +40,20 @@ abstract class ExtractionEngineHelper(val operationName: String) {
|
||||
open fun validate(descriptor: ExtractableCodeDescriptor): ExtractableCodeDescriptorWithConflicts = descriptor.validate()
|
||||
|
||||
abstract fun configureAndRun(
|
||||
project: Project,
|
||||
editor: Editor,
|
||||
descriptorWithConflicts: ExtractableCodeDescriptorWithConflicts,
|
||||
onFinish: (ExtractionResult) -> Unit = {}
|
||||
project: Project,
|
||||
editor: Editor,
|
||||
descriptorWithConflicts: ExtractableCodeDescriptorWithConflicts,
|
||||
onFinish: (ExtractionResult) -> Unit = {}
|
||||
)
|
||||
}
|
||||
|
||||
class ExtractionEngine(
|
||||
val helper: ExtractionEngineHelper
|
||||
val helper: ExtractionEngineHelper
|
||||
) {
|
||||
fun run(editor: Editor,
|
||||
extractionData: ExtractionData,
|
||||
onFinish: (ExtractionResult) -> Unit = {}
|
||||
fun run(
|
||||
editor: Editor,
|
||||
extractionData: ExtractionData,
|
||||
onFinish: (ExtractionResult) -> Unit = {}
|
||||
) {
|
||||
val project = extractionData.project
|
||||
|
||||
@@ -68,8 +69,7 @@ class ExtractionEngine(
|
||||
helper.configureAndRun(project, editor, validationResult) {
|
||||
try {
|
||||
onFinish(it)
|
||||
}
|
||||
finally {
|
||||
} finally {
|
||||
it.dispose()
|
||||
extractionData.dispose()
|
||||
}
|
||||
@@ -85,24 +85,23 @@ class ExtractionEngine(
|
||||
|
||||
AnalysisResult.Status.NON_CRITICAL_ERROR -> {
|
||||
val anchorPoint = RelativePoint(
|
||||
editor.contentComponent,
|
||||
editor.visualPositionToXY(editor.selectionModel.selectionStartPosition!!)
|
||||
editor.contentComponent,
|
||||
editor.visualPositionToXY(editor.selectionModel.selectionStartPosition!!)
|
||||
)
|
||||
JBPopupFactory.getInstance()!!
|
||||
.createHtmlTextBalloonBuilder(
|
||||
"$message<br/><br/><a href=\"EXTRACT\">Proceed with extraction</a>",
|
||||
MessageType.WARNING,
|
||||
{ event ->
|
||||
if (event?.eventType == HyperlinkEvent.EventType.ACTIVATED) {
|
||||
validateAndRefactor()
|
||||
}
|
||||
}
|
||||
)
|
||||
.setHideOnClickOutside(true)
|
||||
.setHideOnFrameResize(false)
|
||||
.setHideOnLinkClick(true)
|
||||
.createBalloon()
|
||||
.show(anchorPoint, Balloon.Position.below)
|
||||
.createHtmlTextBalloonBuilder(
|
||||
"$message<br/><br/><a href=\"EXTRACT\">Proceed with extraction</a>",
|
||||
MessageType.WARNING
|
||||
) { event ->
|
||||
if (event?.eventType == HyperlinkEvent.EventType.ACTIVATED) {
|
||||
validateAndRefactor()
|
||||
}
|
||||
}
|
||||
.setHideOnClickOutside(true)
|
||||
.setHideOnFrameResize(false)
|
||||
.setHideOnLinkClick(true)
|
||||
.createBalloon()
|
||||
.show(anchorPoint, Balloon.Position.below)
|
||||
}
|
||||
|
||||
AnalysisResult.Status.SUCCESS -> validateAndRefactor()
|
||||
|
||||
+38
-32
@@ -42,33 +42,35 @@ fun KotlinPsiRange.highlight(project: Project, editor: Editor): RangeHighlighter
|
||||
val highlighters = ArrayList<RangeHighlighter>()
|
||||
val attributes = EditorColorsManager.getInstance().globalScheme.getAttributes(EditorColors.SEARCH_RESULT_ATTRIBUTES)!!
|
||||
HighlightManager.getInstance(project).addRangeHighlight(
|
||||
editor, textRange.startOffset, textRange.endOffset, attributes, true, highlighters
|
||||
editor, textRange.startOffset, textRange.endOffset, attributes, true, highlighters
|
||||
)
|
||||
return highlighters.firstOrNull()
|
||||
}
|
||||
|
||||
fun KotlinPsiRange.preview(project: Project, editor: Editor): RangeHighlighter? {
|
||||
return highlight(project, editor)?.let {
|
||||
val startOffset = getPhysicalTextRange().startOffset
|
||||
val foldedRegions =
|
||||
CodeFoldingManager.getInstance(project)
|
||||
.getFoldRegionsAtOffset(editor, startOffset)
|
||||
.filter { !it.isExpanded }
|
||||
if (!foldedRegions.isEmpty()) {
|
||||
editor.foldingModel.runBatchFoldingOperation { foldedRegions.forEach { it.isExpanded = true } }
|
||||
}
|
||||
editor.scrollingModel.scrollTo(editor.offsetToLogicalPosition(startOffset), ScrollType.MAKE_VISIBLE)
|
||||
val highlight = highlight(project, editor) ?: return null
|
||||
|
||||
it
|
||||
val startOffset = getPhysicalTextRange().startOffset
|
||||
val foldedRegions = CodeFoldingManager.getInstance(project)
|
||||
.getFoldRegionsAtOffset(editor, startOffset)
|
||||
.filter { !it.isExpanded }
|
||||
|
||||
if (!foldedRegions.isEmpty()) {
|
||||
editor.foldingModel.runBatchFoldingOperation {
|
||||
foldedRegions.forEach { it.isExpanded = true }
|
||||
}
|
||||
}
|
||||
|
||||
editor.scrollingModel.scrollTo(editor.offsetToLogicalPosition(startOffset), ScrollType.MAKE_VISIBLE)
|
||||
return highlight
|
||||
}
|
||||
|
||||
fun processDuplicates(
|
||||
duplicateReplacers: Map<KotlinPsiRange, () -> Unit>,
|
||||
project: Project,
|
||||
editor: Editor,
|
||||
scopeDescription: String = "this file",
|
||||
usageDescription: String = "a usage of extracted declaration"
|
||||
duplicateReplacers: Map<KotlinPsiRange, () -> Unit>,
|
||||
project: Project,
|
||||
editor: Editor,
|
||||
scopeDescription: String = "this file",
|
||||
usageDescription: String = "a usage of extracted declaration"
|
||||
) {
|
||||
val size = duplicateReplacers.size
|
||||
if (size == 0) return
|
||||
@@ -77,22 +79,26 @@ fun processDuplicates(
|
||||
duplicateReplacers.keys.first().preview(project, editor)
|
||||
}
|
||||
|
||||
val answer = if (ApplicationManager.getApplication()!!.isUnitTestMode)
|
||||
val answer = if (ApplicationManager.getApplication()!!.isUnitTestMode) {
|
||||
Messages.YES
|
||||
else
|
||||
} else {
|
||||
Messages.showYesNoDialog(
|
||||
project,
|
||||
KotlinRefactoringBundle.message(
|
||||
"0.has.detected.1.code.fragments.in.2.that.can.be.replaced.with.3",
|
||||
ApplicationNamesInfo.getInstance().productName,
|
||||
duplicateReplacers.size,
|
||||
scopeDescription,
|
||||
usageDescription
|
||||
),
|
||||
"Process Duplicates",
|
||||
Messages.getQuestionIcon()
|
||||
project,
|
||||
KotlinRefactoringBundle.message(
|
||||
"0.has.detected.1.code.fragments.in.2.that.can.be.replaced.with.3",
|
||||
ApplicationNamesInfo.getInstance().productName,
|
||||
duplicateReplacers.size,
|
||||
scopeDescription,
|
||||
usageDescription
|
||||
),
|
||||
"Process Duplicates",
|
||||
Messages.getQuestionIcon()
|
||||
)
|
||||
if (answer != Messages.YES) return
|
||||
}
|
||||
|
||||
if (answer != Messages.YES) {
|
||||
return
|
||||
}
|
||||
|
||||
var showAll = false
|
||||
|
||||
@@ -106,15 +112,15 @@ fun processDuplicates(
|
||||
if (size > 1 && !showAll) {
|
||||
val promptDialog = ReplacePromptDialog(false, RefactoringBundle.message("process.duplicates.title", i + 1, size), project)
|
||||
promptDialog.show()
|
||||
when(promptDialog.exitCode) {
|
||||
when (promptDialog.exitCode) {
|
||||
FindManager.PromptResult.ALL -> showAll = true
|
||||
FindManager.PromptResult.SKIP -> continue@duplicateReplacersLoop
|
||||
FindManager.PromptResult.CANCEL -> return
|
||||
}
|
||||
}
|
||||
}
|
||||
highlighter?.let { HighlightManager.getInstance(project).removeSegmentHighlighter(editor, it) }
|
||||
|
||||
highlighter?.let { HighlightManager.getInstance(project).removeSegmentHighlighter(editor, it) }
|
||||
project.executeWriteCommand(MethodDuplicatesHandler.REFACTORING_NAME, replacer)
|
||||
}
|
||||
}
|
||||
|
||||
+198
-182
@@ -75,8 +75,9 @@ import java.util.*
|
||||
internal val KotlinBuiltIns.defaultReturnType: KotlinType get() = unitType
|
||||
internal val KotlinBuiltIns.defaultParameterType: KotlinType get() = nullableAnyType
|
||||
|
||||
private fun DeclarationDescriptor.renderForMessage(): String =
|
||||
IdeDescriptorRenderers.SOURCE_CODE_SHORT_NAMES_NO_ANNOTATIONS.render(this)
|
||||
private fun DeclarationDescriptor.renderForMessage(): String {
|
||||
return IdeDescriptorRenderers.SOURCE_CODE_SHORT_NAMES_NO_ANNOTATIONS.render(this)
|
||||
}
|
||||
|
||||
private val TYPE_RENDERER = DescriptorRenderer.FQ_NAMES_IN_TYPES.withOptions {
|
||||
typeNormalizer = IdeDescriptorRenderers.APPROXIMATE_FLEXIBLE_TYPES
|
||||
@@ -87,8 +88,6 @@ private fun KotlinType.renderForMessage(): String = TYPE_RENDERER.renderType(thi
|
||||
private fun KtDeclaration.renderForMessage(bindingContext: BindingContext): String? =
|
||||
bindingContext[BindingContext.DECLARATION_TO_DESCRIPTOR, this]?.renderForMessage()
|
||||
|
||||
internal fun KotlinType.isDefault(): Boolean = KotlinBuiltIns.isUnit(this)
|
||||
|
||||
private fun List<Instruction>.getModifiedVarDescriptors(bindingContext: BindingContext): Map<VariableDescriptor, List<KtExpression>> {
|
||||
val result = HashMap<VariableDescriptor, MutableList<KtExpression>>()
|
||||
for (instruction in filterIsInstance<WriteValueInstruction>()) {
|
||||
@@ -125,13 +124,13 @@ private fun List<Instruction>.getVarDescriptorsAccessedAfterwards(bindingContext
|
||||
}
|
||||
|
||||
private fun List<Instruction>.getExitPoints(): List<Instruction> =
|
||||
filter { localInstruction -> localInstruction.nextInstructions.any { it !in this } }
|
||||
filter { localInstruction -> localInstruction.nextInstructions.any { it !in this } }
|
||||
|
||||
private fun ExtractionData.getResultTypeAndExpressions(
|
||||
instructions: List<Instruction>,
|
||||
bindingContext: BindingContext,
|
||||
targetScope: LexicalScope?,
|
||||
options: ExtractionOptions, module: ModuleDescriptor
|
||||
instructions: List<Instruction>,
|
||||
bindingContext: BindingContext,
|
||||
targetScope: LexicalScope?,
|
||||
options: ExtractionOptions, module: ModuleDescriptor
|
||||
): Pair<KotlinType, List<KtExpression>> {
|
||||
fun instructionToExpression(instruction: Instruction, unwrapReturn: Boolean): KtExpression? {
|
||||
return when (instruction) {
|
||||
@@ -153,14 +152,15 @@ private fun ExtractionData.getResultTypeAndExpressions(
|
||||
if (options.inferUnitTypeForUnusedValues && expression.isUsedAsStatement(bindingContext)) return null
|
||||
|
||||
return bindingContext.getType(expression)
|
||||
?: (expression as? KtReferenceExpression)?.let {
|
||||
(bindingContext[BindingContext.REFERENCE_TARGET, it] as? CallableDescriptor)?.returnType
|
||||
}
|
||||
?: (expression as? KtReferenceExpression)?.let {
|
||||
(bindingContext[BindingContext.REFERENCE_TARGET, it] as? CallableDescriptor)?.returnType
|
||||
}
|
||||
}
|
||||
|
||||
val resultTypes = instructions.mapNotNull(::instructionToType)
|
||||
val commonSupertype = if (resultTypes.isNotEmpty()) CommonSupertypes.commonSupertype(resultTypes) else module.builtIns.defaultReturnType
|
||||
val resultType = if (options.allowSpecialClassNames) commonSupertype else commonSupertype.approximateWithResolvableType(targetScope, false)
|
||||
val resultType =
|
||||
if (options.allowSpecialClassNames) commonSupertype else commonSupertype.approximateWithResolvableType(targetScope, false)
|
||||
|
||||
val expressions = instructions.mapNotNull { instructionToExpression(it, false) }
|
||||
|
||||
@@ -168,7 +168,7 @@ private fun ExtractionData.getResultTypeAndExpressions(
|
||||
}
|
||||
|
||||
private fun getCommonNonTrivialSuccessorIfAny(instructions: List<Instruction>): Instruction? {
|
||||
val singleSuccessorCheckingVisitor = object: InstructionVisitorWithResult<Boolean>() {
|
||||
val singleSuccessorCheckingVisitor = object : InstructionVisitorWithResult<Boolean>() {
|
||||
var target: Instruction? = null
|
||||
|
||||
override fun visitInstructionWithNext(instruction: InstructionWithNext): Boolean {
|
||||
@@ -205,9 +205,9 @@ private fun KotlinType.isMeaningful(): Boolean {
|
||||
}
|
||||
|
||||
private fun ExtractionData.getLocalDeclarationsWithNonLocalUsages(
|
||||
pseudocode: Pseudocode,
|
||||
localInstructions: List<Instruction>,
|
||||
bindingContext: BindingContext
|
||||
pseudocode: Pseudocode,
|
||||
localInstructions: List<Instruction>,
|
||||
bindingContext: BindingContext
|
||||
): List<KtNamedDeclaration> {
|
||||
val declarations = HashSet<KtNamedDeclaration>()
|
||||
pseudocode.traverse(TraversalOrder.FORWARD) { instruction ->
|
||||
@@ -224,14 +224,14 @@ private fun ExtractionData.getLocalDeclarationsWithNonLocalUsages(
|
||||
}
|
||||
|
||||
private fun ExtractionData.analyzeControlFlow(
|
||||
localInstructions: List<Instruction>,
|
||||
pseudocode: Pseudocode,
|
||||
module: ModuleDescriptor,
|
||||
bindingContext: BindingContext,
|
||||
modifiedVarDescriptors: Map<VariableDescriptor, List<KtExpression>>,
|
||||
options: ExtractionOptions,
|
||||
targetScope: LexicalScope?,
|
||||
parameters: Set<Parameter>
|
||||
localInstructions: List<Instruction>,
|
||||
pseudocode: Pseudocode,
|
||||
module: ModuleDescriptor,
|
||||
bindingContext: BindingContext,
|
||||
modifiedVarDescriptors: Map<VariableDescriptor, List<KtExpression>>,
|
||||
options: ExtractionOptions,
|
||||
targetScope: LexicalScope?,
|
||||
parameters: Set<Parameter>
|
||||
): Pair<ControlFlow, ErrorMessage?> {
|
||||
val exitPoints = localInstructions.getExitPoints()
|
||||
|
||||
@@ -241,24 +241,23 @@ private fun ExtractionData.analyzeControlFlow(
|
||||
exitPoints.forEach {
|
||||
val e = (it as? UnconditionalJumpInstruction)?.element
|
||||
val inst =
|
||||
when {
|
||||
it !is ReturnValueInstruction && it !is ReturnNoValueInstruction && it.owner != pseudocode ->
|
||||
null
|
||||
it is UnconditionalJumpInstruction && it.targetLabel.isJumpToError ->
|
||||
it
|
||||
e != null && e !is KtBreakExpression && e !is KtContinueExpression ->
|
||||
it.previousInstructions.firstOrNull()
|
||||
else ->
|
||||
it
|
||||
}
|
||||
when {
|
||||
it !is ReturnValueInstruction && it !is ReturnNoValueInstruction && it.owner != pseudocode ->
|
||||
null
|
||||
it is UnconditionalJumpInstruction && it.targetLabel.isJumpToError ->
|
||||
it
|
||||
e != null && e !is KtBreakExpression && e !is KtContinueExpression ->
|
||||
it.previousInstructions.firstOrNull()
|
||||
else ->
|
||||
it
|
||||
}
|
||||
|
||||
when (inst) {
|
||||
is ReturnValueInstruction -> {
|
||||
if (inst.owner == pseudocode) {
|
||||
if (inst.returnExpressionIfAny == null) {
|
||||
defaultExits.add(inst)
|
||||
}
|
||||
else {
|
||||
} else {
|
||||
valuedReturnExits.add(inst)
|
||||
}
|
||||
}
|
||||
@@ -267,11 +266,11 @@ private fun ExtractionData.analyzeControlFlow(
|
||||
is AbstractJumpInstruction -> {
|
||||
val element = inst.element
|
||||
if ((element is KtReturnExpression && inst.owner == pseudocode)
|
||||
|| element is KtBreakExpression
|
||||
|| element is KtContinueExpression) {
|
||||
|| element is KtBreakExpression
|
||||
|| element is KtContinueExpression
|
||||
) {
|
||||
jumpExits.add(inst)
|
||||
}
|
||||
else if (element !is KtThrowExpression && !inst.targetLabel.isJumpToError) {
|
||||
} else if (element !is KtThrowExpression && !inst.targetLabel.isJumpToError) {
|
||||
defaultExits.add(inst)
|
||||
}
|
||||
}
|
||||
@@ -285,19 +284,31 @@ private fun ExtractionData.analyzeControlFlow(
|
||||
val nonLocallyUsedDeclarations = getLocalDeclarationsWithNonLocalUsages(pseudocode, localInstructions, bindingContext)
|
||||
val (declarationsToCopy, declarationsToReport) = nonLocallyUsedDeclarations.partition { it is KtProperty && it.isLocal }
|
||||
|
||||
val (typeOfDefaultFlow, defaultResultExpressions) = getResultTypeAndExpressions(defaultExits, bindingContext, targetScope, options, module)
|
||||
val (returnValueType, valuedReturnExpressions) = getResultTypeAndExpressions(valuedReturnExits, bindingContext, targetScope, options, module)
|
||||
val (typeOfDefaultFlow, defaultResultExpressions) = getResultTypeAndExpressions(
|
||||
defaultExits,
|
||||
bindingContext,
|
||||
targetScope,
|
||||
options,
|
||||
module
|
||||
)
|
||||
|
||||
val (returnValueType, valuedReturnExpressions) = getResultTypeAndExpressions(
|
||||
valuedReturnExits,
|
||||
bindingContext,
|
||||
targetScope,
|
||||
options,
|
||||
module
|
||||
)
|
||||
|
||||
val emptyControlFlow =
|
||||
ControlFlow(Collections.emptyList(), { OutputValueBoxer.AsTuple(it, module) }, declarationsToCopy)
|
||||
ControlFlow(Collections.emptyList(), { OutputValueBoxer.AsTuple(it, module) }, 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, defaultResultExpressions, defaultReturnType)))
|
||||
}
|
||||
else {
|
||||
} else {
|
||||
emptyControlFlow
|
||||
}
|
||||
|
||||
@@ -309,7 +320,7 @@ private fun ExtractionData.analyzeControlFlow(
|
||||
val outParameters =
|
||||
parameters.filter { it.mirrorVarName != null && modifiedVarDescriptors[it.originalDescriptor] != null }.sortedBy { it.nameForRef }
|
||||
val outDeclarations =
|
||||
declarationsToCopy.filter { modifiedVarDescriptors[bindingContext[BindingContext.DECLARATION_TO_DESCRIPTOR, it]] != null }
|
||||
declarationsToCopy.filter { modifiedVarDescriptors[bindingContext[BindingContext.DECLARATION_TO_DESCRIPTOR, it]] != null }
|
||||
val modifiedValueCount = outParameters.size + outDeclarations.size
|
||||
|
||||
val outputValues = ArrayList<OutputValue>()
|
||||
@@ -321,8 +332,7 @@ private fun ExtractionData.analyzeControlFlow(
|
||||
if (valuedReturnExits.isNotEmpty() || jumpExits.isNotEmpty()) return multipleExitsError
|
||||
|
||||
outputValues.add(ExpressionValue(false, defaultResultExpressions, typeOfDefaultFlow))
|
||||
}
|
||||
else if (valuedReturnExits.isNotEmpty()) {
|
||||
} else if (valuedReturnExits.isNotEmpty()) {
|
||||
if (jumpExits.isNotEmpty()) return multipleExitsError
|
||||
|
||||
if (defaultExits.isNotEmpty()) {
|
||||
@@ -350,11 +360,11 @@ private fun ExtractionData.analyzeControlFlow(
|
||||
outputValues.size > 3 -> {
|
||||
if (!options.enableListBoxing) {
|
||||
val outValuesStr =
|
||||
(outParameters.map { it.originalDescriptor.renderForMessage() }
|
||||
+ outDeclarations.map { it.renderForMessage(bindingContext)!! }).sorted()
|
||||
(outParameters.map { it.originalDescriptor.renderForMessage() }
|
||||
+ outDeclarations.map { it.renderForMessage(bindingContext)!! }).sorted()
|
||||
return controlFlow to ErrorMessage.MULTIPLE_OUTPUT.addAdditionalInfo(outValuesStr)
|
||||
}
|
||||
{ outputValues -> OutputValueBoxer.AsList(outputValues) } // KT-8596
|
||||
{ values -> OutputValueBoxer.AsList(values) }
|
||||
}
|
||||
|
||||
else -> controlFlow.boxerFactory
|
||||
@@ -370,7 +380,16 @@ private fun ExtractionData.analyzeControlFlow(
|
||||
val conditional = !singleExit && defaultExits.isNotEmpty()
|
||||
val elements = jumpExits.map { it.element as KtExpression }
|
||||
val elementToInsertAfterCall = if (singleExit) null else elements.first()
|
||||
return controlFlow.copy(outputValues = Collections.singletonList(Jump(elements, elementToInsertAfterCall, conditional, module.builtIns))) to null
|
||||
return controlFlow.copy(
|
||||
outputValues = Collections.singletonList(
|
||||
Jump(
|
||||
elements,
|
||||
elementToInsertAfterCall,
|
||||
conditional,
|
||||
module.builtIns
|
||||
)
|
||||
)
|
||||
) to null
|
||||
}
|
||||
|
||||
return controlFlow to null
|
||||
@@ -386,13 +405,12 @@ fun ExtractionData.createTemporaryDeclaration(pattern: String): KtNamedDeclarati
|
||||
val newTargetParent = newTargetSibling.parent
|
||||
|
||||
val declaration = KtPsiFactory(originalFile).createDeclarationByPattern<KtNamedDeclaration>(
|
||||
pattern,
|
||||
PsiChildRange(originalElements.firstOrNull(), originalElements.lastOrNull())
|
||||
pattern,
|
||||
PsiChildRange(originalElements.firstOrNull(), originalElements.lastOrNull())
|
||||
)
|
||||
return if (insertBefore) {
|
||||
newTargetParent.addBefore(declaration, newTargetSibling) as KtNamedDeclaration
|
||||
}
|
||||
else {
|
||||
} else {
|
||||
newTargetParent.addAfter(declaration, newTargetSibling) as KtNamedDeclaration
|
||||
}
|
||||
}
|
||||
@@ -407,20 +425,20 @@ internal fun ExtractionData.createTemporaryCodeBlock(): KtBlockExpression {
|
||||
private fun KotlinType.collectReferencedTypes(processTypeArguments: Boolean): List<KotlinType> {
|
||||
if (!processTypeArguments) return Collections.singletonList(this)
|
||||
return DFS.dfsFromNode(
|
||||
this,
|
||||
Neighbors<KotlinType> { current -> current.arguments.map { it.type } },
|
||||
VisitedWithSet(),
|
||||
object: CollectingNodeHandler<KotlinType, KotlinType, ArrayList<KotlinType>>(ArrayList()) {
|
||||
override fun afterChildren(current: KotlinType) {
|
||||
result.add(current)
|
||||
}
|
||||
this,
|
||||
Neighbors<KotlinType> { current -> current.arguments.map { it.type } },
|
||||
VisitedWithSet(),
|
||||
object : CollectingNodeHandler<KotlinType, KotlinType, ArrayList<KotlinType>>(ArrayList()) {
|
||||
override fun afterChildren(current: KotlinType) {
|
||||
result.add(current)
|
||||
}
|
||||
}
|
||||
)!!
|
||||
}
|
||||
|
||||
fun KtTypeParameter.collectRelevantConstraints(): List<KtTypeConstraint> {
|
||||
val typeConstraints = getNonStrictParentOfType<KtTypeParameterListOwner>()?.typeConstraints ?: return Collections.emptyList()
|
||||
return typeConstraints.filter { it.subjectTypeParameterName?.mainReference?.resolve() == this}
|
||||
return typeConstraints.filter { it.subjectTypeParameterName?.mainReference?.resolve() == this }
|
||||
}
|
||||
|
||||
fun TypeParameter.collectReferencedTypes(bindingContext: BindingContext): List<KotlinType> {
|
||||
@@ -443,11 +461,11 @@ private fun KotlinType.isExtractable(targetScope: LexicalScope?): Boolean {
|
||||
}
|
||||
|
||||
internal fun KotlinType.processTypeIfExtractable(
|
||||
typeParameters: MutableSet<TypeParameter>,
|
||||
nonDenotableTypes: MutableSet<KotlinType>,
|
||||
options: ExtractionOptions,
|
||||
targetScope: LexicalScope?,
|
||||
processTypeArguments: Boolean = true
|
||||
typeParameters: MutableSet<TypeParameter>,
|
||||
nonDenotableTypes: MutableSet<KotlinType>,
|
||||
options: ExtractionOptions,
|
||||
targetScope: LexicalScope?,
|
||||
processTypeArguments: Boolean = true
|
||||
): Boolean {
|
||||
return collectReferencedTypes(processTypeArguments).fold(true) { extractable, typeToCheck ->
|
||||
val parameterTypeDescriptor = typeToCheck.constructor.declarationDescriptor as? TypeParameterDescriptor
|
||||
@@ -479,13 +497,13 @@ internal fun KotlinType.processTypeIfExtractable(
|
||||
}
|
||||
|
||||
internal class MutableParameter(
|
||||
override val argumentText: String,
|
||||
override val originalDescriptor: DeclarationDescriptor,
|
||||
override val receiverCandidate: Boolean,
|
||||
private val targetScope: LexicalScope?,
|
||||
private val originalType: KotlinType,
|
||||
private val possibleTypes: Set<KotlinType>
|
||||
): Parameter {
|
||||
override val argumentText: String,
|
||||
override val originalDescriptor: DeclarationDescriptor,
|
||||
override val receiverCandidate: Boolean,
|
||||
private val targetScope: LexicalScope?,
|
||||
private val originalType: KotlinType,
|
||||
private val possibleTypes: Set<KotlinType>
|
||||
) : Parameter {
|
||||
// All modifications happen in the same thread
|
||||
private var writable: Boolean = true
|
||||
private val defaultTypes = LinkedHashSet<KotlinType>()
|
||||
@@ -515,8 +533,7 @@ internal class MutableParameter(
|
||||
writable = false
|
||||
if (defaultTypes.isNotEmpty()) {
|
||||
TypeIntersector.intersectTypes(defaultTypes)!!
|
||||
}
|
||||
else originalType
|
||||
} else originalType
|
||||
}
|
||||
|
||||
private val parameterTypeCandidates: List<KotlinType> by lazy {
|
||||
@@ -530,8 +547,7 @@ internal class MutableParameter(
|
||||
if (typePredicate(bounds.upperBound)) add(bounds.upperBound)
|
||||
if (typePredicate(bounds.lowerBound)) add(bounds.lowerBound)
|
||||
}
|
||||
}
|
||||
else linkedSetOf(defaultType)
|
||||
} else linkedSetOf(defaultType)
|
||||
|
||||
val addNullableTypes = defaultType.isNullabilityFlexible() && typeSet.size > 1
|
||||
val superTypes = TypeUtils.getAllSupertypes(defaultType).filter(typePredicate)
|
||||
@@ -547,11 +563,11 @@ internal class MutableParameter(
|
||||
}
|
||||
|
||||
override fun getParameterTypeCandidates(allowSpecialClassNames: Boolean): List<KotlinType> {
|
||||
return if (!allowSpecialClassNames) {
|
||||
parameterTypeCandidates.filter { it.isExtractable(targetScope) }
|
||||
} else {
|
||||
parameterTypeCandidates
|
||||
}
|
||||
return if (!allowSpecialClassNames) {
|
||||
parameterTypeCandidates.filter { it.isExtractable(targetScope) }
|
||||
} else {
|
||||
parameterTypeCandidates
|
||||
}
|
||||
}
|
||||
|
||||
override fun getParameterType(allowSpecialClassNames: Boolean): KotlinType {
|
||||
@@ -562,31 +578,32 @@ internal class MutableParameter(
|
||||
}
|
||||
|
||||
private class DelegatingParameter(
|
||||
val original: Parameter,
|
||||
override val name: String,
|
||||
val parameterType: KotlinType
|
||||
): Parameter by original {
|
||||
val original: Parameter,
|
||||
override val name: String,
|
||||
val parameterType: KotlinType
|
||||
) : Parameter by original {
|
||||
override fun copy(name: String, parameterType: KotlinType): Parameter = DelegatingParameter(original, name, parameterType)
|
||||
override fun getParameterType(allowSpecialClassNames: Boolean) = parameterType
|
||||
}
|
||||
|
||||
private fun ExtractionData.checkDeclarationsMovingOutOfScope(
|
||||
enclosingDeclaration: KtDeclaration,
|
||||
controlFlow: ControlFlow,
|
||||
bindingContext: BindingContext
|
||||
enclosingDeclaration: KtDeclaration,
|
||||
controlFlow: ControlFlow,
|
||||
bindingContext: BindingContext
|
||||
): ErrorMessage? {
|
||||
val declarationsOutOfScope = HashSet<KtNamedDeclaration>()
|
||||
controlFlow.jumpOutputValue?.elementToInsertAfterCall?.accept(
|
||||
object : KtTreeVisitorVoid() {
|
||||
override fun visitSimpleNameExpression(expression: KtSimpleNameExpression) {
|
||||
val target = expression.mainReference.resolve()
|
||||
if (target is KtNamedDeclaration
|
||||
&& target.isInsideOf(physicalElements)
|
||||
&& target.getStrictParentOfType<KtDeclaration>() == enclosingDeclaration) {
|
||||
declarationsOutOfScope.add(target)
|
||||
}
|
||||
object : KtTreeVisitorVoid() {
|
||||
override fun visitSimpleNameExpression(expression: KtSimpleNameExpression) {
|
||||
val target = expression.mainReference.resolve()
|
||||
if (target is KtNamedDeclaration
|
||||
&& target.isInsideOf(physicalElements)
|
||||
&& target.getStrictParentOfType<KtDeclaration>() == enclosingDeclaration
|
||||
) {
|
||||
declarationsOutOfScope.add(target)
|
||||
}
|
||||
}
|
||||
}
|
||||
)
|
||||
|
||||
if (declarationsOutOfScope.isNotEmpty()) {
|
||||
@@ -639,7 +656,7 @@ fun ExtractionData.performAnalysis(): AnalysisResult {
|
||||
|
||||
val declaration = commonParent.containingDeclarationForPseudocode ?: return noContainerError
|
||||
val pseudocode = declaration.getContainingPseudocode(bindingContext)
|
||||
?: return AnalysisResult(null, Status.CRITICAL_ERROR, listOf(ErrorMessage.SYNTAX_ERRORS))
|
||||
?: return AnalysisResult(null, Status.CRITICAL_ERROR, listOf(ErrorMessage.SYNTAX_ERRORS))
|
||||
val localInstructions = getLocalInstructions(pseudocode)
|
||||
|
||||
val modifiedVarDescriptorsWithExpressions = localInstructions.getModifiedVarDescriptors(bindingContext)
|
||||
@@ -648,12 +665,12 @@ fun ExtractionData.performAnalysis(): AnalysisResult {
|
||||
|
||||
val targetScope = targetSibling.getResolutionScope(bindingContext, commonParent.getResolutionFacade())
|
||||
val paramsInfo = inferParametersInfo(
|
||||
virtualBlock,
|
||||
commonParent,
|
||||
pseudocode,
|
||||
bindingContext,
|
||||
targetScope,
|
||||
modifiedVarDescriptorsWithExpressions.keys
|
||||
virtualBlock,
|
||||
commonParent,
|
||||
pseudocode,
|
||||
bindingContext,
|
||||
targetScope,
|
||||
modifiedVarDescriptorsWithExpressions.keys
|
||||
)
|
||||
if (paramsInfo.errorMessage != null) {
|
||||
return AnalysisResult(null, Status.CRITICAL_ERROR, listOf(paramsInfo.errorMessage!!))
|
||||
@@ -665,14 +682,14 @@ fun ExtractionData.performAnalysis(): AnalysisResult {
|
||||
modifiedVarDescriptorsForControlFlow.keys.retainAll(localInstructions.getVarDescriptorsAccessedAfterwards(bindingContext))
|
||||
val (controlFlow, controlFlowMessage) =
|
||||
analyzeControlFlow(
|
||||
localInstructions,
|
||||
pseudocode,
|
||||
originalFile.findModuleDescriptor(),
|
||||
bindingContext,
|
||||
modifiedVarDescriptorsForControlFlow,
|
||||
options,
|
||||
targetScope,
|
||||
paramsInfo.parameters
|
||||
localInstructions,
|
||||
pseudocode,
|
||||
originalFile.findModuleDescriptor(),
|
||||
bindingContext,
|
||||
modifiedVarDescriptorsForControlFlow,
|
||||
options,
|
||||
targetScope,
|
||||
paramsInfo.parameters
|
||||
)
|
||||
controlFlowMessage?.let { messages.add(it) }
|
||||
|
||||
@@ -680,11 +697,11 @@ fun ExtractionData.performAnalysis(): AnalysisResult {
|
||||
returnType.processTypeIfExtractable(paramsInfo.typeParameters, paramsInfo.nonDenotableTypes, options, targetScope)
|
||||
|
||||
if (paramsInfo.nonDenotableTypes.isNotEmpty()) {
|
||||
val typeStr = paramsInfo.nonDenotableTypes.map {it.renderForMessage()}.sorted()
|
||||
val typeStr = paramsInfo.nonDenotableTypes.map { it.renderForMessage() }.sorted()
|
||||
return AnalysisResult(
|
||||
null,
|
||||
Status.CRITICAL_ERROR,
|
||||
listOf(ErrorMessage.DENOTABLE_TYPES.addAdditionalInfo(typeStr))
|
||||
null,
|
||||
Status.CRITICAL_ERROR,
|
||||
listOf(ErrorMessage.DENOTABLE_TYPES.addAdditionalInfo(typeStr))
|
||||
)
|
||||
}
|
||||
|
||||
@@ -692,35 +709,35 @@ fun ExtractionData.performAnalysis(): AnalysisResult {
|
||||
checkDeclarationsMovingOutOfScope(enclosingDeclaration, controlFlow, bindingContext)?.let { messages.add(it) }
|
||||
|
||||
controlFlow.jumpOutputValue?.elementToInsertAfterCall?.accept(
|
||||
object : KtTreeVisitorVoid() {
|
||||
override fun visitSimpleNameExpression(expression: KtSimpleNameExpression) {
|
||||
paramsInfo.originalRefToParameter[expression].firstOrNull()?.let { it.refCount-- }
|
||||
}
|
||||
object : KtTreeVisitorVoid() {
|
||||
override fun visitSimpleNameExpression(expression: KtSimpleNameExpression) {
|
||||
paramsInfo.originalRefToParameter[expression].firstOrNull()?.let { it.refCount-- }
|
||||
}
|
||||
}
|
||||
)
|
||||
val adjustedParameters = paramsInfo.parameters.filterTo(LinkedHashSet<Parameter>()) { it.refCount > 0 }
|
||||
|
||||
val receiverCandidates = adjustedParameters.filterTo(HashSet<Parameter>()) { it.receiverCandidate }
|
||||
val receiverCandidates = adjustedParameters.filterTo(hashSetOf()) { it.receiverCandidate }
|
||||
val receiverParameter = if (receiverCandidates.size == 1 && !options.canWrapInWith) receiverCandidates.first() else null
|
||||
receiverParameter?.let { adjustedParameters.remove(it) }
|
||||
|
||||
var descriptor = ExtractableCodeDescriptor(
|
||||
this,
|
||||
bindingContext,
|
||||
suggestFunctionNames(returnType),
|
||||
getDefaultVisibility(),
|
||||
adjustedParameters.toList(),
|
||||
receiverParameter,
|
||||
paramsInfo.typeParameters.sortedBy { it.originalDeclaration.name!! },
|
||||
paramsInfo.replacementMap,
|
||||
if (messages.isEmpty()) controlFlow else controlFlow.toDefault(),
|
||||
returnType,
|
||||
emptyList()
|
||||
this,
|
||||
bindingContext,
|
||||
suggestFunctionNames(returnType),
|
||||
getDefaultVisibility(),
|
||||
adjustedParameters.toList(),
|
||||
receiverParameter,
|
||||
paramsInfo.typeParameters.sortedBy { it.originalDeclaration.name!! },
|
||||
paramsInfo.replacementMap,
|
||||
if (messages.isEmpty()) controlFlow else controlFlow.toDefault(),
|
||||
returnType,
|
||||
emptyList()
|
||||
)
|
||||
|
||||
val generatedDeclaration = ExtractionGeneratorConfiguration(
|
||||
descriptor,
|
||||
ExtractionGeneratorOptions(inTempFile = true, allowExpressionBody = false)
|
||||
descriptor,
|
||||
ExtractionGeneratorOptions(inTempFile = true, allowExpressionBody = false)
|
||||
).generateDeclaration().declaration
|
||||
val virtualContext = generatedDeclaration.analyzeWithContent()
|
||||
if (virtualContext.diagnostics.all().any { it.factory == Errors.ILLEGAL_SUSPEND_FUNCTION_CALL || it.factory == Errors.ILLEGAL_SUSPEND_PROPERTY_ACCESS }) {
|
||||
@@ -729,9 +746,9 @@ fun ExtractionData.performAnalysis(): AnalysisResult {
|
||||
|
||||
|
||||
return AnalysisResult(
|
||||
descriptor,
|
||||
if (messages.isEmpty()) Status.SUCCESS else Status.NON_CRITICAL_ERROR,
|
||||
messages
|
||||
descriptor,
|
||||
if (messages.isEmpty()) Status.SUCCESS else Status.NON_CRITICAL_ERROR,
|
||||
messages
|
||||
)
|
||||
}
|
||||
|
||||
@@ -739,12 +756,12 @@ private fun ExtractionData.suggestFunctionNames(returnType: KotlinType): List<St
|
||||
val functionNames = LinkedHashSet<String>()
|
||||
|
||||
val validator =
|
||||
NewDeclarationNameValidator(
|
||||
targetSibling.parent,
|
||||
if (targetSibling is KtAnonymousInitializer) targetSibling.parent else targetSibling,
|
||||
if (options.extractAsProperty) NewDeclarationNameValidator.Target.VARIABLES else NewDeclarationNameValidator.Target.FUNCTIONS_AND_CLASSES
|
||||
)
|
||||
if (!returnType.isDefault()) {
|
||||
NewDeclarationNameValidator(
|
||||
targetSibling.parent,
|
||||
if (targetSibling is KtAnonymousInitializer) targetSibling.parent else targetSibling,
|
||||
if (options.extractAsProperty) NewDeclarationNameValidator.Target.VARIABLES else NewDeclarationNameValidator.Target.FUNCTIONS_AND_CLASSES
|
||||
)
|
||||
if (!KotlinBuiltIns.isUnit(returnType)) {
|
||||
functionNames.addAll(KotlinNameSuggester.suggestNamesByType(returnType, validator))
|
||||
}
|
||||
|
||||
@@ -759,20 +776,20 @@ private fun ExtractionData.suggestFunctionNames(returnType: KotlinType): List<St
|
||||
}
|
||||
|
||||
internal fun KtNamedDeclaration.getGeneratedBody() =
|
||||
when (this) {
|
||||
is KtNamedFunction -> bodyExpression
|
||||
else -> {
|
||||
val property = this as KtProperty
|
||||
when (this) {
|
||||
is KtNamedFunction -> bodyExpression
|
||||
else -> {
|
||||
val property = this as KtProperty
|
||||
|
||||
property.getter?.bodyExpression?.let { return it }
|
||||
property.initializer?.let { return it }
|
||||
// We assume lazy property here with delegate expression 'by Delegates.lazy { body }'
|
||||
property.delegateExpression?.let {
|
||||
val call = it.getCalleeExpressionIfAny()?.parent as? KtCallExpression
|
||||
call?.lambdaArguments?.singleOrNull()?.getLambdaExpression()?.bodyExpression
|
||||
}
|
||||
property.getter?.bodyExpression?.let { return it }
|
||||
property.initializer?.let { return it }
|
||||
// We assume lazy property here with delegate expression 'by Delegates.lazy { body }'
|
||||
property.delegateExpression?.let {
|
||||
val call = it.getCalleeExpressionIfAny()?.parent as? KtCallExpression
|
||||
call?.lambdaArguments?.singleOrNull()?.getLambdaExpression()?.bodyExpression
|
||||
}
|
||||
} ?: throw AssertionError("Couldn't get block body for this declaration: ${getElementTextWithContext()}")
|
||||
}
|
||||
} ?: throw AssertionError("Couldn't get block body for this declaration: ${getElementTextWithContext()}")
|
||||
|
||||
@JvmOverloads
|
||||
fun ExtractableCodeDescriptor.validate(target: ExtractionTarget = ExtractionTarget.FUNCTION): ExtractableCodeDescriptorWithConflicts {
|
||||
@@ -785,8 +802,8 @@ fun ExtractableCodeDescriptor.validate(target: ExtractionTarget = ExtractionTarg
|
||||
val conflicts = MultiMap<PsiElement, String>()
|
||||
|
||||
val result = ExtractionGeneratorConfiguration(
|
||||
this,
|
||||
ExtractionGeneratorOptions(inTempFile = true, allowExpressionBody = false, target = target)
|
||||
this,
|
||||
ExtractionGeneratorOptions(inTempFile = true, allowExpressionBody = false, target = target)
|
||||
).generateDeclaration()
|
||||
|
||||
val valueParameterList = (result.declaration as? KtNamedFunction)?.valueParameterList
|
||||
@@ -802,19 +819,20 @@ fun ExtractableCodeDescriptor.validate(target: ExtractionTarget = ExtractionTarg
|
||||
|
||||
val currentDescriptor = bindingContext[BindingContext.REFERENCE_TARGET, currentRefExpr]
|
||||
val currentTarget =
|
||||
currentDescriptor?.let { DescriptorToSourceUtilsIde.getAnyDeclaration(extractionData.project, it) } as? PsiNamedElement
|
||||
currentDescriptor?.let { DescriptorToSourceUtilsIde.getAnyDeclaration(extractionData.project, it) } as? PsiNamedElement
|
||||
if (currentTarget is KtParameter && currentTarget.parent == valueParameterList) return
|
||||
if (currentTarget is KtTypeParameter && currentTarget.parent == typeParameterList) return
|
||||
if (currentDescriptor is LocalVariableDescriptor
|
||||
&& parameters.any { it.mirrorVarName == currentDescriptor.name.asString() }) return
|
||||
&& parameters.any { it.mirrorVarName == currentDescriptor.name.asString() }
|
||||
) return
|
||||
|
||||
if (diagnostics.any { it.factory in Errors.UNRESOLVED_REFERENCE_DIAGNOSTICS }
|
||||
|| (currentDescriptor != null
|
||||
&& !ErrorUtils.isError(currentDescriptor)
|
||||
&& !compareDescriptors(extractionData.project, currentDescriptor, resolveResult.descriptor))) {
|
||||
&& !ErrorUtils.isError(currentDescriptor)
|
||||
&& !compareDescriptors(extractionData.project, currentDescriptor, resolveResult.descriptor))) {
|
||||
conflicts.putValue(
|
||||
resolveResult.originalRefExpr,
|
||||
getDeclarationMessage(resolveResult.declaration, "0.will.no.longer.be.accessible.after.extraction")
|
||||
resolveResult.originalRefExpr,
|
||||
getDeclarationMessage(resolveResult.declaration, "0.will.no.longer.be.accessible.after.extraction")
|
||||
)
|
||||
return
|
||||
}
|
||||
@@ -831,23 +849,21 @@ fun ExtractableCodeDescriptor.validate(target: ExtractionTarget = ExtractionTarg
|
||||
}
|
||||
|
||||
result.declaration.accept(
|
||||
object : KtTreeVisitorVoid() {
|
||||
override fun visitUserType(userType: KtUserType) {
|
||||
val refExpr = userType.referenceExpression ?: return
|
||||
val diagnostics = bindingContext.diagnostics.forElement(refExpr)
|
||||
diagnostics.firstOrNull { it.factory == Errors.INVISIBLE_REFERENCE }?.let {
|
||||
val declaration = refExpr.mainReference.resolve() as? PsiNamedElement ?: return
|
||||
conflicts.putValue(declaration, getDeclarationMessage(declaration, "0.will.become.invisible.after.extraction"))
|
||||
}
|
||||
}
|
||||
|
||||
override fun visitSimpleNameExpression(expression: KtSimpleNameExpression) {
|
||||
processReference(expression)
|
||||
object : KtTreeVisitorVoid() {
|
||||
override fun visitUserType(userType: KtUserType) {
|
||||
val refExpr = userType.referenceExpression ?: return
|
||||
val diagnostics = bindingContext.diagnostics.forElement(refExpr)
|
||||
diagnostics.firstOrNull { it.factory == Errors.INVISIBLE_REFERENCE }?.let {
|
||||
val declaration = refExpr.mainReference.resolve() as? PsiNamedElement ?: return
|
||||
conflicts.putValue(declaration, getDeclarationMessage(declaration, "0.will.become.invisible.after.extraction"))
|
||||
}
|
||||
}
|
||||
|
||||
override fun visitSimpleNameExpression(expression: KtSimpleNameExpression) {
|
||||
processReference(expression)
|
||||
}
|
||||
}
|
||||
)
|
||||
|
||||
return ExtractableCodeDescriptorWithConflicts(this, conflicts)
|
||||
}
|
||||
|
||||
private val LOG = Logger.getInstance(ExtractionEngine::class.java)
|
||||
}
|
||||
+105
-101
@@ -24,6 +24,7 @@ import com.intellij.psi.codeStyle.CodeStyleManager
|
||||
import com.intellij.psi.search.LocalSearchScope
|
||||
import com.intellij.psi.search.searches.ReferencesSearch
|
||||
import com.intellij.refactoring.BaseRefactoringProcessor
|
||||
import org.jetbrains.kotlin.builtins.KotlinBuiltIns
|
||||
import org.jetbrains.kotlin.builtins.isFunctionType
|
||||
import org.jetbrains.kotlin.descriptors.ClassDescriptor
|
||||
import org.jetbrains.kotlin.descriptors.Visibilities
|
||||
@@ -81,22 +82,22 @@ private fun buildSignature(config: ExtractionGeneratorConfiguration, renderer: D
|
||||
modifier(modifiers.joinToString(separator = " "))
|
||||
|
||||
typeParams(
|
||||
config.descriptor.typeParameters.map {
|
||||
val typeParameter = it.originalDeclaration
|
||||
val bound = typeParameter.extendsBound
|
||||
config.descriptor.typeParameters.map {
|
||||
val typeParameter = it.originalDeclaration
|
||||
val bound = typeParameter.extendsBound
|
||||
|
||||
buildString {
|
||||
if (it.isReified()) {
|
||||
append(KtTokens.REIFIED_KEYWORD.value)
|
||||
append(' ')
|
||||
}
|
||||
append(typeParameter.name)
|
||||
if (bound != null) {
|
||||
append(" : ")
|
||||
append(bound.text)
|
||||
}
|
||||
buildString {
|
||||
if (it.isReified()) {
|
||||
append(KtTokens.REIFIED_KEYWORD.value)
|
||||
append(' ')
|
||||
}
|
||||
append(typeParameter.name)
|
||||
if (bound != null) {
|
||||
append(" : ")
|
||||
append(bound.text)
|
||||
}
|
||||
}
|
||||
}
|
||||
)
|
||||
|
||||
fun KotlinType.typeAsString(): String {
|
||||
@@ -116,12 +117,14 @@ private fun buildSignature(config: ExtractionGeneratorConfiguration, renderer: D
|
||||
name(config.generatorOptions.dummyName ?: config.descriptor.name)
|
||||
|
||||
config.descriptor.parameters.forEach { parameter ->
|
||||
param(parameter.name,
|
||||
parameter.getParameterType(config.descriptor.extractionData.options.allowSpecialClassNames).typeAsString())
|
||||
param(
|
||||
parameter.name,
|
||||
parameter.getParameterType(config.descriptor.extractionData.options.allowSpecialClassNames).typeAsString()
|
||||
)
|
||||
}
|
||||
|
||||
with(config.descriptor.returnType) {
|
||||
if (isDefault() || isError || extractionTarget == ExtractionTarget.PROPERTY_WITH_INITIALIZER) {
|
||||
if (KotlinBuiltIns.isUnit(this) || isError || extractionTarget == ExtractionTarget.PROPERTY_WITH_INITIALIZER) {
|
||||
noReturnType()
|
||||
} else {
|
||||
returnType(typeAsString())
|
||||
@@ -135,7 +138,7 @@ private fun buildSignature(config: ExtractionGeneratorConfiguration, renderer: D
|
||||
fun ExtractionGeneratorConfiguration.getSignaturePreview(renderer: DescriptorRenderer) = buildSignature(this, renderer).asString()
|
||||
|
||||
fun ExtractionGeneratorConfiguration.getDeclarationPattern(
|
||||
descriptorRenderer: DescriptorRenderer = IdeDescriptorRenderers.SOURCE_CODE
|
||||
descriptorRenderer: DescriptorRenderer = IdeDescriptorRenderers.SOURCE_CODE
|
||||
): String {
|
||||
val extractionTarget = generatorOptions.target
|
||||
if (!extractionTarget.isAvailable(descriptor)) {
|
||||
@@ -172,9 +175,9 @@ fun createNameCounterpartMap(from: KtElement, to: KtElement): Map<KtSimpleNameEx
|
||||
}
|
||||
|
||||
class DuplicateInfo(
|
||||
val range: KotlinPsiRange,
|
||||
val controlFlow: ControlFlow,
|
||||
val arguments: List<String>
|
||||
val range: KotlinPsiRange,
|
||||
val controlFlow: ControlFlow,
|
||||
val arguments: List<String>
|
||||
)
|
||||
|
||||
fun ExtractableCodeDescriptor.findDuplicates(): List<DuplicateInfo> {
|
||||
@@ -225,25 +228,31 @@ fun ExtractableCodeDescriptor.findDuplicates(): List<DuplicateInfo> {
|
||||
return if (matched) newControlFlow else null
|
||||
}
|
||||
|
||||
val unifierParameters = parameters.map { UnifierParameter(it.originalDescriptor, it.getParameterType(extractionData.options.allowSpecialClassNames)) }
|
||||
val unifierParameters =
|
||||
parameters.map { UnifierParameter(it.originalDescriptor, it.getParameterType(extractionData.options.allowSpecialClassNames)) }
|
||||
|
||||
val unifier = KotlinPsiUnifier(unifierParameters, true)
|
||||
|
||||
val scopeElement = getOccurrenceContainer() ?: return Collections.emptyList()
|
||||
val originalTextRange = extractionData.originalRange.getPhysicalTextRange()
|
||||
return extractionData
|
||||
.originalRange
|
||||
.match(scopeElement, unifier)
|
||||
.asSequence()
|
||||
.filter { !(it.range.getPhysicalTextRange().intersects(originalTextRange)) }
|
||||
.mapNotNull { match ->
|
||||
val controlFlow = getControlFlowIfMatched(match)
|
||||
val range = with(match.range) {
|
||||
(elements.singleOrNull() as? KtStringTemplateEntryWithExpression)?.expression?.toRange() ?: this
|
||||
}
|
||||
controlFlow?.let { DuplicateInfo(range, it, unifierParameters.map { match.substitution[it]!!.text!! }) }
|
||||
.originalRange
|
||||
.match(scopeElement, unifier)
|
||||
.asSequence()
|
||||
.filter { !(it.range.getPhysicalTextRange().intersects(originalTextRange)) }
|
||||
.mapNotNull { match ->
|
||||
val controlFlow = getControlFlowIfMatched(match)
|
||||
val range = with(match.range) {
|
||||
(elements.singleOrNull() as? KtStringTemplateEntryWithExpression)?.expression?.toRange() ?: this
|
||||
}
|
||||
.toList()
|
||||
|
||||
controlFlow?.let {
|
||||
DuplicateInfo(range, it, unifierParameters.map { param ->
|
||||
match.substitution[param]!!.text!!
|
||||
})
|
||||
}
|
||||
}
|
||||
.toList()
|
||||
}
|
||||
|
||||
private fun ExtractableCodeDescriptor.getOccurrenceContainer(): PsiElement? {
|
||||
@@ -251,11 +260,12 @@ private fun ExtractableCodeDescriptor.getOccurrenceContainer(): PsiElement? {
|
||||
}
|
||||
|
||||
private fun makeCall(
|
||||
extractableDescriptor: ExtractableCodeDescriptor,
|
||||
declaration: KtNamedDeclaration,
|
||||
controlFlow: ControlFlow,
|
||||
rangeToReplace: KotlinPsiRange,
|
||||
arguments: List<String>) {
|
||||
extractableDescriptor: ExtractableCodeDescriptor,
|
||||
declaration: KtNamedDeclaration,
|
||||
controlFlow: ControlFlow,
|
||||
rangeToReplace: KotlinPsiRange,
|
||||
arguments: List<String>
|
||||
) {
|
||||
fun insertCall(anchor: PsiElement, wrappedCall: KtExpression): KtExpression? {
|
||||
val firstExpression = rangeToReplace.elements.firstOrNull { it is KtExpression } as? KtExpression
|
||||
if (firstExpression?.isLambdaOutsideParentheses() ?: false) {
|
||||
@@ -318,13 +328,13 @@ private fun makeCall(
|
||||
val isVar = declarationsToMerge.first().isVar
|
||||
if (declarationsToMerge.all { it.isVar == isVar }) {
|
||||
controlFlow.declarationsToCopy.subtract(declarationsToMerge).forEach {
|
||||
block.addBefore(psiFactory.createDeclaration<KtDeclaration>(it.text!!), anchorInBlock) as KtDeclaration
|
||||
block.addBefore(psiFactory.createDeclaration(it.text!!), anchorInBlock) as KtDeclaration
|
||||
block.addBefore(newLine, anchorInBlock)
|
||||
}
|
||||
|
||||
val entries = declarationsToMerge.map { p -> p.name + (p.typeReference?.let { ": ${it.text}" } ?: "") }
|
||||
anchorInBlock?.replace(
|
||||
psiFactory.createDestructuringDeclaration("${if (isVar) "var" else "val"} (${entries.joinToString()}) = $callText")
|
||||
psiFactory.createDestructuringDeclaration("${if (isVar) "var" else "val"} (${entries.joinToString()}) = $callText")
|
||||
)
|
||||
|
||||
return
|
||||
@@ -333,16 +343,15 @@ private fun makeCall(
|
||||
|
||||
val inlinableCall = controlFlow.outputValues.size <= 1
|
||||
val unboxingExpressions =
|
||||
if (inlinableCall) {
|
||||
controlFlow.outputValueBoxer.getUnboxingExpressions(callText ?: return)
|
||||
}
|
||||
else {
|
||||
val varNameValidator = NewDeclarationNameValidator(block, anchorInBlock, NewDeclarationNameValidator.Target.VARIABLES)
|
||||
val resultVal = KotlinNameSuggester.suggestNamesByType(extractableDescriptor.returnType, varNameValidator, null).first()
|
||||
block.addBefore(psiFactory.createDeclaration("val $resultVal = $callText"), anchorInBlock)
|
||||
block.addBefore(newLine, anchorInBlock)
|
||||
controlFlow.outputValueBoxer.getUnboxingExpressions(resultVal)
|
||||
}
|
||||
if (inlinableCall) {
|
||||
controlFlow.outputValueBoxer.getUnboxingExpressions(callText ?: return)
|
||||
} else {
|
||||
val varNameValidator = NewDeclarationNameValidator(block, anchorInBlock, NewDeclarationNameValidator.Target.VARIABLES)
|
||||
val resultVal = KotlinNameSuggester.suggestNamesByType(extractableDescriptor.returnType, varNameValidator, null).first()
|
||||
block.addBefore(psiFactory.createDeclaration("val $resultVal = $callText"), anchorInBlock)
|
||||
block.addBefore(newLine, anchorInBlock)
|
||||
controlFlow.outputValueBoxer.getUnboxingExpressions(resultVal)
|
||||
}
|
||||
|
||||
val copiedDeclarations = HashMap<KtDeclaration, KtDeclaration>()
|
||||
for (decl in controlFlow.declarationsToCopy) {
|
||||
@@ -363,8 +372,7 @@ private fun makeCall(
|
||||
val firstReturn = outputValue.originalExpressions.asSequence().filterIsInstance<KtReturnExpression>().firstOrNull()
|
||||
val label = firstReturn?.getTargetLabel()?.text ?: ""
|
||||
"return$label $callText"
|
||||
}
|
||||
else {
|
||||
} else {
|
||||
callText
|
||||
}
|
||||
Collections.singletonList(psiFactory.createExpression(exprText))
|
||||
@@ -372,19 +380,19 @@ private fun makeCall(
|
||||
|
||||
is ParameterUpdate ->
|
||||
Collections.singletonList(
|
||||
psiFactory.createExpression("${outputValue.parameter.argumentText} = $callText")
|
||||
psiFactory.createExpression("${outputValue.parameter.argumentText} = $callText")
|
||||
)
|
||||
|
||||
is Jump -> {
|
||||
when {
|
||||
outputValue.elementToInsertAfterCall == null -> Collections.singletonList(psiFactory.createExpression(callText))
|
||||
outputValue.conditional -> Collections.singletonList(
|
||||
psiFactory.createExpression("if ($callText) ${outputValue.elementToInsertAfterCall.text}")
|
||||
psiFactory.createExpression("if ($callText) ${outputValue.elementToInsertAfterCall.text}")
|
||||
)
|
||||
else -> listOf(
|
||||
psiFactory.createExpression(callText),
|
||||
newLine,
|
||||
psiFactory.createExpression(outputValue.elementToInsertAfterCall.text!!)
|
||||
psiFactory.createExpression(callText),
|
||||
newLine,
|
||||
psiFactory.createExpression(outputValue.elementToInsertAfterCall.text!!)
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -402,17 +410,17 @@ private fun makeCall(
|
||||
val defaultValue = controlFlow.defaultOutputValue
|
||||
|
||||
controlFlow.outputValues
|
||||
.filter { it != defaultValue }
|
||||
.flatMap { wrapCall(it, unboxingExpressions[it]!!) }
|
||||
.withIndex()
|
||||
.forEach {
|
||||
val (i, e) = it
|
||||
.filter { it != defaultValue }
|
||||
.flatMap { wrapCall(it, unboxingExpressions[it]!!) }
|
||||
.withIndex()
|
||||
.forEach {
|
||||
val (i, e) = it
|
||||
|
||||
if (i > 0) {
|
||||
block.addBefore(newLine, anchorInBlock)
|
||||
}
|
||||
block.addBefore(e, anchorInBlock)
|
||||
if (i > 0) {
|
||||
block.addBefore(newLine, anchorInBlock)
|
||||
}
|
||||
block.addBefore(e, anchorInBlock)
|
||||
}
|
||||
|
||||
defaultValue?.let {
|
||||
if (!inlinableCall) {
|
||||
@@ -433,12 +441,12 @@ private var KtReturnExpression.isReturnForLabelRemoval: Boolean
|
||||
by NotNullablePsiCopyableUserDataProperty(Key.create("IS_RETURN_FOR_LABEL_REMOVAL"), false)
|
||||
|
||||
fun ExtractionGeneratorConfiguration.generateDeclaration(
|
||||
declarationToReplace: KtNamedDeclaration? = null
|
||||
): ExtractionResult{
|
||||
declarationToReplace: KtNamedDeclaration? = null
|
||||
): ExtractionResult {
|
||||
val psiFactory = KtPsiFactory(descriptor.extractionData.originalFile)
|
||||
|
||||
fun getReturnsForLabelRemoval() = descriptor.controlFlow.outputValues
|
||||
.flatMapTo(ArrayList<KtReturnExpression>()) { it.originalExpressions.filterIsInstance<KtReturnExpression>() }
|
||||
.flatMapTo(arrayListOf()) { it.originalExpressions.filterIsInstance<KtReturnExpression>() }
|
||||
|
||||
fun createDeclaration(): KtNamedDeclaration {
|
||||
descriptor.controlFlow.jumpOutputValue?.elementsToReplace?.forEach { it.isJumpElementToReplace = true }
|
||||
@@ -447,11 +455,10 @@ fun ExtractionGeneratorConfiguration.generateDeclaration(
|
||||
return with(descriptor.extractionData) {
|
||||
if (generatorOptions.inTempFile) {
|
||||
createTemporaryDeclaration("${getDeclarationPattern()}\n")
|
||||
}
|
||||
else {
|
||||
} else {
|
||||
psiFactory.createDeclarationByPattern(
|
||||
getDeclarationPattern(),
|
||||
PsiChildRange(originalElements.firstOrNull(), originalElements.lastOrNull())
|
||||
getDeclarationPattern(),
|
||||
PsiChildRange(originalElements.firstOrNull(), originalElements.lastOrNull())
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -459,15 +466,15 @@ fun ExtractionGeneratorConfiguration.generateDeclaration(
|
||||
|
||||
fun getReturnArguments(resultExpression: KtExpression?): List<KtExpression> {
|
||||
return descriptor.controlFlow.outputValues
|
||||
.mapNotNull {
|
||||
when (it) {
|
||||
is ExpressionValue -> resultExpression
|
||||
is Jump -> if (it.conditional) psiFactory.createExpression("false") else null
|
||||
is ParameterUpdate -> psiFactory.createExpression(it.parameter.nameForRef)
|
||||
is Initializer -> psiFactory.createExpression(it.initializedDeclaration.name!!)
|
||||
else -> throw IllegalArgumentException("Unknown output value: $it")
|
||||
}
|
||||
.mapNotNull {
|
||||
when (it) {
|
||||
is ExpressionValue -> resultExpression
|
||||
is Jump -> if (it.conditional) psiFactory.createExpression("false") else null
|
||||
is ParameterUpdate -> psiFactory.createExpression(it.parameter.nameForRef)
|
||||
is Initializer -> psiFactory.createExpression(it.initializedDeclaration.name!!)
|
||||
else -> throw IllegalArgumentException("Unknown output value: $it")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun KtExpression.replaceWithReturn(replacingExpression: KtReturnExpression) {
|
||||
@@ -528,20 +535,19 @@ fun ExtractionGeneratorConfiguration.generateDeclaration(
|
||||
* before calls/types themselves
|
||||
*/
|
||||
val currentRefs = body
|
||||
.collectDescendantsOfType<KtSimpleNameExpression> { it.resolveResult != null }
|
||||
.sortedByDescending { it.startOffset }
|
||||
.collectDescendantsOfType<KtSimpleNameExpression> { it.resolveResult != null }
|
||||
.sortedByDescending { it.startOffset }
|
||||
|
||||
currentRefs.forEach {
|
||||
val resolveResult = it.resolveResult!!
|
||||
val currentRef = if (it.isValid) {
|
||||
it
|
||||
}
|
||||
else {
|
||||
body.findDescendantOfType<KtSimpleNameExpression> { it.resolveResult == resolveResult } ?: return@forEach
|
||||
} else {
|
||||
body.findDescendantOfType { expr -> expr.resolveResult == resolveResult } ?: return@forEach
|
||||
}
|
||||
val originalRef = resolveResult.originalRefExpr
|
||||
val newRef = descriptor.replacementMap[originalRef]
|
||||
.fold(currentRef as KtElement) { currentRef, replacement -> replacement(descriptor, currentRef) }
|
||||
.fold(currentRef as KtElement) { currentRef, replacement -> replacement(descriptor, currentRef) }
|
||||
(newRef as? KtSimpleNameExpression)?.resolveResult = resolveResult
|
||||
}
|
||||
|
||||
@@ -565,19 +571,19 @@ fun ExtractionGeneratorConfiguration.generateDeclaration(
|
||||
if (lastExpression is KtReturnExpression) return
|
||||
|
||||
val defaultExpression =
|
||||
if (!generatorOptions.inTempFile && defaultValue != null && descriptor.controlFlow.outputValueBoxer.boxingRequired && lastExpression!!.isMultiLine()) {
|
||||
val varNameValidator = NewDeclarationNameValidator(body, lastExpression, NewDeclarationNameValidator.Target.VARIABLES)
|
||||
val resultVal = KotlinNameSuggester.suggestNamesByType(defaultValue.valueType, varNameValidator, null).first()
|
||||
body.addBefore(psiFactory.createDeclaration("val $resultVal = ${lastExpression.text}"), lastExpression)
|
||||
body.addBefore(psiFactory.createNewLine(), lastExpression)
|
||||
psiFactory.createExpression(resultVal)
|
||||
}
|
||||
else lastExpression
|
||||
if (!generatorOptions.inTempFile && defaultValue != null && descriptor.controlFlow.outputValueBoxer.boxingRequired && lastExpression!!.isMultiLine()) {
|
||||
val varNameValidator = NewDeclarationNameValidator(body, lastExpression, NewDeclarationNameValidator.Target.VARIABLES)
|
||||
val resultVal = KotlinNameSuggester.suggestNamesByType(defaultValue.valueType, varNameValidator, null).first()
|
||||
body.addBefore(psiFactory.createDeclaration("val $resultVal = ${lastExpression.text}"), lastExpression)
|
||||
body.addBefore(psiFactory.createNewLine(), lastExpression)
|
||||
psiFactory.createExpression(resultVal)
|
||||
} else lastExpression
|
||||
|
||||
val returnExpression = descriptor.controlFlow.outputValueBoxer.getReturnExpression(getReturnArguments(defaultExpression), psiFactory) ?: return
|
||||
val returnExpression =
|
||||
descriptor.controlFlow.outputValueBoxer.getReturnExpression(getReturnArguments(defaultExpression), psiFactory) ?: return
|
||||
|
||||
@Suppress("NON_EXHAUSTIVE_WHEN")
|
||||
when(generatorOptions.target) {
|
||||
when (generatorOptions.target) {
|
||||
ExtractionTarget.LAZY_PROPERTY, ExtractionTarget.FAKE_LAMBDALIKE_FUNCTION -> {
|
||||
// In the case of lazy property absence of default value means that output values are of OutputValue.Initializer type
|
||||
// We just add resulting expressions without return, since returns are prohibited in the body of lazy property
|
||||
@@ -614,8 +620,7 @@ fun ExtractionGeneratorConfiguration.generateDeclaration(
|
||||
(targetContainer.addBefore(declaration, anchor) as KtNamedDeclaration).apply {
|
||||
targetContainer.addBefore(emptyLines, anchor)
|
||||
}
|
||||
}
|
||||
else {
|
||||
} else {
|
||||
(targetContainer.addAfter(declaration, anchor) as KtNamedDeclaration).apply {
|
||||
if (!(targetContainer is KtClassBody && (targetContainer.parent as? KtClass)?.isEnum() ?: false)) {
|
||||
targetContainer.addAfter(emptyLines, anchor)
|
||||
@@ -630,7 +635,7 @@ fun ExtractionGeneratorConfiguration.generateDeclaration(
|
||||
val anchor = with(descriptor.extractionData) {
|
||||
val targetParent = targetSibling.parent
|
||||
|
||||
val anchorCandidates = duplicates.mapTo(ArrayList<PsiElement>()) { it.range.elements.first().substringContextOrThis }
|
||||
val anchorCandidates = duplicates.mapTo(arrayListOf()) { it.range.elements.first().substringContextOrThis }
|
||||
anchorCandidates.add(targetSibling)
|
||||
if (targetSibling is KtEnumEntry) {
|
||||
anchorCandidates.add(targetSibling.siblings().last { it is KtEnumEntry })
|
||||
@@ -638,8 +643,7 @@ fun ExtractionGeneratorConfiguration.generateDeclaration(
|
||||
|
||||
val marginalCandidate = if (insertBefore) {
|
||||
anchorCandidates.minBy { it.startOffset }!!
|
||||
}
|
||||
else {
|
||||
} else {
|
||||
anchorCandidates.maxBy { it.startOffset }!!
|
||||
}
|
||||
|
||||
|
||||
+149
-103
@@ -70,12 +70,12 @@ internal class ParametersInfo {
|
||||
}
|
||||
|
||||
internal fun ExtractionData.inferParametersInfo(
|
||||
virtualBlock: KtBlockExpression,
|
||||
commonParent: PsiElement,
|
||||
pseudocode: Pseudocode,
|
||||
bindingContext: BindingContext,
|
||||
targetScope: LexicalScope,
|
||||
modifiedVarDescriptors: Set<VariableDescriptor>
|
||||
virtualBlock: KtBlockExpression,
|
||||
commonParent: PsiElement,
|
||||
pseudocode: Pseudocode,
|
||||
bindingContext: BindingContext,
|
||||
targetScope: LexicalScope,
|
||||
modifiedVarDescriptors: Set<VariableDescriptor>
|
||||
): ParametersInfo {
|
||||
val info = ParametersInfo()
|
||||
|
||||
@@ -95,8 +95,7 @@ internal fun ExtractionData.inferParametersInfo(
|
||||
val extensionReceiver = resolvedCall?.extensionReceiver
|
||||
val receiverToExtract = (if (extensionReceiver == null || isSynthesizedInvoke(refInfo.resolveResult.descriptor)) {
|
||||
resolvedCall?.dispatchReceiver
|
||||
}
|
||||
else {
|
||||
} else {
|
||||
extensionReceiver
|
||||
})
|
||||
|
||||
@@ -105,36 +104,60 @@ internal fun ExtractionData.inferParametersInfo(
|
||||
if (options.canWrapInWith
|
||||
&& twoReceivers
|
||||
&& resolvedCall!!.extensionReceiver is ExpressionReceiver
|
||||
&& DescriptorUtils.isObject(dispatchReceiverDescriptor)) {
|
||||
info.replacementMap.putValue(refInfo.resolveResult.originalRefExpr,
|
||||
WrapObjectInWithReplacement(dispatchReceiverDescriptor as ClassDescriptor))
|
||||
&& DescriptorUtils.isObject(dispatchReceiverDescriptor)
|
||||
) {
|
||||
info.replacementMap.putValue(
|
||||
refInfo.resolveResult.originalRefExpr,
|
||||
WrapObjectInWithReplacement(dispatchReceiverDescriptor as ClassDescriptor)
|
||||
)
|
||||
continue
|
||||
}
|
||||
|
||||
if (!refInfo.shouldSkipPrimaryReceiver) {
|
||||
extractReceiver(receiverToExtract, info, targetScope, refInfo, extractedDescriptorToParameter, pseudocode, bindingContext, false)
|
||||
extractReceiver(
|
||||
receiverToExtract,
|
||||
info,
|
||||
targetScope,
|
||||
refInfo,
|
||||
extractedDescriptorToParameter,
|
||||
pseudocode,
|
||||
bindingContext,
|
||||
false
|
||||
)
|
||||
}
|
||||
|
||||
if (options.canWrapInWith && twoReceivers) {
|
||||
extractReceiver(resolvedCall!!.dispatchReceiver, info, targetScope, refInfo, extractedDescriptorToParameter, pseudocode, bindingContext, true)
|
||||
extractReceiver(
|
||||
resolvedCall!!.dispatchReceiver,
|
||||
info,
|
||||
targetScope,
|
||||
refInfo,
|
||||
extractedDescriptorToParameter,
|
||||
pseudocode,
|
||||
bindingContext,
|
||||
true
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
val varNameValidator = NewDeclarationNameValidator(
|
||||
commonParent.getNonStrictParentOfType<KtExpression>()!!,
|
||||
physicalElements.firstOrNull(),
|
||||
NewDeclarationNameValidator.Target.VARIABLES
|
||||
commonParent.getNonStrictParentOfType<KtExpression>()!!,
|
||||
physicalElements.firstOrNull(),
|
||||
NewDeclarationNameValidator.Target.VARIABLES
|
||||
)
|
||||
|
||||
val existingParameterNames = hashSetOf<String>()
|
||||
for ((descriptorToExtract, parameter) in extractedDescriptorToParameter) {
|
||||
if (!parameter
|
||||
.getParameterType(options.allowSpecialClassNames)
|
||||
.processTypeIfExtractable(info.typeParameters, info.nonDenotableTypes, options, targetScope)) continue
|
||||
.processTypeIfExtractable(info.typeParameters, info.nonDenotableTypes, options, targetScope)
|
||||
) continue
|
||||
|
||||
with (parameter) {
|
||||
with(parameter) {
|
||||
if (currentName == null) {
|
||||
currentName = KotlinNameSuggester.suggestNamesByType(getParameterType(options.allowSpecialClassNames), varNameValidator, "p").first()
|
||||
currentName =
|
||||
KotlinNameSuggester.suggestNamesByType(getParameterType(options.allowSpecialClassNames), varNameValidator, "p")
|
||||
.first()
|
||||
}
|
||||
|
||||
require(currentName != null)
|
||||
@@ -147,7 +170,10 @@ internal fun ExtractionData.inferParametersInfo(
|
||||
currentName = "$currentName$index"
|
||||
}
|
||||
|
||||
mirrorVarName = if (descriptorToExtract in modifiedVarDescriptors) KotlinNameSuggester.suggestNameByName(name, varNameValidator) else null
|
||||
mirrorVarName = if (descriptorToExtract in modifiedVarDescriptors) KotlinNameSuggester.suggestNameByName(
|
||||
name,
|
||||
varNameValidator
|
||||
) else null
|
||||
info.parameters.add(this)
|
||||
currentName?.let { existingParameterNames += it }
|
||||
}
|
||||
@@ -162,14 +188,14 @@ internal fun ExtractionData.inferParametersInfo(
|
||||
}
|
||||
|
||||
private fun ExtractionData.extractReceiver(
|
||||
receiverToExtract: ReceiverValue?,
|
||||
info: ParametersInfo,
|
||||
targetScope: LexicalScope,
|
||||
refInfo: ResolvedReferenceInfo,
|
||||
extractedDescriptorToParameter: HashMap<DeclarationDescriptor, MutableParameter>,
|
||||
pseudocode: Pseudocode,
|
||||
bindingContext: BindingContext,
|
||||
isMemberExtension: Boolean
|
||||
receiverToExtract: ReceiverValue?,
|
||||
info: ParametersInfo,
|
||||
targetScope: LexicalScope,
|
||||
refInfo: ResolvedReferenceInfo,
|
||||
extractedDescriptorToParameter: HashMap<DeclarationDescriptor, MutableParameter>,
|
||||
pseudocode: Pseudocode,
|
||||
bindingContext: BindingContext,
|
||||
isMemberExtension: Boolean
|
||||
) {
|
||||
val (originalRef, originalDeclaration, originalDescriptor, resolvedCall) = refInfo.resolveResult
|
||||
|
||||
@@ -178,14 +204,15 @@ private fun ExtractionData.extractReceiver(
|
||||
val thisExpr = refInfo.refExpr.parent as? KtThisExpression
|
||||
|
||||
if (hasThisReceiver
|
||||
&& DescriptorToSourceUtilsIde.getAllDeclarations(project, thisDescriptor!!).all { it.isInsideOf(physicalElements) }) {
|
||||
&& DescriptorToSourceUtilsIde.getAllDeclarations(project, thisDescriptor!!).all { it.isInsideOf(physicalElements) }
|
||||
) {
|
||||
return
|
||||
}
|
||||
|
||||
val referencedClassifierDescriptor: ClassifierDescriptor? = (thisDescriptor ?: originalDescriptor).let {
|
||||
when (it) {
|
||||
is ClassDescriptor ->
|
||||
when(it.kind) {
|
||||
when (it.kind) {
|
||||
ClassKind.OBJECT, ClassKind.ENUM_CLASS -> it
|
||||
ClassKind.ENUM_ENTRY -> it.containingDeclaration as? ClassDescriptor
|
||||
else -> if (refInfo.refExpr.getNonStrictParentOfType<KtTypeReference>() != null) it else null
|
||||
@@ -202,29 +229,30 @@ private fun ExtractionData.extractReceiver(
|
||||
if (referencedClassifierDescriptor != null) {
|
||||
if (!referencedClassifierDescriptor.defaultType.processTypeIfExtractable(
|
||||
info.typeParameters, info.nonDenotableTypes, options, targetScope, referencedClassifierDescriptor is TypeParameterDescriptor
|
||||
)) return
|
||||
)
|
||||
) return
|
||||
|
||||
if (options.canWrapInWith
|
||||
&& resolvedCall != null
|
||||
&& resolvedCall.hasBothReceivers()
|
||||
&& DescriptorUtils.isObject(referencedClassifierDescriptor)) {
|
||||
&& DescriptorUtils.isObject(referencedClassifierDescriptor)
|
||||
) {
|
||||
info.replacementMap.putValue(originalRef, WrapObjectInWithReplacement(referencedClassifierDescriptor as ClassDescriptor))
|
||||
} else if (referencedClassifierDescriptor is ClassDescriptor) {
|
||||
info.replacementMap.putValue(originalRef, FqNameReplacement(originalDescriptor.getImportableDescriptor().fqNameSafe))
|
||||
}
|
||||
}
|
||||
else {
|
||||
} else {
|
||||
val extractThis = (hasThisReceiver && refInfo.smartCast == null) || thisExpr != null
|
||||
val extractOrdinaryParameter =
|
||||
originalDeclaration is KtDestructuringDeclarationEntry ||
|
||||
originalDeclaration is KtProperty ||
|
||||
originalDeclaration is KtParameter
|
||||
originalDeclaration is KtDestructuringDeclarationEntry ||
|
||||
originalDeclaration is KtProperty ||
|
||||
originalDeclaration is KtParameter
|
||||
|
||||
val extractFunctionRef =
|
||||
options.captureLocalFunctions
|
||||
&& originalRef.getReferencedName() == originalDescriptor.name.asString() // to forbid calls by convention
|
||||
&& originalDeclaration is KtNamedFunction && originalDeclaration.isLocal
|
||||
&& targetScope.findFunction(originalDescriptor.name, NoLookupLocation.FROM_IDE) { it == originalDescriptor } == null
|
||||
options.captureLocalFunctions
|
||||
&& originalRef.getReferencedName() == originalDescriptor.name.asString() // to forbid calls by convention
|
||||
&& originalDeclaration is KtNamedFunction && originalDeclaration.isLocal
|
||||
&& targetScope.findFunction(originalDescriptor.name, NoLookupLocation.FROM_IDE) { it == originalDescriptor } == null
|
||||
|
||||
val descriptorToExtract = (if (extractThis) thisDescriptor else null) ?: originalDescriptor
|
||||
|
||||
@@ -240,32 +268,46 @@ private fun ExtractionData.extractReceiver(
|
||||
else -> (originalRef.parent as? KtThisExpression) ?: originalRef
|
||||
}
|
||||
|
||||
val parameterType = suggestParameterType(extractFunctionRef, originalDescriptor, parameterExpression, receiverToExtract, resolvedCall, true, bindingContext)
|
||||
val parameterType = suggestParameterType(
|
||||
extractFunctionRef,
|
||||
originalDescriptor,
|
||||
parameterExpression,
|
||||
receiverToExtract,
|
||||
resolvedCall,
|
||||
true,
|
||||
bindingContext
|
||||
)
|
||||
|
||||
val parameter = extractedDescriptorToParameter.getOrPut(descriptorToExtract) {
|
||||
var argumentText =
|
||||
if (hasThisReceiver && extractThis) {
|
||||
val label = if (descriptorToExtract is ClassDescriptor) "@${descriptorToExtract.name.asString()}" else ""
|
||||
"this$label"
|
||||
}
|
||||
else {
|
||||
val argumentExpr = (thisExpr ?: refInfo.refExpr).getQualifiedExpressionForSelectorOrThis()
|
||||
if (argumentExpr is KtOperationReferenceExpression) {
|
||||
val nameElement = argumentExpr.getReferencedNameElement()
|
||||
val nameElementType = nameElement.node.elementType
|
||||
(nameElementType as? KtToken)?.let {
|
||||
OperatorConventions.getNameForOperationSymbol(it)?.asString()
|
||||
} ?: nameElement.text
|
||||
}
|
||||
else argumentExpr.text
|
||||
?: throw AssertionError("reference shouldn't be empty: code fragment = $codeFragmentText")
|
||||
}
|
||||
if (hasThisReceiver && extractThis) {
|
||||
val label = if (descriptorToExtract is ClassDescriptor) "@${descriptorToExtract.name.asString()}" else ""
|
||||
"this$label"
|
||||
} else {
|
||||
val argumentExpr = (thisExpr ?: refInfo.refExpr).getQualifiedExpressionForSelectorOrThis()
|
||||
if (argumentExpr is KtOperationReferenceExpression) {
|
||||
val nameElement = argumentExpr.getReferencedNameElement()
|
||||
val nameElementType = nameElement.node.elementType
|
||||
(nameElementType as? KtToken)?.let {
|
||||
OperatorConventions.getNameForOperationSymbol(it)?.asString()
|
||||
} ?: nameElement.text
|
||||
} else argumentExpr.text
|
||||
?: throw AssertionError("reference shouldn't be empty: code fragment = $codeFragmentText")
|
||||
}
|
||||
if (extractFunctionRef) {
|
||||
val receiverTypeText = (originalDeclaration as KtCallableDeclaration).receiverTypeReference?.text ?: ""
|
||||
argumentText = "$receiverTypeText::$argumentText"
|
||||
}
|
||||
|
||||
val originalType = suggestParameterType(extractFunctionRef, originalDescriptor, parameterExpression, receiverToExtract, resolvedCall, false, bindingContext)
|
||||
val originalType = suggestParameterType(
|
||||
extractFunctionRef,
|
||||
originalDescriptor,
|
||||
parameterExpression,
|
||||
receiverToExtract,
|
||||
resolvedCall,
|
||||
false,
|
||||
bindingContext
|
||||
)
|
||||
|
||||
MutableParameter(argumentText, descriptorToExtract, extractThis, targetScope, originalType, refInfo.possibleTypes)
|
||||
}
|
||||
@@ -287,13 +329,17 @@ private fun ExtractionData.extractReceiver(
|
||||
val instruction = pseudocode.getElementValue(callElement)?.createdAt as? InstructionWithReceivers
|
||||
val receiverValue = instruction?.receiverValues?.entries?.singleOrNull { it.value == receiverToExtract }?.key
|
||||
if (receiverValue != null) {
|
||||
parameter.addTypePredicate(getExpectedTypePredicate(receiverValue, bindingContext, targetScope.ownerDescriptor.builtIns))
|
||||
parameter.addTypePredicate(
|
||||
getExpectedTypePredicate(
|
||||
receiverValue,
|
||||
bindingContext,
|
||||
targetScope.ownerDescriptor.builtIns
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
else if (extractFunctionRef) {
|
||||
} else if (extractFunctionRef) {
|
||||
parameter.addTypePredicate(SingleType(parameterType))
|
||||
}
|
||||
else {
|
||||
} else {
|
||||
pseudocode.getElementValuesRecursively(originalRef).forEach {
|
||||
parameter.addTypePredicate(getExpectedTypePredicate(it, bindingContext, targetScope.ownerDescriptor.builtIns))
|
||||
}
|
||||
@@ -310,50 +356,50 @@ private fun ExtractionData.extractReceiver(
|
||||
}
|
||||
|
||||
private fun suggestParameterType(
|
||||
extractFunctionRef: Boolean,
|
||||
originalDescriptor: DeclarationDescriptor,
|
||||
parameterExpression: KtExpression?,
|
||||
receiverToExtract: ReceiverValue?,
|
||||
resolvedCall: ResolvedCall<*>?,
|
||||
useSmartCastsIfPossible: Boolean, bindingContext: BindingContext
|
||||
extractFunctionRef: Boolean,
|
||||
originalDescriptor: DeclarationDescriptor,
|
||||
parameterExpression: KtExpression?,
|
||||
receiverToExtract: ReceiverValue?,
|
||||
resolvedCall: ResolvedCall<*>?,
|
||||
useSmartCastsIfPossible: Boolean, bindingContext: BindingContext
|
||||
): KotlinType {
|
||||
val builtIns = originalDescriptor.builtIns
|
||||
return when {
|
||||
extractFunctionRef -> {
|
||||
originalDescriptor as FunctionDescriptor
|
||||
createFunctionType(
|
||||
builtIns,
|
||||
Annotations.EMPTY,
|
||||
originalDescriptor.extensionReceiverParameter?.type,
|
||||
originalDescriptor.valueParameters.map { it.type },
|
||||
originalDescriptor.valueParameters.map { it.name },
|
||||
originalDescriptor.returnType ?: builtIns.defaultReturnType
|
||||
)
|
||||
}
|
||||
extractFunctionRef -> {
|
||||
originalDescriptor as FunctionDescriptor
|
||||
createFunctionType(
|
||||
builtIns,
|
||||
Annotations.EMPTY,
|
||||
originalDescriptor.extensionReceiverParameter?.type,
|
||||
originalDescriptor.valueParameters.map { it.type },
|
||||
originalDescriptor.valueParameters.map { it.name },
|
||||
originalDescriptor.returnType ?: builtIns.defaultReturnType
|
||||
)
|
||||
}
|
||||
|
||||
parameterExpression != null ->
|
||||
(if (useSmartCastsIfPossible) bindingContext[BindingContext.SMARTCAST, parameterExpression]?.defaultType else null)
|
||||
?: bindingContext.getType(parameterExpression)
|
||||
?: (parameterExpression as? KtReferenceExpression)?.let {
|
||||
(bindingContext[BindingContext.REFERENCE_TARGET, it] as? CallableDescriptor)?.returnType
|
||||
}
|
||||
?: receiverToExtract?.type
|
||||
parameterExpression != null ->
|
||||
(if (useSmartCastsIfPossible) bindingContext[BindingContext.SMARTCAST, parameterExpression]?.defaultType else null)
|
||||
?: bindingContext.getType(parameterExpression)
|
||||
?: (parameterExpression as? KtReferenceExpression)?.let {
|
||||
(bindingContext[BindingContext.REFERENCE_TARGET, it] as? CallableDescriptor)?.returnType
|
||||
}
|
||||
?: receiverToExtract?.type
|
||||
|
||||
receiverToExtract is ImplicitReceiver -> {
|
||||
val typeByDataFlowInfo = if (useSmartCastsIfPossible) {
|
||||
val callElement = resolvedCall!!.call.callElement
|
||||
val dataFlowInfo = bindingContext.getDataFlowInfoAfter(callElement)
|
||||
receiverToExtract is ImplicitReceiver -> {
|
||||
val typeByDataFlowInfo = if (useSmartCastsIfPossible) {
|
||||
val callElement = resolvedCall!!.call.callElement
|
||||
val dataFlowInfo = bindingContext.getDataFlowInfoAfter(callElement)
|
||||
|
||||
val dataFlowValueFactory = callElement.getResolutionFacade().frontendService<DataFlowValueFactory>()
|
||||
val possibleTypes = dataFlowInfo.getCollectedTypes(
|
||||
dataFlowValueFactory.createDataFlowValueForStableReceiver(receiverToExtract),
|
||||
callElement.languageVersionSettings
|
||||
)
|
||||
if (possibleTypes.isNotEmpty()) CommonSupertypes.commonSupertype(possibleTypes) else null
|
||||
} else null
|
||||
typeByDataFlowInfo ?: receiverToExtract.type
|
||||
}
|
||||
val dataFlowValueFactory = callElement.getResolutionFacade().frontendService<DataFlowValueFactory>()
|
||||
val possibleTypes = dataFlowInfo.getCollectedTypes(
|
||||
dataFlowValueFactory.createDataFlowValueForStableReceiver(receiverToExtract),
|
||||
callElement.languageVersionSettings
|
||||
)
|
||||
if (possibleTypes.isNotEmpty()) CommonSupertypes.commonSupertype(possibleTypes) else null
|
||||
} else null
|
||||
typeByDataFlowInfo ?: receiverToExtract.type
|
||||
}
|
||||
|
||||
else -> receiverToExtract?.type
|
||||
} ?: builtIns.defaultParameterType
|
||||
else -> receiverToExtract?.type
|
||||
} ?: builtIns.defaultParameterType
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user