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