Converting to Kotlin (step 2)

This commit is contained in:
Valentin Kipyatkov
2016-03-28 22:05:28 +03:00
parent 6afd88bf07
commit e0ba2e8395
@@ -14,252 +14,209 @@
* limitations under the License. * limitations under the License.
*/ */
package org.jetbrains.kotlin.idea.quickfix; package org.jetbrains.kotlin.idea.quickfix
import com.intellij.codeInsight.intention.IntentionAction; import com.intellij.codeInsight.intention.IntentionAction
import com.intellij.openapi.editor.Editor; import com.intellij.openapi.editor.Editor
import com.intellij.openapi.project.Project; import com.intellij.openapi.project.Project
import com.intellij.psi.PsiElement; import com.intellij.psi.PsiFile
import com.intellij.psi.PsiFile; import com.intellij.psi.util.PsiTreeUtil
import com.intellij.psi.util.PsiTreeUtil; import com.intellij.util.IncorrectOperationException
import com.intellij.util.IncorrectOperationException; import org.jetbrains.kotlin.builtins.KotlinBuiltIns
import org.jetbrains.annotations.NotNull; import org.jetbrains.kotlin.descriptors.FunctionDescriptor
import org.jetbrains.annotations.Nullable; import org.jetbrains.kotlin.diagnostics.Diagnostic
import org.jetbrains.kotlin.builtins.KotlinBuiltIns; import org.jetbrains.kotlin.diagnostics.Errors.COMPONENT_FUNCTION_RETURN_TYPE_MISMATCH
import org.jetbrains.kotlin.descriptors.CallableDescriptor; import org.jetbrains.kotlin.idea.KotlinBundle
import org.jetbrains.kotlin.descriptors.FunctionDescriptor; import org.jetbrains.kotlin.idea.caches.resolve.analyze
import org.jetbrains.kotlin.diagnostics.Diagnostic; import org.jetbrains.kotlin.idea.caches.resolve.resolveToDescriptor
import org.jetbrains.kotlin.idea.KotlinBundle; import org.jetbrains.kotlin.idea.core.quickfix.QuickFixUtil
import org.jetbrains.kotlin.idea.caches.resolve.ResolutionUtils; import org.jetbrains.kotlin.idea.project.platform
import org.jetbrains.kotlin.idea.core.quickfix.QuickFixUtil; import org.jetbrains.kotlin.idea.util.IdeDescriptorRenderers
import org.jetbrains.kotlin.idea.util.IdeDescriptorRenderers; import org.jetbrains.kotlin.idea.util.ShortenReferences
import org.jetbrains.kotlin.idea.util.ShortenReferences; import org.jetbrains.kotlin.psi.*
import org.jetbrains.kotlin.name.FqName; import org.jetbrains.kotlin.psi.psiUtil.getElementTextWithContext
import org.jetbrains.kotlin.name.Name; import org.jetbrains.kotlin.resolve.BindingContext
import org.jetbrains.kotlin.psi.*; import org.jetbrains.kotlin.resolve.DescriptorToSourceUtils
import org.jetbrains.kotlin.psi.psiUtil.PsiUtilsKt; import org.jetbrains.kotlin.resolve.calls.callUtil.getResolvedCall
import org.jetbrains.kotlin.resolve.BindingContext; import org.jetbrains.kotlin.resolve.dataClassUtils.getComponentIndex
import org.jetbrains.kotlin.resolve.DescriptorToSourceUtils; import org.jetbrains.kotlin.resolve.descriptorUtil.builtIns
import org.jetbrains.kotlin.resolve.calls.callUtil.CallUtilKt; import org.jetbrains.kotlin.types.ErrorUtils
import org.jetbrains.kotlin.resolve.calls.model.ResolvedCall; import org.jetbrains.kotlin.types.KotlinType
import org.jetbrains.kotlin.resolve.dataClassUtils.DataClassUtilsKt; import org.jetbrains.kotlin.types.checker.KotlinTypeChecker
import org.jetbrains.kotlin.types.ErrorUtils; import java.util.*
import org.jetbrains.kotlin.types.KotlinType;
import org.jetbrains.kotlin.types.checker.KotlinTypeChecker;
import java.util.LinkedList; class ChangeFunctionReturnTypeFix(element: KtFunction, private val type: KotlinType) : KotlinQuickFixAction<KtFunction>(element) {
import java.util.List; private val changeFunctionLiteralReturnTypeFix: ChangeFunctionLiteralReturnTypeFix?
import static org.jetbrains.kotlin.diagnostics.Errors.COMPONENT_FUNCTION_RETURN_TYPE_MISMATCH; init {
import static org.jetbrains.kotlin.idea.project.PlatformKt.getPlatform; if (element is KtFunctionLiteral) {
import static org.jetbrains.kotlin.resolve.descriptorUtil.DescriptorUtilsKt.getBuiltIns; val functionLiteralExpression = PsiTreeUtil.getParentOfType(element, KtLambdaExpression::class.java) ?: error("FunctionLiteral outside any FunctionLiteralExpression: " + element.getElementTextWithContext())
changeFunctionLiteralReturnTypeFix = ChangeFunctionLiteralReturnTypeFix(functionLiteralExpression, type)
public class ChangeFunctionReturnTypeFix extends KotlinQuickFixAction<KtFunction> {
private final KotlinType type;
private final ChangeFunctionLiteralReturnTypeFix changeFunctionLiteralReturnTypeFix;
public ChangeFunctionReturnTypeFix(@NotNull KtFunction element, @NotNull KotlinType type) {
super(element);
this.type = type;
if (element instanceof KtFunctionLiteral) {
KtLambdaExpression functionLiteralExpression = PsiTreeUtil.getParentOfType(element, KtLambdaExpression.class);
assert functionLiteralExpression != null : "FunctionLiteral outside any FunctionLiteralExpression: " +
PsiUtilsKt.getElementTextWithContext(element);
changeFunctionLiteralReturnTypeFix = new ChangeFunctionLiteralReturnTypeFix(functionLiteralExpression, type);
} }
else { else {
changeFunctionLiteralReturnTypeFix = null; changeFunctionLiteralReturnTypeFix = null
} }
} }
@NotNull override fun getText(): String {
@Override
public String getText() {
if (changeFunctionLiteralReturnTypeFix != null) { if (changeFunctionLiteralReturnTypeFix != null) {
return changeFunctionLiteralReturnTypeFix.getText(); return changeFunctionLiteralReturnTypeFix.text
} }
String functionName = getElement().getName(); var functionName = element.name
FqName fqName = getElement().getFqName(); val fqName = element.fqName
if (fqName != null) functionName = fqName.asString(); if (fqName != null) functionName = fqName.asString()
if (KotlinBuiltIns.isUnit(type) && getElement().hasBlockBody()) { if (KotlinBuiltIns.isUnit(type) && element.hasBlockBody()) {
return functionName == null ? return if (functionName == null)
KotlinBundle.message("remove.no.name.function.return.type") : KotlinBundle.message("remove.no.name.function.return.type")
KotlinBundle.message("remove.function.return.type", functionName); else
KotlinBundle.message("remove.function.return.type", functionName)
} }
String renderedType = IdeDescriptorRenderers.SOURCE_CODE_SHORT_NAMES_IN_TYPES.renderType(type); val renderedType = IdeDescriptorRenderers.SOURCE_CODE_SHORT_NAMES_IN_TYPES.renderType(type)
return functionName == null ? return if (functionName == null)
KotlinBundle.message("change.no.name.function.return.type", renderedType) : KotlinBundle.message("change.no.name.function.return.type", renderedType)
KotlinBundle.message("change.function.return.type", functionName, renderedType); else
KotlinBundle.message("change.function.return.type", functionName, renderedType)
} }
@NotNull override fun getFamilyName(): String {
@Override return KotlinBundle.message("change.type.family")
public String getFamilyName() {
return KotlinBundle.message("change.type.family");
} }
@Override override fun isAvailable(project: Project, editor: Editor?, file: PsiFile): Boolean {
public boolean isAvailable(@NotNull Project project, @Nullable Editor editor, @NotNull PsiFile file) {
return super.isAvailable(project, editor, file) && return super.isAvailable(project, editor, file) &&
!ErrorUtils.containsErrorType(type) && !ErrorUtils.containsErrorType(type) &&
!(getElement() instanceof KtConstructor); element !is KtConstructor<*>
} }
@Override @Throws(IncorrectOperationException::class)
public void invoke(@NotNull Project project, @Nullable Editor editor, @NotNull KtFile file) throws IncorrectOperationException { public override operator fun invoke(project: Project, editor: Editor?, file: KtFile) {
if (changeFunctionLiteralReturnTypeFix != null) { if (changeFunctionLiteralReturnTypeFix != null) {
changeFunctionLiteralReturnTypeFix.invoke(project, editor, file); changeFunctionLiteralReturnTypeFix.invoke(project, editor!!, file)
} }
else { else {
if (!(KotlinBuiltIns.isUnit(type) && getElement().hasBlockBody())) { if (!(KotlinBuiltIns.isUnit(type) && element.hasBlockBody())) {
KtTypeReference newTypeRef = KtPsiFactoryKt var newTypeRef: KtTypeReference? = KtPsiFactory(project).createType(IdeDescriptorRenderers.SOURCE_CODE.renderType(type))
.KtPsiFactory(project).createType(IdeDescriptorRenderers.SOURCE_CODE.renderType(type)); newTypeRef = element.setTypeReference(newTypeRef)
newTypeRef = getElement().setTypeReference(newTypeRef); assert(newTypeRef != null)
assert newTypeRef != null; ShortenReferences.DEFAULT.process(newTypeRef!!)
ShortenReferences.DEFAULT.process(newTypeRef);
} }
else { else {
getElement().setTypeReference(null); element.typeReference = null
} }
} }
} }
@NotNull companion object {
public static KtDestructuringDeclarationEntry getDestructuringDeclarationEntryThatTypeMismatchComponentFunction(Diagnostic diagnostic) {
Name componentName = COMPONENT_FUNCTION_RETURN_TYPE_MISMATCH.cast(diagnostic).getA();
int componentIndex = DataClassUtilsKt.getComponentIndex(componentName);
KtDestructuringDeclaration multiDeclaration = QuickFixUtil.getParentElementOfType(diagnostic, KtDestructuringDeclaration.class);
assert multiDeclaration != null : "COMPONENT_FUNCTION_RETURN_TYPE_MISMATCH reported on expression that is not within any multi declaration";
return multiDeclaration.getEntries().get(componentIndex - 1);
}
@NotNull fun getDestructuringDeclarationEntryThatTypeMismatchComponentFunction(diagnostic: Diagnostic): KtDestructuringDeclarationEntry {
public static KotlinSingleIntentionActionFactory createFactoryForComponentFunctionReturnTypeMismatch() { val componentName = COMPONENT_FUNCTION_RETURN_TYPE_MISMATCH.cast(diagnostic).a
return new KotlinSingleIntentionActionFactory() { val componentIndex = getComponentIndex(componentName)
@Nullable val multiDeclaration = QuickFixUtil.getParentElementOfType(diagnostic, KtDestructuringDeclaration::class.java) ?: error("COMPONENT_FUNCTION_RETURN_TYPE_MISMATCH reported on expression that is not within any multi declaration")
@Override return multiDeclaration.entries[componentIndex - 1]
public IntentionAction createAction(@NotNull Diagnostic diagnostic) { }
KtDestructuringDeclarationEntry entry = getDestructuringDeclarationEntryThatTypeMismatchComponentFunction(diagnostic);
BindingContext context = ResolutionUtils.analyze(entry);
ResolvedCall<FunctionDescriptor> resolvedCall = context.get(BindingContext.COMPONENT_RESOLVED_CALL, entry);
if (resolvedCall == null) return null;
KtFunction componentFunction = (KtFunction) DescriptorToSourceUtils
.descriptorToDeclaration(resolvedCall.getCandidateDescriptor());
KotlinType expectedType = context.get(BindingContext.TYPE, entry.getTypeReference());
if (componentFunction != null && expectedType != null) {
return new ChangeFunctionReturnTypeFix(componentFunction, expectedType);
}
else return null;
}
};
}
@NotNull fun createFactoryForComponentFunctionReturnTypeMismatch(): KotlinSingleIntentionActionFactory {
public static KotlinSingleIntentionActionFactory createFactoryForHasNextFunctionTypeMismatch() { return object : KotlinSingleIntentionActionFactory() {
return new KotlinSingleIntentionActionFactory() { public override fun createAction(diagnostic: Diagnostic): IntentionAction? {
@Nullable val entry = getDestructuringDeclarationEntryThatTypeMismatchComponentFunction(diagnostic)
@Override val context = entry.analyze()
public IntentionAction createAction(@NotNull Diagnostic diagnostic) { val resolvedCall = context.get(BindingContext.COMPONENT_RESOLVED_CALL, entry) ?: return null
KtExpression expression = QuickFixUtil.getParentElementOfType(diagnostic, KtExpression.class); val componentFunction = DescriptorToSourceUtils.descriptorToDeclaration(resolvedCall.candidateDescriptor) as KtFunction?
assert expression != null : "HAS_NEXT_FUNCTION_TYPE_MISMATCH reported on element that is not within any expression"; val expectedType = context.get(BindingContext.TYPE, entry.typeReference!!)
BindingContext context = ResolutionUtils.analyze(expression); if (componentFunction != null && expectedType != null) {
ResolvedCall<FunctionDescriptor> resolvedCall = context.get(BindingContext.LOOP_RANGE_HAS_NEXT_RESOLVED_CALL, expression); return ChangeFunctionReturnTypeFix(componentFunction, expectedType)
if (resolvedCall == null) return null;
FunctionDescriptor hasNextDescriptor = resolvedCall.getCandidateDescriptor();
KtFunction hasNextFunction = (KtFunction) DescriptorToSourceUtils
.descriptorToDeclaration(hasNextDescriptor);
if (hasNextFunction != null) {
return new ChangeFunctionReturnTypeFix(hasNextFunction, getBuiltIns(hasNextDescriptor).getBooleanType());
}
else return null;
}
};
}
@NotNull
public static KotlinSingleIntentionActionFactory createFactoryForCompareToTypeMismatch() {
return new KotlinSingleIntentionActionFactory() {
@Nullable
@Override
public IntentionAction createAction(@NotNull Diagnostic diagnostic) {
KtBinaryExpression expression = QuickFixUtil.getParentElementOfType(diagnostic, KtBinaryExpression.class);
assert expression != null : "COMPARE_TO_TYPE_MISMATCH reported on element that is not within any expression";
BindingContext context = ResolutionUtils.analyze(expression);
ResolvedCall<?> resolvedCall = CallUtilKt.getResolvedCall(expression, context);
if (resolvedCall == null) return null;
CallableDescriptor compareToDescriptor = resolvedCall.getCandidateDescriptor();
PsiElement compareTo = DescriptorToSourceUtils.descriptorToDeclaration(compareToDescriptor);
if (!(compareTo instanceof KtFunction)) return null;
return new ChangeFunctionReturnTypeFix((KtFunction) compareTo, getBuiltIns(compareToDescriptor).getIntType());
}
};
}
@NotNull
public static KotlinIntentionActionsFactory createFactoryForReturnTypeMismatchOnOverride() {
return new KotlinIntentionActionsFactory() {
@NotNull
@Override
protected List<IntentionAction> doCreateActions(@NotNull Diagnostic diagnostic) {
List<IntentionAction> actions = new LinkedList<IntentionAction>();
KtFunction function = QuickFixUtil.getParentElementOfType(diagnostic, KtFunction.class);
if (function != null) {
FunctionDescriptor descriptor = (FunctionDescriptor) ResolutionUtils.resolveToDescriptor(function);
KotlinType matchingReturnType = QuickFixUtil.findLowerBoundOfOverriddenCallablesReturnTypes(descriptor);
if (matchingReturnType != null) {
actions.add(new ChangeFunctionReturnTypeFix(function, matchingReturnType));
} }
else
return null
}
}
}
KotlinType functionType = descriptor.getReturnType(); fun createFactoryForHasNextFunctionTypeMismatch(): KotlinSingleIntentionActionFactory {
if (functionType == null) return actions; return object : KotlinSingleIntentionActionFactory() {
public override fun createAction(diagnostic: Diagnostic): IntentionAction? {
val expression = QuickFixUtil.getParentElementOfType(diagnostic, KtExpression::class.java) ?: error("HAS_NEXT_FUNCTION_TYPE_MISMATCH reported on element that is not within any expression")
val context = expression.analyze()
val resolvedCall = context.get(BindingContext.LOOP_RANGE_HAS_NEXT_RESOLVED_CALL, expression) ?: return null
val hasNextDescriptor = resolvedCall.candidateDescriptor
val hasNextFunction = DescriptorToSourceUtils.descriptorToDeclaration(hasNextDescriptor) as KtFunction?
if (hasNextFunction != null) {
return ChangeFunctionReturnTypeFix(hasNextFunction, hasNextDescriptor.builtIns.booleanType)
}
else
return null
}
}
}
List<FunctionDescriptor> overriddenMismatchingFunctions = new LinkedList<FunctionDescriptor>(); fun createFactoryForCompareToTypeMismatch(): KotlinSingleIntentionActionFactory {
for (FunctionDescriptor overriddenFunction: descriptor.getOverriddenDescriptors()) { return object : KotlinSingleIntentionActionFactory() {
KotlinType overriddenFunctionType = overriddenFunction.getReturnType(); public override fun createAction(diagnostic: Diagnostic): IntentionAction? {
if (overriddenFunctionType == null) continue; val expression = QuickFixUtil.getParentElementOfType(diagnostic, KtBinaryExpression::class.java) ?: error("COMPARE_TO_TYPE_MISMATCH reported on element that is not within any expression")
if (!KotlinTypeChecker.DEFAULT.isSubtypeOf(functionType, overriddenFunctionType)) { val context = expression.analyze()
overriddenMismatchingFunctions.add(overriddenFunction); val resolvedCall = expression.getResolvedCall(context) ?: return null
} val compareToDescriptor = resolvedCall.candidateDescriptor
} val compareTo = DescriptorToSourceUtils.descriptorToDeclaration(compareToDescriptor)
if (compareTo !is KtFunction) return null
if (overriddenMismatchingFunctions.size() == 1) { return ChangeFunctionReturnTypeFix(compareTo, compareToDescriptor.builtIns.intType)
PsiElement overriddenFunction = DescriptorToSourceUtils }
.descriptorToDeclaration(overriddenMismatchingFunctions.get(0)); }
if (overriddenFunction instanceof KtFunction) { }
actions.add(new ChangeFunctionReturnTypeFix((KtFunction) overriddenFunction, functionType));
fun createFactoryForReturnTypeMismatchOnOverride(): KotlinIntentionActionsFactory {
return object : KotlinIntentionActionsFactory() {
override fun doCreateActions(diagnostic: Diagnostic): List<IntentionAction> {
val actions = LinkedList<IntentionAction>()
val function = QuickFixUtil.getParentElementOfType(diagnostic, KtFunction::class.java)
if (function != null) {
val descriptor = function.resolveToDescriptor() as FunctionDescriptor
val matchingReturnType = QuickFixUtil.findLowerBoundOfOverriddenCallablesReturnTypes(descriptor)
if (matchingReturnType != null) {
actions.add(ChangeFunctionReturnTypeFix(function, matchingReturnType))
}
val functionType = descriptor.returnType ?: return actions
val overriddenMismatchingFunctions = LinkedList<FunctionDescriptor>()
for (overriddenFunction in descriptor.overriddenDescriptors) {
val overriddenFunctionType = overriddenFunction.returnType ?: continue
if (!KotlinTypeChecker.DEFAULT.isSubtypeOf(functionType, overriddenFunctionType)) {
overriddenMismatchingFunctions.add(overriddenFunction)
}
}
if (overriddenMismatchingFunctions.size == 1) {
val overriddenFunction = DescriptorToSourceUtils.descriptorToDeclaration(overriddenMismatchingFunctions[0])
if (overriddenFunction is KtFunction) {
actions.add(ChangeFunctionReturnTypeFix(overriddenFunction, functionType))
}
} }
} }
return actions
} }
return actions;
} }
}; }
}
@NotNull fun createFactoryForChangingReturnTypeToUnit(): KotlinSingleIntentionActionFactory {
public static KotlinSingleIntentionActionFactory createFactoryForChangingReturnTypeToUnit() { return object : KotlinSingleIntentionActionFactory() {
return new KotlinSingleIntentionActionFactory() { public override fun createAction(diagnostic: Diagnostic): IntentionAction? {
@Nullable val function = QuickFixUtil.getParentElementOfType(diagnostic, KtFunction::class.java) ?: return null
@Override return ChangeFunctionReturnTypeFix(function, function.platform.builtIns.unitType)
public IntentionAction createAction(@NotNull Diagnostic diagnostic) { }
KtFunction function = QuickFixUtil.getParentElementOfType(diagnostic, KtFunction.class);
return function == null ? null : new ChangeFunctionReturnTypeFix(function, getPlatform(function).getBuiltIns().getUnitType());
} }
}; }
}
@NotNull fun createFactoryForChangingReturnTypeToNothing(): KotlinSingleIntentionActionFactory {
public static KotlinSingleIntentionActionFactory createFactoryForChangingReturnTypeToNothing() { return object : KotlinSingleIntentionActionFactory() {
return new KotlinSingleIntentionActionFactory() { public override fun createAction(diagnostic: Diagnostic): IntentionAction? {
@Nullable val function = QuickFixUtil.getParentElementOfType(diagnostic, KtFunction::class.java)
@Override return if (function == null) null else ChangeFunctionReturnTypeFix(function, function.platform.builtIns.nothingType)
public IntentionAction createAction(@NotNull Diagnostic diagnostic) { }
KtFunction function = QuickFixUtil.getParentElementOfType(diagnostic, KtFunction.class);
return function == null ? null : new ChangeFunctionReturnTypeFix(function, getPlatform(function).getBuiltIns().getNothingType());
} }
}; }
} }
} }