Extract Function: Approximate non-resolvable types with nearest resolvable supertype when possible
#KT-7120 Fixed
This commit is contained in:
@@ -20,15 +20,12 @@ import com.google.common.base.Function;
|
||||
import com.google.common.collect.Collections2;
|
||||
import com.google.common.collect.Lists;
|
||||
import com.intellij.psi.PsiElement;
|
||||
import com.intellij.psi.util.PsiTreeUtil;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
import org.jetbrains.annotations.TestOnly;
|
||||
import org.jetbrains.kotlin.analyzer.AnalysisResult;
|
||||
import org.jetbrains.kotlin.descriptors.*;
|
||||
import org.jetbrains.kotlin.psi.JetClassBody;
|
||||
import org.jetbrains.kotlin.psi.JetClassOrObject;
|
||||
import org.jetbrains.kotlin.psi.JetExpression;
|
||||
import org.jetbrains.kotlin.psi.JetFile;
|
||||
import org.jetbrains.kotlin.psi.*;
|
||||
import org.jetbrains.kotlin.resolve.BindingContext;
|
||||
import org.jetbrains.kotlin.resolve.BindingTrace;
|
||||
import org.jetbrains.kotlin.resolve.TraceBasedRedeclarationHandler;
|
||||
@@ -158,12 +155,12 @@ public final class JetScopeUtils {
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public static JetScope getResolutionScope(@NotNull JetExpression expression, @NotNull AnalysisResult analysisResult) {
|
||||
PsiElement parent = expression.getParent();
|
||||
public static JetScope getResolutionScope(@NotNull PsiElement element, @NotNull BindingContext context) {
|
||||
PsiElement parent = element.getParent();
|
||||
|
||||
if (parent instanceof JetClassBody) {
|
||||
JetClassOrObject classOrObject = (JetClassOrObject) parent.getParent();
|
||||
ClassDescriptor classDescriptor = analysisResult.getBindingContext().get(BindingContext.CLASS, classOrObject);
|
||||
ClassDescriptor classDescriptor = context.get(BindingContext.CLASS, classOrObject);
|
||||
if (classDescriptor instanceof ClassDescriptorWithResolutionScopes) {
|
||||
return ((ClassDescriptorWithResolutionScopes) classDescriptor).getScopeForMemberDeclarationResolution();
|
||||
}
|
||||
@@ -171,10 +168,14 @@ public final class JetScopeUtils {
|
||||
}
|
||||
|
||||
if (parent instanceof JetFile) {
|
||||
PackageViewDescriptor packageView = analysisResult.getModuleDescriptor().getPackage(((JetFile) parent).getPackageFqName());
|
||||
PackageFragmentDescriptor packageFragment = context.get(BindingContext.FILE_TO_PACKAGE_FRAGMENT, (JetFile) parent);
|
||||
if (packageFragment == null) return null;
|
||||
|
||||
PackageViewDescriptor packageView = packageFragment.getContainingDeclaration().getPackage(((JetFile) parent).getPackageFqName());
|
||||
return packageView != null ? packageView.getMemberScope() : null;
|
||||
}
|
||||
|
||||
return analysisResult.getBindingContext().get(BindingContext.RESOLUTION_SCOPE, expression);
|
||||
JetExpression expression = PsiTreeUtil.getParentOfType(element, JetExpression.class, false);
|
||||
return expression != null ? context.get(BindingContext.RESOLUTION_SCOPE, expression) : null;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -20,7 +20,10 @@ import org.jetbrains.kotlin.types.*
|
||||
import org.jetbrains.kotlin.builtins.KotlinBuiltIns
|
||||
import org.jetbrains.kotlin.resolve.jvm.kotlinSignature.CollectionClassMapping
|
||||
import org.jetbrains.kotlin.descriptors.ClassDescriptor
|
||||
import org.jetbrains.kotlin.descriptors.TypeParameterDescriptor
|
||||
import org.jetbrains.kotlin.idea.imports.canBeReferencedViaImport
|
||||
import org.jetbrains.kotlin.load.java.JvmAnnotationNames
|
||||
import org.jetbrains.kotlin.resolve.scopes.JetScope
|
||||
import java.util.LinkedHashSet
|
||||
import org.jetbrains.kotlin.types.typeUtil.substitute
|
||||
|
||||
@@ -87,3 +90,19 @@ public fun JetType.nullability(): TypeNullability {
|
||||
else -> TypeNullability.NOT_NULL
|
||||
}
|
||||
}
|
||||
|
||||
fun JetType.isResolvableInScope(scope: JetScope?, checkTypeParameters: Boolean): Boolean {
|
||||
if (canBeReferencedViaImport()) return true
|
||||
|
||||
val descriptor = getConstructor().getDeclarationDescriptor()
|
||||
if (descriptor == null || descriptor.getName().isSpecial()) return false
|
||||
if (!checkTypeParameters && descriptor is TypeParameterDescriptor) return true
|
||||
|
||||
return scope != null && scope.getClassifier(descriptor.getName()) == descriptor
|
||||
}
|
||||
|
||||
public fun JetType.approximateWithResolvableType(scope: JetScope?, checkTypeParameters: Boolean): JetType {
|
||||
if (isError() || isResolvableInScope(scope, checkTypeParameters)) return this
|
||||
return supertypes().firstOrNull { it.isResolvableInScope(scope, checkTypeParameters) }
|
||||
?: KotlinBuiltIns.getInstance().getAnyType()
|
||||
}
|
||||
+7
-38
@@ -20,21 +20,15 @@ import com.intellij.codeInsight.intention.IntentionAction;
|
||||
import com.intellij.openapi.diagnostic.Logger;
|
||||
import com.intellij.psi.PsiElement;
|
||||
import com.intellij.psi.util.PsiTreeUtil;
|
||||
import kotlin.Function1;
|
||||
import kotlin.KotlinPackage;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
import org.jetbrains.kotlin.analyzer.AnalysisResult;
|
||||
import org.jetbrains.kotlin.builtins.KotlinBuiltIns;
|
||||
import org.jetbrains.kotlin.descriptors.CallableDescriptor;
|
||||
import org.jetbrains.kotlin.descriptors.ClassifierDescriptor;
|
||||
import org.jetbrains.kotlin.diagnostics.Diagnostic;
|
||||
import org.jetbrains.kotlin.diagnostics.DiagnosticWithParameters1;
|
||||
import org.jetbrains.kotlin.diagnostics.DiagnosticWithParameters2;
|
||||
import org.jetbrains.kotlin.diagnostics.Errors;
|
||||
import org.jetbrains.kotlin.diagnostics.rendering.DefaultErrorMessages;
|
||||
import org.jetbrains.kotlin.idea.caches.resolve.ResolvePackage;
|
||||
import org.jetbrains.kotlin.idea.imports.ImportsPackage;
|
||||
import org.jetbrains.kotlin.idea.util.UtilPackage;
|
||||
import org.jetbrains.kotlin.psi.*;
|
||||
import org.jetbrains.kotlin.resolve.BindingContext;
|
||||
@@ -44,7 +38,6 @@ import org.jetbrains.kotlin.resolve.calls.model.ResolvedCall;
|
||||
import org.jetbrains.kotlin.resolve.scopes.JetScope;
|
||||
import org.jetbrains.kotlin.resolve.scopes.JetScopeUtils;
|
||||
import org.jetbrains.kotlin.types.JetType;
|
||||
import org.jetbrains.kotlin.types.TypeUtils;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.LinkedList;
|
||||
@@ -54,36 +47,12 @@ import java.util.List;
|
||||
public class QuickFixFactoryForTypeMismatchError extends JetIntentionActionsFactory {
|
||||
private final static Logger LOG = Logger.getInstance(QuickFixFactoryForTypeMismatchError.class);
|
||||
|
||||
private static boolean isResolvableType(@NotNull JetType type, @Nullable JetScope scope) {
|
||||
if (ImportsPackage.canBeReferencedViaImport(type)) return true;
|
||||
|
||||
ClassifierDescriptor descriptor = type.getConstructor().getDeclarationDescriptor();
|
||||
if (descriptor == null || descriptor.getName().isSpecial()) return false;
|
||||
|
||||
return scope != null && scope.getClassifier(descriptor.getName()) == descriptor;
|
||||
}
|
||||
|
||||
private static JetType approximateWithResolvableType(@NotNull JetType type, @Nullable final JetScope scope) {
|
||||
if (isResolvableType(type, scope)) return type;
|
||||
JetType superType = KotlinPackage.firstOrNull(
|
||||
TypeUtils.getAllSupertypes(type),
|
||||
new Function1<JetType, Boolean>() {
|
||||
@Override
|
||||
public Boolean invoke(JetType type) {
|
||||
return isResolvableType(type, scope);
|
||||
}
|
||||
}
|
||||
);
|
||||
return superType != null ? superType : KotlinBuiltIns.getInstance().getAnyType();
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
protected List<IntentionAction> doCreateActions(@NotNull Diagnostic diagnostic) {
|
||||
List<IntentionAction> actions = new LinkedList<IntentionAction>();
|
||||
|
||||
AnalysisResult analysisResult = ResolvePackage.analyzeFullyAndGetResult((JetFile) diagnostic.getPsiFile());
|
||||
BindingContext context = analysisResult.getBindingContext();
|
||||
BindingContext context = ResolvePackage.analyzeFully((JetFile) diagnostic.getPsiFile());
|
||||
|
||||
PsiElement diagnosticElement = diagnostic.getPsiElement();
|
||||
if (!(diagnosticElement instanceof JetExpression)) {
|
||||
@@ -133,8 +102,8 @@ public class QuickFixFactoryForTypeMismatchError extends JetIntentionActionsFact
|
||||
JetExpression initializer = property.getInitializer();
|
||||
if (QuickFixUtil.canEvaluateTo(initializer, expression) ||
|
||||
(getter != null && QuickFixUtil.canFunctionOrGetterReturnExpression(property.getGetter(), expression))) {
|
||||
JetScope scope = JetScopeUtils.getResolutionScope(property, analysisResult);
|
||||
JetType typeToInsert = approximateWithResolvableType(expressionType, scope);
|
||||
JetScope scope = JetScopeUtils.getResolutionScope(property, context);
|
||||
JetType typeToInsert = UtilPackage.approximateWithResolvableType(expressionType, scope, false);
|
||||
actions.add(new ChangeVariableTypeFix(property, typeToInsert));
|
||||
}
|
||||
}
|
||||
@@ -147,8 +116,8 @@ public class QuickFixFactoryForTypeMismatchError extends JetIntentionActionsFact
|
||||
? BindingContextUtilPackage.getTargetFunction((JetReturnExpression) expressionParent, context)
|
||||
: PsiTreeUtil.getParentOfType(expression, JetFunction.class, true);
|
||||
if (function instanceof JetFunction && QuickFixUtil.canFunctionOrGetterReturnExpression(function, expression)) {
|
||||
JetScope scope = JetScopeUtils.getResolutionScope(function, analysisResult);
|
||||
JetType typeToInsert = approximateWithResolvableType(expressionType, scope);
|
||||
JetScope scope = JetScopeUtils.getResolutionScope(function, context);
|
||||
JetType typeToInsert = UtilPackage.approximateWithResolvableType(expressionType, scope, false);
|
||||
actions.add(new ChangeFunctionReturnTypeFix((JetFunction) function, typeToInsert));
|
||||
}
|
||||
|
||||
@@ -187,8 +156,8 @@ public class QuickFixFactoryForTypeMismatchError extends JetIntentionActionsFact
|
||||
? expressionType
|
||||
: context.get(BindingContext.EXPRESSION_TYPE, valueArgument.getArgumentExpression());
|
||||
if (correspondingParameter != null && valueArgumentType != null) {
|
||||
JetScope scope = JetScopeUtils.getResolutionScope(valueArgument.getArgumentExpression(), analysisResult);
|
||||
JetType typeToInsert = approximateWithResolvableType(valueArgumentType, scope);
|
||||
JetScope scope = JetScopeUtils.getResolutionScope(valueArgument.getArgumentExpression(), context);
|
||||
JetType typeToInsert = UtilPackage.approximateWithResolvableType(valueArgumentType, scope, true);
|
||||
actions.add(new ChangeParameterTypeFix(correspondingParameter, typeToInsert));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -19,14 +19,11 @@ package org.jetbrains.kotlin.idea.refactoring;
|
||||
import com.intellij.openapi.util.Ref;
|
||||
import com.intellij.psi.PsiElement;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
import org.jetbrains.kotlin.analyzer.AnalysisResult;
|
||||
import org.jetbrains.kotlin.descriptors.ClassDescriptor;
|
||||
import org.jetbrains.kotlin.descriptors.ClassDescriptorWithResolutionScopes;
|
||||
import org.jetbrains.kotlin.descriptors.PackageViewDescriptor;
|
||||
import org.jetbrains.kotlin.idea.caches.resolve.ResolvePackage;
|
||||
import org.jetbrains.kotlin.name.Name;
|
||||
import org.jetbrains.kotlin.psi.*;
|
||||
import org.jetbrains.kotlin.psi.JetElement;
|
||||
import org.jetbrains.kotlin.psi.JetExpression;
|
||||
import org.jetbrains.kotlin.psi.JetVisitorVoid;
|
||||
import org.jetbrains.kotlin.resolve.BindingContext;
|
||||
import org.jetbrains.kotlin.resolve.scopes.JetScope;
|
||||
import org.jetbrains.kotlin.resolve.scopes.JetScopeUtils;
|
||||
@@ -76,7 +73,7 @@ public class JetNameValidatorImpl extends JetNameValidator {
|
||||
private boolean checkElement(String name, PsiElement sibling, final Set<JetScope> visitedScopes) {
|
||||
if (!(sibling instanceof JetElement)) return true;
|
||||
|
||||
final AnalysisResult analysisResult = ResolvePackage.analyzeAndGetResult((JetElement) sibling);
|
||||
final BindingContext context = ResolvePackage.analyze((JetElement) sibling);
|
||||
final Name identifier = Name.identifier(name);
|
||||
|
||||
final Ref<Boolean> result = new Ref<Boolean>(true);
|
||||
@@ -90,7 +87,7 @@ public class JetNameValidatorImpl extends JetNameValidator {
|
||||
|
||||
@Override
|
||||
public void visitExpression(@NotNull JetExpression expression) {
|
||||
JetScope resolutionScope = JetScopeUtils.getResolutionScope(expression, analysisResult);
|
||||
JetScope resolutionScope = JetScopeUtils.getResolutionScope(expression, context);
|
||||
|
||||
if (resolutionScope != null) {
|
||||
if (!visitedScopes.add(resolutionScope)) return;
|
||||
|
||||
+32
-16
@@ -41,7 +41,6 @@ import org.jetbrains.kotlin.diagnostics.Errors
|
||||
import org.jetbrains.kotlin.idea.caches.resolve.analyzeFully
|
||||
import org.jetbrains.kotlin.idea.caches.resolve.findModuleDescriptor
|
||||
import org.jetbrains.kotlin.idea.codeInsight.DescriptorToSourceUtilsIde
|
||||
import org.jetbrains.kotlin.idea.imports.canBeReferencedViaImport
|
||||
import org.jetbrains.kotlin.idea.imports.importableFqNameSafe
|
||||
import org.jetbrains.kotlin.idea.refactoring.*
|
||||
import org.jetbrains.kotlin.idea.refactoring.introduce.extractionEngine.AnalysisResult.ErrorMessage
|
||||
@@ -52,6 +51,8 @@ import org.jetbrains.kotlin.idea.refactoring.introduce.extractionEngine.OutputVa
|
||||
import org.jetbrains.kotlin.idea.refactoring.introduce.extractionEngine.OutputValue.ParameterUpdate
|
||||
import org.jetbrains.kotlin.idea.refactoring.introduce.extractionEngine.OutputValueBoxer.AsList
|
||||
import org.jetbrains.kotlin.idea.util.IdeDescriptorRenderers
|
||||
import org.jetbrains.kotlin.idea.util.approximateWithResolvableType
|
||||
import org.jetbrains.kotlin.idea.util.isResolvableInScope
|
||||
import org.jetbrains.kotlin.idea.util.makeNullable
|
||||
import org.jetbrains.kotlin.psi.*
|
||||
import org.jetbrains.kotlin.psi.psiUtil.getNonStrictParentOfType
|
||||
@@ -63,6 +64,8 @@ import org.jetbrains.kotlin.resolve.DescriptorToSourceUtils
|
||||
import org.jetbrains.kotlin.resolve.bindingContextUtil.isUsedAsStatement
|
||||
import org.jetbrains.kotlin.resolve.calls.CallTransformer
|
||||
import org.jetbrains.kotlin.resolve.calls.callUtil.getCalleeExpressionIfAny
|
||||
import org.jetbrains.kotlin.resolve.scopes.JetScope
|
||||
import org.jetbrains.kotlin.resolve.scopes.JetScopeUtils
|
||||
import org.jetbrains.kotlin.resolve.scopes.receivers.ReceiverValue
|
||||
import org.jetbrains.kotlin.resolve.scopes.receivers.ThisReceiver
|
||||
import org.jetbrains.kotlin.types.*
|
||||
@@ -127,7 +130,9 @@ private fun List<Instruction>.getExitPoints(): List<Instruction> =
|
||||
|
||||
private fun List<Instruction>.getResultTypeAndExpressions(
|
||||
bindingContext: BindingContext,
|
||||
options: ExtractionOptions): Pair<JetType, List<JetExpression>> {
|
||||
targetScope: JetScope?,
|
||||
options: ExtractionOptions
|
||||
): Pair<JetType, List<JetExpression>> {
|
||||
fun instructionToExpression(instruction: Instruction, unwrapReturn: Boolean): JetExpression? {
|
||||
return when (instruction) {
|
||||
is ReturnValueInstruction ->
|
||||
@@ -148,7 +153,9 @@ private fun List<Instruction>.getResultTypeAndExpressions(
|
||||
}
|
||||
|
||||
val resultTypes = map(::instructionToType).filterNotNull()
|
||||
val resultType = if (resultTypes.isNotEmpty()) CommonSupertypes.commonSupertype(resultTypes) else DEFAULT_RETURN_TYPE
|
||||
var commonSupertype = if (resultTypes.isNotEmpty()) CommonSupertypes.commonSupertype(resultTypes) else DEFAULT_RETURN_TYPE
|
||||
val resultType = if (options.allowSpecialClassNames) commonSupertype else commonSupertype.approximateWithResolvableType(targetScope, false)
|
||||
|
||||
val expressions = map { instructionToExpression(it, false) }.filterNotNull()
|
||||
|
||||
return resultType to expressions
|
||||
@@ -217,6 +224,7 @@ private fun ExtractionData.analyzeControlFlow(
|
||||
bindingContext: BindingContext,
|
||||
modifiedVarDescriptors: Map<VariableDescriptor, List<JetExpression>>,
|
||||
options: ExtractionOptions,
|
||||
targetScope: JetScope?,
|
||||
parameters: Set<Parameter>
|
||||
): Pair<ControlFlow, ErrorMessage?> {
|
||||
val exitPoints = localInstructions.getExitPoints()
|
||||
@@ -269,8 +277,8 @@ private fun ExtractionData.analyzeControlFlow(
|
||||
val nonLocallyUsedDeclarations = getLocalDeclarationsWithNonLocalUsages(pseudocode, localInstructions, bindingContext)
|
||||
val (declarationsToCopy, declarationsToReport) = nonLocallyUsedDeclarations.partition { it is JetProperty && it.isLocal() }
|
||||
|
||||
val (typeOfDefaultFlow, defaultResultExpressions) = defaultExits.getResultTypeAndExpressions(bindingContext, options)
|
||||
val (returnValueType, valuedReturnExpressions) = valuedReturnExits.getResultTypeAndExpressions(bindingContext, options)
|
||||
val (typeOfDefaultFlow, defaultResultExpressions) = defaultExits.getResultTypeAndExpressions(bindingContext, targetScope, options)
|
||||
val (returnValueType, valuedReturnExpressions) = valuedReturnExits.getResultTypeAndExpressions(bindingContext, targetScope, options)
|
||||
|
||||
val emptyControlFlow =
|
||||
ControlFlow(Collections.emptyList(), { OutputValueBoxer.AsTuple(it, module) }, declarationsToCopy)
|
||||
@@ -421,14 +429,14 @@ fun TypeParameter.collectReferencedTypes(bindingContext: BindingContext): List<J
|
||||
.filterNotNull()
|
||||
}
|
||||
|
||||
private fun JetType.isExtractable(): Boolean {
|
||||
private fun JetType.isExtractable(targetScope: JetScope?): Boolean {
|
||||
return collectReferencedTypes(true).fold(true) { (extractable, typeToCheck) ->
|
||||
val parameterTypeDescriptor = typeToCheck.getConstructor().getDeclarationDescriptor() as? TypeParameterDescriptor
|
||||
val typeParameter = parameterTypeDescriptor?.let {
|
||||
DescriptorToSourceUtils.descriptorToDeclaration(it)
|
||||
} as? JetTypeParameter
|
||||
|
||||
extractable && (typeParameter != null || typeToCheck.canBeReferencedViaImport())
|
||||
extractable && (typeParameter != null || typeToCheck.isResolvableInScope(targetScope, false))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -436,6 +444,7 @@ private fun JetType.processTypeIfExtractable(
|
||||
typeParameters: MutableSet<TypeParameter>,
|
||||
nonDenotableTypes: MutableSet<JetType>,
|
||||
options: ExtractionOptions,
|
||||
targetScope: JetScope?,
|
||||
processTypeArguments: Boolean = true
|
||||
): Boolean {
|
||||
return collectReferencedTypes(processTypeArguments).fold(true) { (extractable, typeToCheck) ->
|
||||
@@ -450,7 +459,7 @@ private fun JetType.processTypeIfExtractable(
|
||||
extractable
|
||||
}
|
||||
|
||||
typeToCheck.canBeReferencedViaImport() ->
|
||||
typeToCheck.isResolvableInScope(targetScope, false) ->
|
||||
extractable
|
||||
|
||||
options.allowSpecialClassNames && typeToCheck.isSpecial() ->
|
||||
@@ -470,7 +479,8 @@ private fun JetType.processTypeIfExtractable(
|
||||
private class MutableParameter(
|
||||
override val argumentText: String,
|
||||
override val originalDescriptor: DeclarationDescriptor,
|
||||
override val receiverCandidate: Boolean
|
||||
override val receiverCandidate: Boolean,
|
||||
private val targetScope: JetScope?
|
||||
): Parameter {
|
||||
// All modifications happen in the same thread
|
||||
private var writable: Boolean = true
|
||||
@@ -525,7 +535,7 @@ private class MutableParameter(
|
||||
|
||||
override fun getParameterTypeCandidates(allowSpecialClassNames: Boolean): List<JetType> {
|
||||
return if (!allowSpecialClassNames) {
|
||||
parameterTypeCandidates.filter { it.isExtractable() }
|
||||
parameterTypeCandidates.filter { it.isExtractable(targetScope) }
|
||||
} else {
|
||||
parameterTypeCandidates
|
||||
}
|
||||
@@ -560,6 +570,7 @@ private fun ExtractionData.inferParametersInfo(
|
||||
commonParent: PsiElement,
|
||||
pseudocode: Pseudocode,
|
||||
bindingContext: BindingContext,
|
||||
targetScope: JetScope?,
|
||||
modifiedVarDescriptors: Set<VariableDescriptor>
|
||||
): ParametersInfo {
|
||||
val info = ParametersInfo()
|
||||
@@ -610,7 +621,7 @@ private fun ExtractionData.inferParametersInfo(
|
||||
|
||||
if (referencedClassDescriptor != null) {
|
||||
if (!referencedClassDescriptor.getDefaultType().processTypeIfExtractable(
|
||||
info.typeParameters, info.nonDenotableTypes, options, false
|
||||
info.typeParameters, info.nonDenotableTypes, options, targetScope, false
|
||||
)) continue
|
||||
|
||||
info.replacementMap[refInfo.offsetInBody] = FqNameReplacement(originalDescriptor.importableFqNameSafe)
|
||||
@@ -642,7 +653,7 @@ private fun ExtractionData.inferParametersInfo(
|
||||
else
|
||||
(thisExpr ?: ref).getText() ?: throw AssertionError("'this' reference shouldn't be empty: code fragment = $codeFragmentText")
|
||||
|
||||
MutableParameter(argumentText, descriptorToExtract, extractThis)
|
||||
MutableParameter(argumentText, descriptorToExtract, extractThis, targetScope)
|
||||
}
|
||||
|
||||
if (!extractThis) {
|
||||
@@ -681,7 +692,9 @@ private fun ExtractionData.inferParametersInfo(
|
||||
)
|
||||
|
||||
for ((descriptorToExtract, parameter) in extractedDescriptorToParameter) {
|
||||
if (!parameter.getParameterType(options.allowSpecialClassNames).processTypeIfExtractable(info.typeParameters, info.nonDenotableTypes, options)) continue
|
||||
if (!parameter
|
||||
.getParameterType(options.allowSpecialClassNames)
|
||||
.processTypeIfExtractable(info.typeParameters, info.nonDenotableTypes, options, targetScope)) continue
|
||||
|
||||
with (parameter) {
|
||||
if (currentName == null) {
|
||||
@@ -693,7 +706,7 @@ private fun ExtractionData.inferParametersInfo(
|
||||
}
|
||||
|
||||
for (typeToCheck in info.typeParameters.flatMapTo(HashSet<JetType>()) { it.collectReferencedTypes(bindingContext) }) {
|
||||
typeToCheck.processTypeIfExtractable(info.typeParameters, info.nonDenotableTypes, options)
|
||||
typeToCheck.processTypeIfExtractable(info.typeParameters, info.nonDenotableTypes, options, targetScope)
|
||||
}
|
||||
|
||||
|
||||
@@ -765,6 +778,8 @@ fun ExtractionData.performAnalysis(): AnalysisResult {
|
||||
val bindingContext = commonParent.getContextForContainingDeclarationBody()
|
||||
if (bindingContext == null) return noContainerError
|
||||
|
||||
val targetScope = JetScopeUtils.getResolutionScope(targetSibling, bindingContext)
|
||||
|
||||
val pseudocodeDeclaration =
|
||||
PsiTreeUtil.getParentOfType(commonParent, javaClass<JetDeclarationWithBody>(), javaClass<JetClassOrObject>())
|
||||
?: commonParent.getNonStrictParentOfType<JetProperty>()
|
||||
@@ -786,7 +801,7 @@ fun ExtractionData.performAnalysis(): AnalysisResult {
|
||||
|
||||
val modifiedVarDescriptorsWithExpressions = localInstructions.getModifiedVarDescriptors(bindingContext)
|
||||
|
||||
val paramsInfo = inferParametersInfo(commonParent, pseudocode, bindingContext, modifiedVarDescriptorsWithExpressions.keySet())
|
||||
val paramsInfo = inferParametersInfo(commonParent, pseudocode, bindingContext, targetScope, modifiedVarDescriptorsWithExpressions.keySet())
|
||||
if (paramsInfo.errorMessage != null) {
|
||||
return AnalysisResult(null, Status.CRITICAL_ERROR, listOf(paramsInfo.errorMessage!!))
|
||||
}
|
||||
@@ -803,12 +818,13 @@ fun ExtractionData.performAnalysis(): AnalysisResult {
|
||||
bindingContext,
|
||||
modifiedVarDescriptorsForControlFlow,
|
||||
options,
|
||||
targetScope,
|
||||
paramsInfo.parameters
|
||||
)
|
||||
controlFlowMessage?.let { messages.add(it) }
|
||||
|
||||
val returnType = controlFlow.outputValueBoxer.returnType
|
||||
returnType.processTypeIfExtractable(paramsInfo.typeParameters, paramsInfo.nonDenotableTypes, options)
|
||||
returnType.processTypeIfExtractable(paramsInfo.typeParameters, paramsInfo.nonDenotableTypes, options, targetScope)
|
||||
|
||||
if (paramsInfo.nonDenotableTypes.isNotEmpty()) {
|
||||
val typeStr = paramsInfo.nonDenotableTypes.map {it.renderForMessage()}.sort()
|
||||
|
||||
@@ -3,6 +3,7 @@ LineBreakpoint created at ceObject.kt:5
|
||||
Connected to the target VM, address: '!HOST_NAME!:PORT_NAME!', transport: 'socket'
|
||||
ceObject.kt:5
|
||||
Compile bytecode for (object: T {}).test()
|
||||
Compile bytecode for (object: T { fun a() = 1 }).a()
|
||||
Compile bytecode for object: T {}
|
||||
Disconnected from the target VM, address: '!HOST_NAME!:PORT_NAME!', transport: 'socket'
|
||||
|
||||
|
||||
+3
@@ -12,5 +12,8 @@ trait T {
|
||||
// EXPRESSION: (object: T {}).test()
|
||||
// RESULT: 1: I
|
||||
|
||||
// EXPRESSION: (object: T { fun a() = 1 }).a()
|
||||
// RESULT: 1: I
|
||||
|
||||
// EXPRESSION: object: T {}
|
||||
// RESULT: instance of packageForDebugger.PackageForDebuggerPackage$debugFile$@packagePartHASH$myFun$1(id=ID): LpackageForDebugger/PackageForDebuggerPackage$debugFile$@packagePartHASH$myFun$1;
|
||||
+1
-1
@@ -1 +1 @@
|
||||
Cannot extract method since following types are not denotable in the target scope: <no name provided>
|
||||
Following declarations are used outside of selected code fragment: override fun call(): Int
|
||||
+3
-13
@@ -1,15 +1,5 @@
|
||||
trait Callable<T> {
|
||||
fun call(): T
|
||||
}
|
||||
trait T
|
||||
|
||||
fun foo(a: Int): Int {
|
||||
// SIBLING:
|
||||
val o = object: Callable<Int> {
|
||||
val b: Int = 1
|
||||
|
||||
override fun call(): Int {
|
||||
return <selection>a + b</selection>
|
||||
}
|
||||
}
|
||||
return o.call()
|
||||
fun foo(): T {
|
||||
return <selection>object: T() {}</selection>
|
||||
}
|
||||
+7
@@ -0,0 +1,7 @@
|
||||
trait T
|
||||
|
||||
fun foo(): T {
|
||||
return t()
|
||||
}
|
||||
|
||||
private fun t() = object : T() {}
|
||||
+15
@@ -0,0 +1,15 @@
|
||||
trait Callable<T> {
|
||||
fun call(): T
|
||||
}
|
||||
|
||||
fun foo(a: Int): Int {
|
||||
// SIBLING:
|
||||
val o = object: Callable<Int> {
|
||||
val b: Int = 1
|
||||
|
||||
override fun call(): Int {
|
||||
return <selection>a + b</selection>
|
||||
}
|
||||
}
|
||||
return o.call()
|
||||
}
|
||||
+10
@@ -0,0 +1,10 @@
|
||||
trait T
|
||||
|
||||
fun foo(): T {
|
||||
class A: T
|
||||
|
||||
// SIBLING:
|
||||
fun bar(): T {
|
||||
return <selection>A()</selection>
|
||||
}
|
||||
}
|
||||
+12
@@ -0,0 +1,12 @@
|
||||
trait T
|
||||
|
||||
fun foo(): T {
|
||||
class A: T
|
||||
|
||||
fun a() = A()
|
||||
|
||||
// SIBLING:
|
||||
fun bar(): T {
|
||||
return a()
|
||||
}
|
||||
}
|
||||
+13
@@ -0,0 +1,13 @@
|
||||
// PARAM_DESCRIPTOR: val a: foo.A defined in foo.bar
|
||||
// PARAM_TYPES: T
|
||||
trait T
|
||||
|
||||
// SIBLING:
|
||||
fun foo(): T {
|
||||
class A: T
|
||||
|
||||
fun bar(): T {
|
||||
val a = A()
|
||||
return <selection>a</selection>
|
||||
}
|
||||
}
|
||||
+15
@@ -0,0 +1,15 @@
|
||||
// PARAM_DESCRIPTOR: val a: foo.A defined in foo.bar
|
||||
// PARAM_TYPES: T
|
||||
trait T
|
||||
|
||||
// SIBLING:
|
||||
fun foo(): T {
|
||||
class A: T
|
||||
|
||||
fun bar(): T {
|
||||
val a = A()
|
||||
return t(a)
|
||||
}
|
||||
}
|
||||
|
||||
private fun t(a: T) = a
|
||||
+13
@@ -0,0 +1,13 @@
|
||||
// PARAM_DESCRIPTOR: val a: foo.A defined in foo.bar
|
||||
// PARAM_TYPES: foo.A, T
|
||||
trait T
|
||||
|
||||
fun foo(): T {
|
||||
class A: T
|
||||
|
||||
// SIBLING:
|
||||
fun bar(): T {
|
||||
val a = A()
|
||||
return <selection>a</selection>
|
||||
}
|
||||
}
|
||||
+15
@@ -0,0 +1,15 @@
|
||||
// PARAM_DESCRIPTOR: val a: foo.A defined in foo.bar
|
||||
// PARAM_TYPES: foo.A, T
|
||||
trait T
|
||||
|
||||
fun foo(): T {
|
||||
class A: T
|
||||
|
||||
fun a(a: A) = a
|
||||
|
||||
// SIBLING:
|
||||
fun bar(): T {
|
||||
val a = A()
|
||||
return a(a)
|
||||
}
|
||||
}
|
||||
+11
@@ -0,0 +1,11 @@
|
||||
trait T
|
||||
|
||||
// SIBLING:
|
||||
fun foo(): T {
|
||||
class A: T
|
||||
|
||||
fun bar(): A {
|
||||
val a = A()
|
||||
return <selection>a</selection>
|
||||
}
|
||||
}
|
||||
+1
@@ -0,0 +1 @@
|
||||
Cannot extract method since following types are not denotable in the target scope: A
|
||||
+30
@@ -1897,11 +1897,41 @@ public class JetExtractionTestGenerated extends AbstractJetExtractionTest {
|
||||
doExtractFunctionTest(fileName);
|
||||
}
|
||||
|
||||
@TestMetadata("anonymousObjectWithCall.kt")
|
||||
public void testAnonymousObjectWithCall() throws Exception {
|
||||
String fileName = JetTestUtils.navigationMetadata("idea/testData/refactoring/extractFunction/parameters/nonDenotableTypes/anonymousObjectWithCall.kt");
|
||||
doExtractFunctionTest(fileName);
|
||||
}
|
||||
|
||||
@TestMetadata("localClass.kt")
|
||||
public void testLocalClass() throws Exception {
|
||||
String fileName = JetTestUtils.navigationMetadata("idea/testData/refactoring/extractFunction/parameters/nonDenotableTypes/localClass.kt");
|
||||
doExtractFunctionTest(fileName);
|
||||
}
|
||||
|
||||
@TestMetadata("localClassWithSuperclass.kt")
|
||||
public void testLocalClassWithSuperclass() throws Exception {
|
||||
String fileName = JetTestUtils.navigationMetadata("idea/testData/refactoring/extractFunction/parameters/nonDenotableTypes/localClassWithSuperclass.kt");
|
||||
doExtractFunctionTest(fileName);
|
||||
}
|
||||
|
||||
@TestMetadata("localClassWithSuperclassParameter.kt")
|
||||
public void testLocalClassWithSuperclassParameter() throws Exception {
|
||||
String fileName = JetTestUtils.navigationMetadata("idea/testData/refactoring/extractFunction/parameters/nonDenotableTypes/localClassWithSuperclassParameter.kt");
|
||||
doExtractFunctionTest(fileName);
|
||||
}
|
||||
|
||||
@TestMetadata("localClassWithSuperclassParameterInLocalFun.kt")
|
||||
public void testLocalClassWithSuperclassParameterInLocalFun() throws Exception {
|
||||
String fileName = JetTestUtils.navigationMetadata("idea/testData/refactoring/extractFunction/parameters/nonDenotableTypes/localClassWithSuperclassParameterInLocalFun.kt");
|
||||
doExtractFunctionTest(fileName);
|
||||
}
|
||||
|
||||
@TestMetadata("localClassWithSuperclassParameterNoApproximation.kt")
|
||||
public void testLocalClassWithSuperclassParameterNoApproximation() throws Exception {
|
||||
String fileName = JetTestUtils.navigationMetadata("idea/testData/refactoring/extractFunction/parameters/nonDenotableTypes/localClassWithSuperclassParameterNoApproximation.kt");
|
||||
doExtractFunctionTest(fileName);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user