Converting to Kotlin (step 2)

This commit is contained in:
Valentin Kipyatkov
2016-03-29 16:17:19 +03:00
parent f18db1f88e
commit 74f9f89728
@@ -14,147 +14,125 @@
* 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.PsiFile; import com.intellij.psi.PsiFile
import com.intellij.psi.util.PsiTreeUtil; import com.intellij.psi.util.PsiTreeUtil
import com.intellij.util.IncorrectOperationException; import org.jetbrains.kotlin.diagnostics.Diagnostic
import org.jetbrains.annotations.NotNull; import org.jetbrains.kotlin.idea.KotlinBundle
import org.jetbrains.annotations.Nullable; import org.jetbrains.kotlin.idea.caches.resolve.analyzeFullyAndGetResult
import org.jetbrains.kotlin.analyzer.AnalysisResult; import org.jetbrains.kotlin.idea.core.quickfix.QuickFixUtil
import org.jetbrains.kotlin.builtins.KotlinBuiltIns; import org.jetbrains.kotlin.idea.project.platform
import org.jetbrains.kotlin.descriptors.CallableDescriptor; import org.jetbrains.kotlin.idea.util.IdeDescriptorRenderers
import org.jetbrains.kotlin.descriptors.ClassDescriptor; import org.jetbrains.kotlin.idea.util.ShortenReferences
import org.jetbrains.kotlin.diagnostics.Diagnostic; import org.jetbrains.kotlin.psi.*
import org.jetbrains.kotlin.idea.KotlinBundle; import org.jetbrains.kotlin.resolve.BindingContext
import org.jetbrains.kotlin.idea.caches.resolve.ResolutionUtils; import org.jetbrains.kotlin.resolve.calls.callUtil.getParentResolvedCall
import org.jetbrains.kotlin.idea.core.quickfix.QuickFixUtil; import org.jetbrains.kotlin.resolve.calls.callUtil.getValueArgumentForExpression
import org.jetbrains.kotlin.idea.util.IdeDescriptorRenderers; import org.jetbrains.kotlin.types.KotlinType
import org.jetbrains.kotlin.idea.util.ShortenReferences; import org.jetbrains.kotlin.types.TypeUtils
import org.jetbrains.kotlin.psi.*; import org.jetbrains.kotlin.types.checker.KotlinTypeChecker
import org.jetbrains.kotlin.resolve.BindingContext; import java.util.*
import org.jetbrains.kotlin.resolve.calls.callUtil.CallUtilKt;
import org.jetbrains.kotlin.resolve.calls.model.ResolvedCall;
import org.jetbrains.kotlin.types.KotlinType;
import org.jetbrains.kotlin.types.TypeProjection;
import org.jetbrains.kotlin.types.TypeUtils;
import org.jetbrains.kotlin.types.checker.KotlinTypeChecker;
import java.util.LinkedList; class ChangeFunctionLiteralReturnTypeFix(functionLiteralExpression: KtLambdaExpression, private val type: KotlinType) : KotlinQuickFixAction<KtLambdaExpression>(functionLiteralExpression) {
import java.util.List; private val functionLiteralReturnTypeRef: KtTypeReference?
private var appropriateQuickFix: IntentionAction? = null
import static org.jetbrains.kotlin.idea.project.PlatformKt.getPlatform; init {
functionLiteralReturnTypeRef = functionLiteralExpression.functionLiteral.typeReference
public class ChangeFunctionLiteralReturnTypeFix extends KotlinQuickFixAction<KtLambdaExpression> { doInit(functionLiteralExpression)
private final KotlinType type; }
private final KtTypeReference functionLiteralReturnTypeRef;
private IntentionAction appropriateQuickFix = null;
public ChangeFunctionLiteralReturnTypeFix(@NotNull KtLambdaExpression functionLiteralExpression, @NotNull KotlinType type) { private fun doInit(functionLiteralExpression: KtLambdaExpression) {
super(functionLiteralExpression); val analysisResult = functionLiteralExpression.getContainingKtFile().analyzeFullyAndGetResult()
this.type = type; val context = analysisResult.bindingContext
functionLiteralReturnTypeRef = functionLiteralExpression.getFunctionLiteral().getTypeReference(); val functionLiteralType = context.getType(functionLiteralExpression) ?: error("Type of function literal not available in binding context")
AnalysisResult analysisResult = ResolutionUtils.analyzeFullyAndGetResult(functionLiteralExpression.getContainingKtFile()); val builtIns = analysisResult.moduleDescriptor.builtIns
BindingContext context = analysisResult.getBindingContext(); val functionClass = builtIns.getFunction(functionLiteralType.arguments.size - 1)
KotlinType functionLiteralType = context.getType(functionLiteralExpression); val functionClassTypeParameters = LinkedList<KotlinType>()
assert functionLiteralType != null : "Type of function literal not available in binding context"; for (typeProjection in functionLiteralType.arguments) {
functionClassTypeParameters.add(typeProjection.type)
KotlinBuiltIns builtIns = analysisResult.getModuleDescriptor().getBuiltIns();
ClassDescriptor functionClass = builtIns.getFunction(functionLiteralType.getArguments().size() - 1);
List<KotlinType> functionClassTypeParameters = new LinkedList<KotlinType>();
for (TypeProjection typeProjection: functionLiteralType.getArguments()) {
functionClassTypeParameters.add(typeProjection.getType());
} }
// Replacing return type: // Replacing return type:
functionClassTypeParameters.remove(functionClassTypeParameters.size() - 1); functionClassTypeParameters.removeAt(functionClassTypeParameters.size - 1)
functionClassTypeParameters.add(type); functionClassTypeParameters.add(type)
KotlinType eventualFunctionLiteralType = TypeUtils.substituteParameters(functionClass, functionClassTypeParameters); val eventualFunctionLiteralType = TypeUtils.substituteParameters(functionClass, functionClassTypeParameters)
KtProperty correspondingProperty = PsiTreeUtil.getParentOfType(functionLiteralExpression, KtProperty.class); val correspondingProperty = PsiTreeUtil.getParentOfType(functionLiteralExpression, KtProperty::class.java)
if (correspondingProperty != null && QuickFixUtil.canEvaluateTo(correspondingProperty.getInitializer(), functionLiteralExpression)) { if (correspondingProperty != null && QuickFixUtil.canEvaluateTo(correspondingProperty.initializer!!, functionLiteralExpression)) {
KtTypeReference correspondingPropertyTypeRef = correspondingProperty.getTypeReference(); val correspondingPropertyTypeRef = correspondingProperty.typeReference
KotlinType propertyType = context.get(BindingContext.TYPE, correspondingPropertyTypeRef); val propertyType = context.get(BindingContext.TYPE, correspondingPropertyTypeRef)
if (propertyType != null && !KotlinTypeChecker.DEFAULT.isSubtypeOf(eventualFunctionLiteralType, propertyType)) { if (propertyType != null && !KotlinTypeChecker.DEFAULT.isSubtypeOf(eventualFunctionLiteralType, propertyType)) {
appropriateQuickFix = new ChangeVariableTypeFix(correspondingProperty, eventualFunctionLiteralType); appropriateQuickFix = ChangeVariableTypeFix(correspondingProperty, eventualFunctionLiteralType)
} }
return; return
} }
ResolvedCall<? extends CallableDescriptor> resolvedCall = CallUtilKt.getParentResolvedCall( val resolvedCall = functionLiteralExpression.getParentResolvedCall(context, true)
functionLiteralExpression, context, true);
if (resolvedCall != null) { if (resolvedCall != null) {
ValueArgument valueArgument = CallUtilKt.getValueArgumentForExpression(resolvedCall.getCall(), functionLiteralExpression); val valueArgument = resolvedCall.call.getValueArgumentForExpression(functionLiteralExpression)
KtParameter correspondingParameter = QuickFixUtil.getParameterDeclarationForValueArgument(resolvedCall, valueArgument); val correspondingParameter = QuickFixUtil.getParameterDeclarationForValueArgument(resolvedCall, valueArgument)
if (correspondingParameter != null) { if (correspondingParameter != null) {
KtTypeReference correspondingParameterTypeRef = correspondingParameter.getTypeReference(); val correspondingParameterTypeRef = correspondingParameter.typeReference
KotlinType parameterType = context.get(BindingContext.TYPE, correspondingParameterTypeRef); val parameterType = context.get(BindingContext.TYPE, correspondingParameterTypeRef)
if (parameterType != null && !KotlinTypeChecker.DEFAULT.isSubtypeOf(eventualFunctionLiteralType, parameterType)) { if (parameterType != null && !KotlinTypeChecker.DEFAULT.isSubtypeOf(eventualFunctionLiteralType, parameterType)) {
appropriateQuickFix = new ChangeParameterTypeFix(correspondingParameter, eventualFunctionLiteralType); appropriateQuickFix = ChangeParameterTypeFix(correspondingParameter, eventualFunctionLiteralType)
} }
return; return
} }
} }
KtFunction parentFunction = PsiTreeUtil.getParentOfType(functionLiteralExpression, KtFunction.class, true); val parentFunction = PsiTreeUtil.getParentOfType(functionLiteralExpression, KtFunction::class.java, true)
if (parentFunction != null && QuickFixUtil.canFunctionOrGetterReturnExpression(parentFunction, functionLiteralExpression)) { if (parentFunction != null && QuickFixUtil.canFunctionOrGetterReturnExpression(parentFunction, functionLiteralExpression)) {
KtTypeReference parentFunctionReturnTypeRef = parentFunction.getTypeReference(); val parentFunctionReturnTypeRef = parentFunction.typeReference
KotlinType parentFunctionReturnType = context.get(BindingContext.TYPE, parentFunctionReturnTypeRef); val parentFunctionReturnType = context.get(BindingContext.TYPE, parentFunctionReturnTypeRef)
if (parentFunctionReturnType != null && !KotlinTypeChecker.DEFAULT.isSubtypeOf(eventualFunctionLiteralType, parentFunctionReturnType)) { if (parentFunctionReturnType != null && !KotlinTypeChecker.DEFAULT.isSubtypeOf(eventualFunctionLiteralType, parentFunctionReturnType)) {
appropriateQuickFix = new ChangeFunctionReturnTypeFix(parentFunction, eventualFunctionLiteralType); appropriateQuickFix = ChangeFunctionReturnTypeFix(parentFunction, eventualFunctionLiteralType)
} }
} }
} }
@NotNull override fun getText(): String {
@Override
public String getText() {
if (appropriateQuickFix != null) { if (appropriateQuickFix != null) {
return appropriateQuickFix.getText(); return appropriateQuickFix!!.text
} }
return String.format("Change lambda expression return type to '%s'", return String.format("Change lambda expression return type to '%s'",
IdeDescriptorRenderers.SOURCE_CODE_SHORT_NAMES_IN_TYPES.renderType(type)); IdeDescriptorRenderers.SOURCE_CODE_SHORT_NAMES_IN_TYPES.renderType(type))
} }
@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, Editor editor, @NotNull PsiFile file) { return super.isAvailable(project, editor, file) && (functionLiteralReturnTypeRef != null || appropriateQuickFix != null && appropriateQuickFix!!.isAvailable(project, editor!!, file))
return super.isAvailable(project, editor, file) &&
(functionLiteralReturnTypeRef != null || (appropriateQuickFix != null && appropriateQuickFix.isAvailable(project, editor, file)));
} }
@Override override fun invoke(project: Project, editor: Editor?, file: KtFile) {
public void invoke(@NotNull Project project, Editor editor, @NotNull KtFile file) throws IncorrectOperationException {
if (functionLiteralReturnTypeRef != null) { if (functionLiteralReturnTypeRef != null) {
KtTypeReference newTypeRef = KtPsiFactoryKt.KtPsiFactory(file).createType(IdeDescriptorRenderers.SOURCE_CODE.renderType(type)); var newTypeRef = KtPsiFactory(file).createType(IdeDescriptorRenderers.SOURCE_CODE.renderType(type))
newTypeRef = (KtTypeReference) functionLiteralReturnTypeRef.replace(newTypeRef); newTypeRef = functionLiteralReturnTypeRef.replace(newTypeRef) as KtTypeReference
ShortenReferences.DEFAULT.process(newTypeRef); ShortenReferences.DEFAULT.process(newTypeRef)
} }
if (appropriateQuickFix != null && appropriateQuickFix.isAvailable(project, editor, file)) { if (appropriateQuickFix != null && appropriateQuickFix!!.isAvailable(project, editor!!, file)) {
appropriateQuickFix.invoke(project, editor, file); appropriateQuickFix!!.invoke(project, editor, file)
} }
} }
@NotNull companion object {
public static KotlinSingleIntentionActionFactory createFactoryForExpectedOrAssignmentTypeMismatch() { fun createFactoryForExpectedOrAssignmentTypeMismatch(): KotlinSingleIntentionActionFactory {
return new KotlinSingleIntentionActionFactory() { return object : KotlinSingleIntentionActionFactory() {
@Nullable public override fun createAction(diagnostic: Diagnostic): IntentionAction? {
@Override val functionLiteralExpression = QuickFixUtil.getParentElementOfType(diagnostic, KtLambdaExpression::class.java) ?: return null
public IntentionAction createAction(@NotNull Diagnostic diagnostic) { return ChangeFunctionLiteralReturnTypeFix(functionLiteralExpression, functionLiteralExpression.platform.builtIns.getUnitType())
KtLambdaExpression }
functionLiteralExpression = QuickFixUtil.getParentElementOfType(diagnostic, KtLambdaExpression.class);
if (functionLiteralExpression == null) return null;
return new ChangeFunctionLiteralReturnTypeFix(functionLiteralExpression, getPlatform(functionLiteralExpression).getBuiltIns().getUnitType());
} }
}; }
} }
} }