diff --git a/java/java-impl/src/com/intellij/codeInsight/daemon/impl/analysis/HighlightUtil.java b/java/java-impl/src/com/intellij/codeInsight/daemon/impl/analysis/HighlightUtil.java index 5d6a82078bc7..f56bd1f662cb 100644 --- a/java/java-impl/src/com/intellij/codeInsight/daemon/impl/analysis/HighlightUtil.java +++ b/java/java-impl/src/com/intellij/codeInsight/daemon/impl/analysis/HighlightUtil.java @@ -490,6 +490,7 @@ public class HighlightUtil { } if (expression != null && lType != null) { QuickFixAction.registerQuickFixAction(highlightInfo, new WrapExpressionFix(lType, expression)); + AddTypeArgumentsConditionalFix.register(highlightInfo, expression, lType); } ChangeNewOperatorTypeFix.register(highlightInfo, expression, lType); return highlightInfo; diff --git a/java/java-impl/src/com/intellij/codeInsight/daemon/impl/quickfix/AddTypeArgumentsConditionalFix.java b/java/java-impl/src/com/intellij/codeInsight/daemon/impl/quickfix/AddTypeArgumentsConditionalFix.java new file mode 100644 index 000000000000..f1392ed12460 --- /dev/null +++ b/java/java-impl/src/com/intellij/codeInsight/daemon/impl/quickfix/AddTypeArgumentsConditionalFix.java @@ -0,0 +1,153 @@ +/* + * 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. + */ +package com.intellij.codeInsight.daemon.impl.quickfix; + +import com.intellij.codeInsight.daemon.impl.HighlightInfo; +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.TypeConversionUtil; +import com.intellij.refactoring.util.RefactoringUtil; +import com.intellij.util.Function; +import com.intellij.util.IncorrectOperationException; +import org.jetbrains.annotations.NotNull; + +/** + * User: anna + * Date: 2/17/12 + */ +public class AddTypeArgumentsConditionalFix implements IntentionAction { + private static final Logger LOG = Logger.getInstance("#" + AddTypeArgumentsConditionalFix.class.getName()); + + private final PsiSubstitutor mySubstitutor; + private final PsiMethodCallExpression myExpression; + private final PsiMethod myMethod; + + public AddTypeArgumentsConditionalFix(PsiSubstitutor substitutor, PsiMethodCallExpression expression, PsiMethod method) { + mySubstitutor = substitutor; + myExpression = expression; + myMethod = method; + } + + @NotNull + @Override + public String getText() { + return "Add explicit type arguments"; + } + + @NotNull + @Override + public String getFamilyName() { + return getText(); + } + + @Override + public boolean isAvailable(@NotNull Project project, Editor editor, PsiFile file) { + if (mySubstitutor.isValid() && myExpression.isValid() && myMethod.isValid()) { + return true; + } + return false; + } + + @Override + public void invoke(@NotNull Project project, Editor editor, PsiFile file) throws IncorrectOperationException { + final PsiTypeParameter[] typeParameters = myMethod.getTypeParameters(); + final String typeArguments = "<" + StringUtil.join(typeParameters, new Function() { + @Override + public String fun(PsiTypeParameter parameter) { + final PsiType substituteTypeParam = mySubstitutor.substitute(parameter); + LOG.assertTrue(substituteTypeParam != null); + return substituteTypeParam.getCanonicalText(); + } + }, ", ") + ">"; + final PsiExpression expression = myExpression.getMethodExpression().getQualifierExpression(); + String withTypeArgsText; + if (expression != null) { + withTypeArgsText = expression.getText(); + } + else { + if (RefactoringUtil.isInStaticContext(myExpression, null) || myMethod.hasModifierProperty(PsiModifier.STATIC)) { + final PsiClass aClass = myMethod.getContainingClass(); + LOG.assertTrue(aClass != null); + withTypeArgsText = aClass.getQualifiedName(); + } + else { + withTypeArgsText = "this"; + } + } + withTypeArgsText += "." + typeArguments + myExpression.getMethodExpression().getReferenceName(); + final PsiExpression withTypeArgs = JavaPsiFacade.getElementFactory(project).createExpressionFromText(withTypeArgsText + myExpression.getArgumentList().getText(), myExpression); + myExpression.replace(withTypeArgs); + } + + @Override + public boolean startInWriteAction() { + return true; + } + + public static void register(HighlightInfo highlightInfo, PsiExpression expression, PsiType lType) { + if (expression instanceof PsiConditionalExpression) { + final PsiExpression thenExpression = ((PsiConditionalExpression)expression).getThenExpression(); + final PsiExpression elseExpression = ((PsiConditionalExpression)expression).getElseExpression(); + if (thenExpression != null && elseExpression != null) { + final PsiType thenType = thenExpression.getType(); + final PsiType elseType = elseExpression.getType(); + if (thenType != null && elseType != null) { + final boolean thenAssignable = TypeConversionUtil.isAssignable(lType, thenType); + final boolean elseAssignable = TypeConversionUtil.isAssignable(lType, elseType); + if (!thenAssignable && thenExpression instanceof PsiMethodCallExpression) { + inferTypeArgs(highlightInfo, lType, thenExpression); + } + if (!elseAssignable && elseExpression instanceof PsiMethodCallExpression) { + inferTypeArgs(highlightInfo, lType, elseExpression); + } + } + } + } + } + + private static void inferTypeArgs(HighlightInfo highlightInfo, PsiType lType, PsiExpression thenExpression) { + final JavaResolveResult result = ((PsiMethodCallExpression)thenExpression).resolveMethodGenerics(); + final PsiMethod method = (PsiMethod)result.getElement(); + if (method != null) { + final PsiType returnType = method.getReturnType(); + final PsiClass aClass = method.getContainingClass(); + if (returnType != null && aClass != null && aClass.getQualifiedName() != null) { + final JavaPsiFacade javaPsiFacade = JavaPsiFacade.getInstance(method.getProject()); + final PsiDeclarationStatement variableDeclarationStatement = + javaPsiFacade.getElementFactory().createVariableDeclarationStatement("xxx", lType, thenExpression); + final PsiExpression initializer = + ((PsiLocalVariable)variableDeclarationStatement.getDeclaredElements()[0]).getInitializer(); + LOG.assertTrue(initializer != null); + + final PsiSubstitutor substitutor = javaPsiFacade.getResolveHelper() + .inferTypeArguments(method.getTypeParameters(), method.getParameterList().getParameters(), + ((PsiMethodCallExpression)thenExpression).getArgumentList().getExpressions(), PsiSubstitutor.EMPTY, + initializer, DefaultParameterTypeInferencePolicy.INSTANCE); + PsiType substitutedType = substitutor.substitute(returnType); + if (substitutedType != null && TypeConversionUtil.isAssignable(lType, substitutedType)) { + QuickFixAction.registerQuickFixAction(highlightInfo, + thenExpression.getTextRange(), + new AddTypeArgumentsConditionalFix(substitutor, (PsiMethodCallExpression)thenExpression, method), null); + } + } + } + } +} diff --git a/java/java-impl/src/com/intellij/refactoring/util/RefactoringUtil.java b/java/java-impl/src/com/intellij/refactoring/util/RefactoringUtil.java index 9093461943bb..8f1e53ec583d 100644 --- a/java/java-impl/src/com/intellij/refactoring/util/RefactoringUtil.java +++ b/java/java-impl/src/com/intellij/refactoring/util/RefactoringUtil.java @@ -80,7 +80,7 @@ public class RefactoringUtil { return Comparing.equal(virtualFile, sourceRootForFile); } - public static boolean isInStaticContext(PsiElement element, final PsiClass aClass) { + public static boolean isInStaticContext(PsiElement element, @Nullable final PsiClass aClass) { return PsiUtil.getEnclosingStaticElement(element, aClass) != null; } diff --git a/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/addTypeArgumentsConditional/afterBothParts.java b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/addTypeArgumentsConditional/afterBothParts.java new file mode 100644 index 000000000000..b225d5486481 --- /dev/null +++ b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/addTypeArgumentsConditional/afterBothParts.java @@ -0,0 +1,9 @@ +// "Add explicit type arguments" "true" +import java.util.*; + +class Test { + static List f() { return new ArrayList(); } + void someMethod(boolean b) { + List s = b ? Test.f() : f(); + } +} \ No newline at end of file diff --git a/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/addTypeArgumentsConditional/afterInstanceMethod.java b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/addTypeArgumentsConditional/afterInstanceMethod.java new file mode 100644 index 000000000000..5783cef30336 --- /dev/null +++ b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/addTypeArgumentsConditional/afterInstanceMethod.java @@ -0,0 +1,9 @@ +// "Add explicit type arguments" "true" +import java.util.*; + +class Test { + List f() { return new ArrayList(); } + void someMethod(boolean b) { + List s = b ? this.f() : new ArrayList(); + } +} \ No newline at end of file diff --git a/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/addTypeArgumentsConditional/afterOuterClass.java b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/addTypeArgumentsConditional/afterOuterClass.java new file mode 100644 index 000000000000..865fd9fe29aa --- /dev/null +++ b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/addTypeArgumentsConditional/afterOuterClass.java @@ -0,0 +1,13 @@ +// "Add explicit type arguments" "true" +import java.util.*; + +class Test { + + void someMethod(boolean b) { + List s = b ? Foo.f() : new ArrayList(); + } +} + +class Foo{ + static List f() { return new ArrayList(); } +} \ No newline at end of file diff --git a/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/addTypeArgumentsConditional/afterQualifiedInstance.java b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/addTypeArgumentsConditional/afterQualifiedInstance.java new file mode 100644 index 000000000000..bdf85c93150d --- /dev/null +++ b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/addTypeArgumentsConditional/afterQualifiedInstance.java @@ -0,0 +1,9 @@ +// "Add explicit type arguments" "true" +import java.util.*; + +class Test { + List f() { return new ArrayList(); } + void someMethod(Test t, boolean b) { + List s = b ? t.f() : new ArrayList(); + } +} \ No newline at end of file diff --git a/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/addTypeArgumentsConditional/afterQualifiedStatic.java b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/addTypeArgumentsConditional/afterQualifiedStatic.java new file mode 100644 index 000000000000..fd0682ac09ee --- /dev/null +++ b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/addTypeArgumentsConditional/afterQualifiedStatic.java @@ -0,0 +1,9 @@ +// "Add explicit type arguments" "true" +import java.util.*; + +class Test { + static List f() { return new ArrayList(); } + void someMethod(Test t, boolean b) { + List s = b ? t.f() : new ArrayList(); + } +} \ No newline at end of file diff --git a/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/addTypeArgumentsConditional/afterUnqualifiedStatic.java b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/addTypeArgumentsConditional/afterUnqualifiedStatic.java new file mode 100644 index 000000000000..8852c76f6bb3 --- /dev/null +++ b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/addTypeArgumentsConditional/afterUnqualifiedStatic.java @@ -0,0 +1,9 @@ +// "Add explicit type arguments" "true" +import java.util.*; + +class Test { + static List f() { return new ArrayList(); } + void someMethod(boolean b) { + List s = b ? Test.f() : new ArrayList(); + } +} \ No newline at end of file diff --git a/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/addTypeArgumentsConditional/beforeBothParts.java b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/addTypeArgumentsConditional/beforeBothParts.java new file mode 100644 index 000000000000..c8158411b8a8 --- /dev/null +++ b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/addTypeArgumentsConditional/beforeBothParts.java @@ -0,0 +1,9 @@ +// "Add explicit type arguments" "true" +import java.util.*; + +class Test { + static List f() { return new ArrayList(); } + void someMethod(boolean b) { + List s = b ? f() : f(); + } +} \ No newline at end of file diff --git a/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/addTypeArgumentsConditional/beforeInstanceMethod.java b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/addTypeArgumentsConditional/beforeInstanceMethod.java new file mode 100644 index 000000000000..eab38f7d274b --- /dev/null +++ b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/addTypeArgumentsConditional/beforeInstanceMethod.java @@ -0,0 +1,9 @@ +// "Add explicit type arguments" "true" +import java.util.*; + +class Test { + List f() { return new ArrayList(); } + void someMethod(boolean b) { + List s = b ? f() : new ArrayList(); + } +} \ No newline at end of file diff --git a/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/addTypeArgumentsConditional/beforeOuterClass.java b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/addTypeArgumentsConditional/beforeOuterClass.java new file mode 100644 index 000000000000..f92c2de9280d --- /dev/null +++ b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/addTypeArgumentsConditional/beforeOuterClass.java @@ -0,0 +1,13 @@ +// "Add explicit type arguments" "true" +import java.util.*; + +class Test { + + void someMethod(boolean b) { + List s = b ? Foo.f() : new ArrayList(); + } +} + +class Foo{ + static List f() { return new ArrayList(); } +} \ No newline at end of file diff --git a/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/addTypeArgumentsConditional/beforeQualifiedInstance.java b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/addTypeArgumentsConditional/beforeQualifiedInstance.java new file mode 100644 index 000000000000..ded79198610e --- /dev/null +++ b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/addTypeArgumentsConditional/beforeQualifiedInstance.java @@ -0,0 +1,9 @@ +// "Add explicit type arguments" "true" +import java.util.*; + +class Test { + List f() { return new ArrayList(); } + void someMethod(Test t, boolean b) { + List s = b ? t.f() : new ArrayList(); + } +} \ No newline at end of file diff --git a/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/addTypeArgumentsConditional/beforeQualifiedStatic.java b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/addTypeArgumentsConditional/beforeQualifiedStatic.java new file mode 100644 index 000000000000..befd23c30449 --- /dev/null +++ b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/addTypeArgumentsConditional/beforeQualifiedStatic.java @@ -0,0 +1,9 @@ +// "Add explicit type arguments" "true" +import java.util.*; + +class Test { + static List f() { return new ArrayList(); } + void someMethod(Test t, boolean b) { + List s = b ? t.f() : new ArrayList(); + } +} \ No newline at end of file diff --git a/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/addTypeArgumentsConditional/beforeUnqualifiedStatic.java b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/addTypeArgumentsConditional/beforeUnqualifiedStatic.java new file mode 100644 index 000000000000..4f62c3aed444 --- /dev/null +++ b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/addTypeArgumentsConditional/beforeUnqualifiedStatic.java @@ -0,0 +1,9 @@ +// "Add explicit type arguments" "true" +import java.util.*; + +class Test { + static List f() { return new ArrayList(); } + void someMethod(boolean b) { + List s = b ? f() : new ArrayList(); + } +} \ No newline at end of file diff --git a/java/java-tests/testSrc/com/intellij/codeInsight/daemon/quickFix/AddTypeArgumentsConditionalTest.java b/java/java-tests/testSrc/com/intellij/codeInsight/daemon/quickFix/AddTypeArgumentsConditionalTest.java new file mode 100644 index 000000000000..0f13cf303856 --- /dev/null +++ b/java/java-tests/testSrc/com/intellij/codeInsight/daemon/quickFix/AddTypeArgumentsConditionalTest.java @@ -0,0 +1,26 @@ +/* + * 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. + */ +package com.intellij.codeInsight.daemon.quickFix; + +public class AddTypeArgumentsConditionalTest extends LightQuickFix15TestCase { + + public void test() throws Exception { doAllTests(); } + + @Override + protected String getBasePath() { + return "/codeInsight/daemonCodeAnalyzer/quickFix/addTypeArgumentsConditional"; + } +}