From 679e1c6bcf94f7578bff92fa4a75835d70c1db79 Mon Sep 17 00:00:00 2001 From: Anna Kozlova Date: Thu, 14 Jun 2012 20:33:26 +0400 Subject: [PATCH] change type args fix (IDEA-84097) --- .../impl/analysis/HighlightMethodUtil.java | 1 + .../impl/quickfix/ChangeTypeArgumentsFix.java | 165 ++++++++++++++++++ .../quickFix/changeTypeArgs/after1.java | 11 ++ .../changeTypeArgs/afterMultipleParams.java | 11 ++ .../changeTypeArgs/afterSuperType.java | 13 ++ .../quickFix/changeTypeArgs/before1.java | 11 ++ .../beforeCorrectTypeParam.java | 11 ++ .../changeTypeArgs/beforeDisabled.java | 13 ++ .../changeTypeArgs/beforeIncomplete.java | 11 ++ .../changeTypeArgs/beforeIncomplete2.java | 11 ++ .../changeTypeArgs/beforeMultipleParams.java | 11 ++ .../changeTypeArgs/beforeSuperType.java | 11 ++ 12 files changed, 280 insertions(+) create mode 100644 java/java-impl/src/com/intellij/codeInsight/daemon/impl/quickfix/ChangeTypeArgumentsFix.java create mode 100644 java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/changeTypeArgs/after1.java create mode 100644 java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/changeTypeArgs/afterMultipleParams.java create mode 100644 java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/changeTypeArgs/afterSuperType.java create mode 100644 java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/changeTypeArgs/before1.java create mode 100644 java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/changeTypeArgs/beforeCorrectTypeParam.java create mode 100644 java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/changeTypeArgs/beforeDisabled.java create mode 100644 java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/changeTypeArgs/beforeIncomplete.java create mode 100644 java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/changeTypeArgs/beforeIncomplete2.java create mode 100644 java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/changeTypeArgs/beforeMultipleParams.java create mode 100644 java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/changeTypeArgs/beforeSuperType.java diff --git a/java/java-impl/src/com/intellij/codeInsight/daemon/impl/analysis/HighlightMethodUtil.java b/java/java-impl/src/com/intellij/codeInsight/daemon/impl/analysis/HighlightMethodUtil.java index 555cbfef7581..14e7352028ee 100644 --- a/java/java-impl/src/com/intellij/codeInsight/daemon/impl/analysis/HighlightMethodUtil.java +++ b/java/java-impl/src/com/intellij/codeInsight/daemon/impl/analysis/HighlightMethodUtil.java @@ -1278,6 +1278,7 @@ public class HighlightMethodUtil { if (classReference != null) { ConstructorParametersFixer.registerFixActions(classReference, constructorCall, info, getFixRange(infoElement)); ChangeMethodSignatureFromUsageFix.registerIntentions(results, list, info, null); + ChangeTypeArgumentsFix.registerIntentions(results, list, info, aClass); ConvertDoubleToFloatFix.registerIntentions(results, list, info, null); PermuteArgumentsFix.registerFix(info, constructorCall, toMethodCandidates(results), getFixRange(list)); ChangeParameterClassFix.registerQuickFixActions(constructorCall, list, info); diff --git a/java/java-impl/src/com/intellij/codeInsight/daemon/impl/quickfix/ChangeTypeArgumentsFix.java b/java/java-impl/src/com/intellij/codeInsight/daemon/impl/quickfix/ChangeTypeArgumentsFix.java new file mode 100644 index 000000000000..3945e632a75a --- /dev/null +++ b/java/java-impl/src/com/intellij/codeInsight/daemon/impl/quickfix/ChangeTypeArgumentsFix.java @@ -0,0 +1,165 @@ +/* + * Copyright 2000-2012 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + * Created by IntelliJ IDEA. + * User: cdr + * Date: Nov 13, 2002 + * Time: 3:26:50 PM + * To change this template use Options | File Templates. + */ +package com.intellij.codeInsight.daemon.impl.quickfix; + +import com.intellij.codeInsight.CodeInsightUtilBase; +import com.intellij.codeInsight.daemon.impl.HighlightInfo; +import com.intellij.codeInsight.intention.HighPriorityAction; +import com.intellij.codeInsight.intention.IntentionAction; +import com.intellij.openapi.diagnostic.Logger; +import com.intellij.openapi.editor.Editor; +import com.intellij.openapi.project.Project; +import com.intellij.openapi.util.text.StringUtil; +import com.intellij.psi.*; +import com.intellij.psi.impl.source.resolve.DefaultParameterTypeInferencePolicy; +import com.intellij.psi.util.PsiTreeUtil; +import com.intellij.psi.util.TypeConversionUtil; +import com.intellij.util.Function; +import org.jetbrains.annotations.NotNull; + +public class ChangeTypeArgumentsFix implements IntentionAction, HighPriorityAction { + private final PsiMethod myTargetMethod; + private final PsiClass myPsiClass; + private final PsiExpression[] myExpressions; + private static final Logger LOG = Logger.getInstance("#" + ChangeTypeArgumentsFix.class.getName()); + private final PsiNewExpression myNewExpression; + + ChangeTypeArgumentsFix(@NotNull PsiMethod targetMethod, + PsiClass psiClass, + @NotNull PsiExpression[] expressions, + @NotNull PsiElement context) { + myTargetMethod = targetMethod; + myPsiClass = psiClass; + myExpressions = expressions; + myNewExpression = PsiTreeUtil.getParentOfType(context, PsiNewExpression.class); + } + + @Override + @NotNull + public String getText() { + final PsiSubstitutor substitutor = inferTypeArguments(); + return "Change type arguments to <" + StringUtil.join(myPsiClass.getTypeParameters(), new Function() { + @Override + public String fun(PsiTypeParameter typeParameter) { + return substitutor.substitute(typeParameter).getPresentableText(); + } + }, ", ") + ">"; + } + + + @Override + @NotNull + public String getFamilyName() { + return "Change type arguments"; + } + + @Override + public boolean isAvailable(@NotNull Project project, Editor editor, PsiFile file) { + final PsiTypeParameter[] typeParameters = myPsiClass.getTypeParameters(); + if (typeParameters.length > 0) { + if (myNewExpression != null && myNewExpression.isValid() && myNewExpression.getArgumentList() != null) { + final PsiJavaCodeReferenceElement reference = myNewExpression.getClassOrAnonymousClassReference(); + if (reference != null) { + final PsiReferenceParameterList parameterList = reference.getParameterList(); + if (parameterList != null) { + final PsiSubstitutor substitutor = inferTypeArguments(); + final PsiParameter[] parameters = myTargetMethod.getParameterList().getParameters(); + if (parameters.length != myExpressions.length) return false; + for (int i = 0, length = parameters.length; i < length; i++) { + PsiParameter parameter = parameters[i]; + final PsiType expectedType = substitutor.substitute(parameter.getType()); + if (!myExpressions[i].isValid()) return false; + final PsiType actualType = myExpressions[i].getType(); + if (expectedType == null || actualType == null || !TypeConversionUtil.isAssignable(expectedType, actualType)) return false; + } + return true; + } + } + } + } + return false; + } + + @Override + public void invoke(@NotNull final Project project, Editor editor, final PsiFile file) { + if (!CodeInsightUtilBase.prepareFileForWrite(file)) return; + + final PsiTypeParameter[] typeParameters = myPsiClass.getTypeParameters(); + final PsiSubstitutor psiSubstitutor = inferTypeArguments(); + final PsiJavaCodeReferenceElement reference = myNewExpression.getClassOrAnonymousClassReference(); + LOG.assertTrue(reference != null, myNewExpression); + final PsiReferenceParameterList parameterList = reference.getParameterList(); + LOG.assertTrue(parameterList != null, myNewExpression); + PsiTypeElement[] elements = parameterList.getTypeParameterElements(); + for (int i = elements.length - 1; i >= 0; i--) { + PsiTypeElement typeElement = elements[i]; + final PsiType typeArg = psiSubstitutor.substitute(typeParameters[i]); + typeElement.replace(JavaPsiFacade.getElementFactory(project).createTypeElement(typeArg)); + } + } + + private PsiSubstitutor inferTypeArguments() { + final JavaPsiFacade facade = JavaPsiFacade.getInstance(myNewExpression.getProject()); + final PsiResolveHelper resolveHelper = facade.getResolveHelper(); + final PsiParameter[] parameters = myTargetMethod.getParameterList().getParameters(); + final PsiExpressionList argumentList = myNewExpression.getArgumentList(); + LOG.assertTrue(argumentList != null); + final PsiExpression[] expressions = argumentList.getExpressions(); + return resolveHelper.inferTypeArguments(myPsiClass.getTypeParameters(), parameters, expressions, + PsiSubstitutor.EMPTY, + myNewExpression.getParent(), + DefaultParameterTypeInferencePolicy.INSTANCE); + } + + + public static void registerIntentions(@NotNull JavaResolveResult[] candidates, + @NotNull PsiExpressionList list, + @NotNull HighlightInfo highlightInfo, + PsiClass psiClass) { + if (candidates.length == 0) return; + PsiExpression[] expressions = list.getExpressions(); + for (JavaResolveResult candidate : candidates) { + registerIntention(expressions, highlightInfo, psiClass, candidate, list); + } + } + + private static void registerIntention(@NotNull PsiExpression[] expressions, + @NotNull HighlightInfo highlightInfo, + PsiClass psiClass, + @NotNull JavaResolveResult candidate, + @NotNull PsiElement context) { + if (!candidate.isStaticsScopeCorrect()) return; + PsiMethod method = (PsiMethod)candidate.getElement(); + PsiSubstitutor substitutor = candidate.getSubstitutor(); + if (method != null && context.getManager().isInProject(method)) { + final ChangeTypeArgumentsFix fix = new ChangeTypeArgumentsFix(method, psiClass, expressions, context); + QuickFixAction.registerQuickFixAction(highlightInfo, null, fix); + } + } + + @Override + public boolean startInWriteAction() { + return true; + } +} diff --git a/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/changeTypeArgs/after1.java b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/changeTypeArgs/after1.java new file mode 100644 index 000000000000..430b68756e80 --- /dev/null +++ b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/changeTypeArgs/after1.java @@ -0,0 +1,11 @@ +// "Change type arguments to " "true" +class Generic { + Generic(E arg) { + } +} + +class Tester { + void method() { + new Generic("hi"); + } +} \ No newline at end of file diff --git a/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/changeTypeArgs/afterMultipleParams.java b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/changeTypeArgs/afterMultipleParams.java new file mode 100644 index 000000000000..55ff7b66dbb4 --- /dev/null +++ b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/changeTypeArgs/afterMultipleParams.java @@ -0,0 +1,11 @@ +// "Change type arguments to " "true" +class Generic { + Generic(E arg, K arg1) { + } +} + +class Tester { + void method() { + new Generic("hi", 1); + } +} \ No newline at end of file diff --git a/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/changeTypeArgs/afterSuperType.java b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/changeTypeArgs/afterSuperType.java new file mode 100644 index 000000000000..337708e8fb14 --- /dev/null +++ b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/changeTypeArgs/afterSuperType.java @@ -0,0 +1,13 @@ +import java.io.Serializable; + +// "Change type arguments to " "true" +class Generic { + Generic(E arg, E arg1) { + } +} + +class Tester { + void method() { + new Generic("hi", 1); + } +} \ No newline at end of file diff --git a/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/changeTypeArgs/before1.java b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/changeTypeArgs/before1.java new file mode 100644 index 000000000000..dd660b853e5c --- /dev/null +++ b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/changeTypeArgs/before1.java @@ -0,0 +1,11 @@ +// "Change type arguments to " "true" +class Generic { + Generic(E arg) { + } +} + +class Tester { + void method() { + new Generic("hi"); + } +} \ No newline at end of file diff --git a/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/changeTypeArgs/beforeCorrectTypeParam.java b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/changeTypeArgs/beforeCorrectTypeParam.java new file mode 100644 index 000000000000..3d3c223e7260 --- /dev/null +++ b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/changeTypeArgs/beforeCorrectTypeParam.java @@ -0,0 +1,11 @@ +// "Change type arguments to " "false" +class Generic { + Generic(E arg, int i) { + } +} + +class Tester { + void method() { + new Generic("hi", ""); + } +} \ No newline at end of file diff --git a/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/changeTypeArgs/beforeDisabled.java b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/changeTypeArgs/beforeDisabled.java new file mode 100644 index 000000000000..2869486b752e --- /dev/null +++ b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/changeTypeArgs/beforeDisabled.java @@ -0,0 +1,13 @@ +// "Change type arguments to " "false" +import java.util.List; + +class Generic { + Generic(E arg, List arg1) { + } +} + +class Tester { + void method() { + new Generic("hi", "hi"); + } +} \ No newline at end of file diff --git a/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/changeTypeArgs/beforeIncomplete.java b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/changeTypeArgs/beforeIncomplete.java new file mode 100644 index 000000000000..b6b5b04cbf2a --- /dev/null +++ b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/changeTypeArgs/beforeIncomplete.java @@ -0,0 +1,11 @@ +// "Change type arguments to " "false" +class Generic { + Generic(E arg) { + } +} + +class Tester { + void method() { + new Generic("hi" + } +} \ No newline at end of file diff --git a/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/changeTypeArgs/beforeIncomplete2.java b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/changeTypeArgs/beforeIncomplete2.java new file mode 100644 index 000000000000..9b1def3056fc --- /dev/null +++ b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/changeTypeArgs/beforeIncomplete2.java @@ -0,0 +1,11 @@ +// "Change type arguments to " "false" +class Generic { + Generic(E arg, int i) { + } +} + +class Tester { + void method() { + new Generic("hi"); + } +} \ No newline at end of file diff --git a/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/changeTypeArgs/beforeMultipleParams.java b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/changeTypeArgs/beforeMultipleParams.java new file mode 100644 index 000000000000..896686b641fc --- /dev/null +++ b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/changeTypeArgs/beforeMultipleParams.java @@ -0,0 +1,11 @@ +// "Change type arguments to " "true" +class Generic { + Generic(E arg, K arg1) { + } +} + +class Tester { + void method() { + new Generic("hi", 1); + } +} \ No newline at end of file diff --git a/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/changeTypeArgs/beforeSuperType.java b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/changeTypeArgs/beforeSuperType.java new file mode 100644 index 000000000000..1b6580752258 --- /dev/null +++ b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/changeTypeArgs/beforeSuperType.java @@ -0,0 +1,11 @@ +// "Change type arguments to " "true" +class Generic { + Generic(E arg, E arg1) { + } +} + +class Tester { + void method() { + new Generic("hi", 1); + } +} \ No newline at end of file