diff options
Diffstat (limited to 'java/java-analysis-impl/src/com')
11 files changed, 319 insertions, 107 deletions
diff --git a/java/java-analysis-impl/src/com/intellij/codeInsight/daemon/impl/analysis/GenericsHighlightUtil.java b/java/java-analysis-impl/src/com/intellij/codeInsight/daemon/impl/analysis/GenericsHighlightUtil.java index 324a222a018d..72ad0f26efa1 100644 --- a/java/java-analysis-impl/src/com/intellij/codeInsight/daemon/impl/analysis/GenericsHighlightUtil.java +++ b/java/java-analysis-impl/src/com/intellij/codeInsight/daemon/impl/analysis/GenericsHighlightUtil.java @@ -406,8 +406,9 @@ public class GenericsHighlightUtil { if (languageLevel.isAtLeast(LanguageLevel.JDK_1_8) && aClass.isInterface() && method.hasModifierProperty(PsiModifier.DEFAULT)) { HierarchicalMethodSignature sig = method.getHierarchicalMethodSignature(); for (HierarchicalMethodSignature methodSignature : sig.getSuperSignatures()) { - final PsiClass containingClass = methodSignature.getMethod().getContainingClass(); - if (containingClass != null && CommonClassNames.JAVA_LANG_OBJECT.equals(containingClass.getQualifiedName())) { + final PsiMethod objectMethod = methodSignature.getMethod(); + final PsiClass containingClass = objectMethod.getContainingClass(); + if (containingClass != null && CommonClassNames.JAVA_LANG_OBJECT.equals(containingClass.getQualifiedName()) && objectMethod.hasModifierProperty(PsiModifier.PUBLIC)) { return HighlightInfo.newHighlightInfo(HighlightInfoType.ERROR) .descriptionAndTooltip("Default method '" + sig.getName() + "' overrides a member of 'java.lang.Object'") .range(methodIdentifier) diff --git a/java/java-analysis-impl/src/com/intellij/codeInsight/daemon/impl/analysis/HighlightVisitorImpl.java b/java/java-analysis-impl/src/com/intellij/codeInsight/daemon/impl/analysis/HighlightVisitorImpl.java index 4e9d79fecf57..352e74557eed 100644 --- a/java/java-analysis-impl/src/com/intellij/codeInsight/daemon/impl/analysis/HighlightVisitorImpl.java +++ b/java/java-analysis-impl/src/com/intellij/codeInsight/daemon/impl/analysis/HighlightVisitorImpl.java @@ -1324,8 +1324,8 @@ public class HighlightVisitorImpl extends JavaElementVisitor implements Highligh myHolder.add(GenericsHighlightUtil.checkParametersAllowed(list, myLanguageLevel,myFile)); if (!myHolder.hasErrorResults()) myHolder.add(GenericsHighlightUtil.checkParametersOnRaw(list)); if (!myHolder.hasErrorResults()) { - for (PsiType type : list.getTypeArguments()) { - myHolder.add(HighlightUtil.checkDiamondFeature(type, list, myLanguageLevel,myFile)); + for (PsiTypeElement typeElement : list.getTypeParameterElements()) { + myHolder.add(HighlightUtil.checkDiamondFeature(typeElement.getType(), list, myLanguageLevel,myFile)); } } } diff --git a/java/java-analysis-impl/src/com/intellij/codeInsight/daemon/impl/quickfix/AddTypeCastFix.java b/java/java-analysis-impl/src/com/intellij/codeInsight/daemon/impl/quickfix/AddTypeCastFix.java index b9c5d21bcd5e..a5104f1deb51 100644 --- a/java/java-analysis-impl/src/com/intellij/codeInsight/daemon/impl/quickfix/AddTypeCastFix.java +++ b/java/java-analysis-impl/src/com/intellij/codeInsight/daemon/impl/quickfix/AddTypeCastFix.java @@ -1,5 +1,5 @@ /* - * Copyright 2000-2009 JetBrains s.r.o. + * Copyright 2000-2014 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. @@ -33,10 +33,11 @@ import com.intellij.psi.*; import com.intellij.psi.codeStyle.CodeStyleManager; import com.intellij.psi.util.PsiUtil; import com.intellij.psi.util.TypeConversionUtil; -import com.intellij.util.IncorrectOperationException; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; +import static com.intellij.util.ObjectUtils.assertNotNull; + public class AddTypeCastFix extends LocalQuickFixAndIntentionActionOnPsiElement { private final PsiType myType; @@ -75,28 +76,26 @@ public class AddTypeCastFix extends LocalQuickFixAndIntentionActionOnPsiElement addTypeCast(project, (PsiExpression)startElement, myType); } - private static void addTypeCast(Project project, PsiExpression originalExpression, PsiType type) throws IncorrectOperationException { + private static void addTypeCast(Project project, PsiExpression originalExpression, PsiType type) { PsiExpression typeCast = createCastExpression(originalExpression, project, type); originalExpression.replace(typeCast); } - static PsiExpression createCastExpression(PsiExpression originalExpression, Project project, PsiType type) throws IncorrectOperationException { + static PsiExpression createCastExpression(PsiExpression originalExpression, Project project, PsiType type) { // remove nested casts - PsiElement element = PsiUtil.deparenthesizeExpression(originalExpression); - if (element == null){ - return null; - } - PsiElementFactory factory = JavaPsiFacade.getInstance(originalExpression.getProject()).getElementFactory(); + PsiElement expression = PsiUtil.deparenthesizeExpression(originalExpression); + if (expression == null) return null; + PsiElementFactory factory = JavaPsiFacade.getInstance(originalExpression.getProject()).getElementFactory(); PsiTypeCastExpression typeCast = (PsiTypeCastExpression)factory.createExpressionFromText("(Type)value", null); + assertNotNull(typeCast.getCastType()).replace(factory.createTypeElement(type)); typeCast = (PsiTypeCastExpression)CodeStyleManager.getInstance(project).reformat(typeCast); - typeCast.getCastType().replace(factory.createTypeElement(type)); - if (element instanceof PsiConditionalExpression) { - // we'd better cast one branch of ternary expression if we could - PsiConditionalExpression expression = (PsiConditionalExpression)element.copy(); - PsiExpression thenE = expression.getThenExpression(); - PsiExpression elseE = expression.getElseExpression(); + if (expression instanceof PsiConditionalExpression) { + // we'd better cast one branch of ternary expression if we can + PsiConditionalExpression conditional = (PsiConditionalExpression)expression.copy(); + PsiExpression thenE = conditional.getThenExpression(); + PsiExpression elseE = conditional.getElseExpression(); PsiType thenType = thenE == null ? null : thenE.getType(); PsiType elseType = elseE == null ? null : elseE.getType(); if (elseType != null && thenType != null) { @@ -104,18 +103,20 @@ public class AddTypeCastFix extends LocalQuickFixAndIntentionActionOnPsiElement boolean replaceElse = !TypeConversionUtil.isAssignable(type, elseType); if (replaceThen != replaceElse) { if (replaceThen) { - typeCast.getOperand().replace(thenE); + assertNotNull(typeCast.getOperand()).replace(thenE); thenE.replace(typeCast); } else { - typeCast.getOperand().replace(elseE); + assertNotNull(typeCast.getOperand()).replace(elseE); elseE.replace(typeCast); } - return expression; + return conditional; } } } - typeCast.getOperand().replace(element); + + assertNotNull(typeCast.getOperand()).replace(expression); + return typeCast; } diff --git a/java/java-analysis-impl/src/com/intellij/codeInsight/daemon/impl/quickfix/ChangeTypeArgumentsFix.java b/java/java-analysis-impl/src/com/intellij/codeInsight/daemon/impl/quickfix/ChangeTypeArgumentsFix.java index 76580adaa5f4..b4a8d5b99d17 100644 --- a/java/java-analysis-impl/src/com/intellij/codeInsight/daemon/impl/quickfix/ChangeTypeArgumentsFix.java +++ b/java/java-analysis-impl/src/com/intellij/codeInsight/daemon/impl/quickfix/ChangeTypeArgumentsFix.java @@ -1,5 +1,5 @@ /* - * Copyright 2000-2012 JetBrains s.r.o. + * Copyright 2000-2014 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. @@ -13,14 +13,6 @@ * 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.FileModificationService; @@ -32,6 +24,7 @@ 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.codeStyle.JavaCodeStyleManager; import com.intellij.psi.impl.source.resolve.DefaultParameterTypeInferencePolicy; import com.intellij.psi.util.PsiTreeUtil; import com.intellij.psi.util.TypeConversionUtil; @@ -39,11 +32,18 @@ import com.intellij.util.Function; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; +import static com.intellij.util.ObjectUtils.assertNotNull; + +/** + * @author cdr + * @since Nov 13, 2002 + */ public class ChangeTypeArgumentsFix implements IntentionAction, HighPriorityAction { + private static final Logger LOG = Logger.getInstance("#" + ChangeTypeArgumentsFix.class.getName()); + 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, @@ -116,11 +116,12 @@ public class ChangeTypeArgumentsFix implements IntentionAction, HighPriorityActi LOG.assertTrue(reference != null, myNewExpression); final PsiReferenceParameterList parameterList = reference.getParameterList(); LOG.assertTrue(parameterList != null, myNewExpression); + PsiElementFactory factory = JavaPsiFacade.getElementFactory(project); 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)); + PsiType typeArg = assertNotNull(psiSubstitutor.substitute(typeParameters[i])); + PsiElement replaced = elements[i].replace(factory.createTypeElement(typeArg)); + JavaCodeStyleManager.getInstance(file.getProject()).shortenClassReferences(replaced); } } diff --git a/java/java-analysis-impl/src/com/intellij/codeInsight/intention/AddAnnotationPsiFix.java b/java/java-analysis-impl/src/com/intellij/codeInsight/intention/AddAnnotationPsiFix.java index 987f02dc9c39..84f704f64dcd 100644 --- a/java/java-analysis-impl/src/com/intellij/codeInsight/intention/AddAnnotationPsiFix.java +++ b/java/java-analysis-impl/src/com/intellij/codeInsight/intention/AddAnnotationPsiFix.java @@ -64,15 +64,25 @@ public class AddAnnotationPsiFix extends LocalQuickFixOnPsiElement { } @Nullable - public static PsiModifierListOwner getContainer(final PsiElement element) { - PsiModifierListOwner listOwner = PsiTreeUtil.getParentOfType(element, PsiParameter.class, false); - if (listOwner == null) { - final PsiIdentifier psiIdentifier = PsiTreeUtil.getParentOfType(element, PsiIdentifier.class, false); - if (psiIdentifier != null && psiIdentifier.getParent() instanceof PsiModifierListOwner) { - listOwner = (PsiModifierListOwner)psiIdentifier.getParent(); + public static PsiModifierListOwner getContainer(final PsiFile file, int offset) { + PsiReference reference = file.findReferenceAt(offset); + if (reference != null) { + PsiElement target = reference.resolve(); + if (target instanceof PsiMember) { + return (PsiMember)target; } } - return listOwner; + + PsiElement element = file.findElementAt(offset); + + PsiModifierListOwner listOwner = PsiTreeUtil.getParentOfType(element, PsiParameter.class, false); + if (listOwner != null) return listOwner; + + final PsiIdentifier psiIdentifier = PsiTreeUtil.getParentOfType(element, PsiIdentifier.class, false); + if (psiIdentifier != null && psiIdentifier.getParent() instanceof PsiModifierListOwner) { + return (PsiModifierListOwner)psiIdentifier.getParent(); + } + return null; } @Override diff --git a/java/java-analysis-impl/src/com/intellij/codeInsight/intention/impl/AddNullableNotNullAnnotationFix.java b/java/java-analysis-impl/src/com/intellij/codeInsight/intention/impl/AddNullableNotNullAnnotationFix.java index 6684613f0ab0..272ce37708ad 100644 --- a/java/java-analysis-impl/src/com/intellij/codeInsight/intention/impl/AddNullableNotNullAnnotationFix.java +++ b/java/java-analysis-impl/src/com/intellij/codeInsight/intention/impl/AddNullableNotNullAnnotationFix.java @@ -41,7 +41,7 @@ public class AddNullableNotNullAnnotationFix extends AddAnnotationPsiFix { if (!super.isAvailable(project, file, startElement, endElement)) { return false; } - PsiModifierListOwner owner = getContainer(startElement); + PsiModifierListOwner owner = getContainer(file, startElement.getTextRange().getStartOffset()); if (owner == null || AnnotationUtil.isAnnotated(owner, getAnnotationsToRemove()[0], false, false)) { return false; } diff --git a/java/java-analysis-impl/src/com/intellij/codeInspection/AnonymousCanBeLambdaInspection.java b/java/java-analysis-impl/src/com/intellij/codeInspection/AnonymousCanBeLambdaInspection.java index ed3494096265..b89473e29b83 100644 --- a/java/java-analysis-impl/src/com/intellij/codeInspection/AnonymousCanBeLambdaInspection.java +++ b/java/java-analysis-impl/src/com/intellij/codeInspection/AnonymousCanBeLambdaInspection.java @@ -22,6 +22,7 @@ import com.intellij.codeInsight.intention.HighPriorityAction; import com.intellij.openapi.diagnostic.Logger; import com.intellij.openapi.project.Project; import com.intellij.openapi.util.Comparing; +import com.intellij.openapi.util.TextRange; import com.intellij.openapi.util.text.StringUtil; import com.intellij.pom.java.LanguageLevel; import com.intellij.psi.*; @@ -97,8 +98,11 @@ public class AnonymousCanBeLambdaInspection extends BaseJavaBatchLocalInspection final String localName = local.getName(); if (localName != null && helper.resolveReferencedVariable(localName, aClass) != null) return; } - holder.registerProblem(aClass.getBaseClassReference(), "Anonymous #ref #loc can be replaced with lambda", - ProblemHighlightType.GENERIC_ERROR_OR_WARNING, new ReplaceWithLambdaFix()); + final PsiElement lBrace = aClass.getLBrace(); + LOG.assertTrue(lBrace != null); + final TextRange rangeInElement = new TextRange(0, aClass.getStartOffsetInParent() + lBrace.getStartOffsetInParent()); + holder.registerProblem(aClass.getParent(), "Anonymous #ref #loc can be replaced with lambda", + ProblemHighlightType.LIKE_UNUSED_SYMBOL, rangeInElement, new ReplaceWithLambdaFix()); } } } @@ -125,8 +129,8 @@ public class AnonymousCanBeLambdaInspection extends BaseJavaBatchLocalInspection @Override public void applyFix(@NotNull Project project, @NotNull ProblemDescriptor descriptor) { final PsiElement element = descriptor.getPsiElement(); - if (element != null) { - final PsiAnonymousClass anonymousClass = PsiTreeUtil.getParentOfType(element, PsiAnonymousClass.class); + if (element instanceof PsiNewExpression) { + final PsiAnonymousClass anonymousClass = ((PsiNewExpression)element).getAnonymousClass(); LOG.assertTrue(anonymousClass != null); ChangeContextUtil.encodeContextInfo(anonymousClass, true); final PsiElement lambdaContext = anonymousClass.getParent().getParent(); diff --git a/java/java-analysis-impl/src/com/intellij/codeInspection/AnonymousCanBeMethodReferenceInspection.java b/java/java-analysis-impl/src/com/intellij/codeInspection/AnonymousCanBeMethodReferenceInspection.java index 6ad6d223b5a0..6eec61428cda 100644 --- a/java/java-analysis-impl/src/com/intellij/codeInspection/AnonymousCanBeMethodReferenceInspection.java +++ b/java/java-analysis-impl/src/com/intellij/codeInspection/AnonymousCanBeMethodReferenceInspection.java @@ -18,10 +18,10 @@ package com.intellij.codeInspection; import com.intellij.codeInsight.daemon.GroupNames; import com.intellij.openapi.diagnostic.Logger; import com.intellij.openapi.project.Project; +import com.intellij.openapi.util.TextRange; import com.intellij.pom.java.LanguageLevel; import com.intellij.psi.*; import com.intellij.psi.codeStyle.JavaCodeStyleManager; -import com.intellij.psi.util.PsiTreeUtil; import com.intellij.psi.util.PsiUtil; import com.intellij.psi.util.RedundantCastUtil; import org.jetbrains.annotations.Nls; @@ -79,8 +79,11 @@ public class AnonymousCanBeMethodReferenceInspection extends BaseJavaBatchLocalI if (parent instanceof PsiNewExpression) { final PsiJavaCodeReferenceElement classReference = ((PsiNewExpression)parent).getClassOrAnonymousClassReference(); if (classReference != null) { - holder.registerProblem(classReference, - "Anonymous #ref #loc can be replaced with method reference", new ReplaceWithMethodRefFix()); + final PsiElement lBrace = aClass.getLBrace(); + LOG.assertTrue(lBrace != null); + final TextRange rangeInElement = new TextRange(0, aClass.getStartOffsetInParent() + lBrace.getStartOffsetInParent()); + holder.registerProblem(parent, + "Anonymous #ref #loc can be replaced with method reference", ProblemHighlightType.LIKE_UNUSED_SYMBOL, rangeInElement, new ReplaceWithMethodRefFix()); } } } @@ -107,29 +110,31 @@ public class AnonymousCanBeMethodReferenceInspection extends BaseJavaBatchLocalI @Override public void applyFix(@NotNull Project project, @NotNull ProblemDescriptor descriptor) { final PsiElement element = descriptor.getPsiElement(); - final PsiAnonymousClass anonymousClass = PsiTreeUtil.getParentOfType(element, PsiAnonymousClass.class); - if (anonymousClass == null) return; - final PsiMethod[] methods = anonymousClass.getMethods(); - if (methods.length != 1) return; + if (element instanceof PsiNewExpression) { + final PsiAnonymousClass anonymousClass = ((PsiNewExpression)element).getAnonymousClass(); + if (anonymousClass == null) return; + final PsiMethod[] methods = anonymousClass.getMethods(); + if (methods.length != 1) return; - final PsiParameter[] parameters = methods[0].getParameterList().getParameters(); - final PsiCallExpression callExpression = LambdaCanBeMethodReferenceInspection - .canBeMethodReferenceProblem(methods[0].getBody(), parameters, anonymousClass.getBaseClassType()); - if (callExpression == null) return; - final String methodRefText = - LambdaCanBeMethodReferenceInspection.createMethodReferenceText(callExpression, anonymousClass.getBaseClassType(), parameters); + final PsiParameter[] parameters = methods[0].getParameterList().getParameters(); + final PsiCallExpression callExpression = LambdaCanBeMethodReferenceInspection + .canBeMethodReferenceProblem(methods[0].getBody(), parameters, anonymousClass.getBaseClassType()); + if (callExpression == null) return; + final String methodRefText = + LambdaCanBeMethodReferenceInspection.createMethodReferenceText(callExpression, anonymousClass.getBaseClassType(), parameters); - if (methodRefText != null) { - final String canonicalText = anonymousClass.getBaseClassType().getCanonicalText(); - final PsiExpression psiExpression = JavaPsiFacade.getElementFactory(project).createExpressionFromText("(" + canonicalText + ")" + methodRefText, anonymousClass); - - PsiElement castExpr = anonymousClass.getParent().replace(psiExpression); - if (RedundantCastUtil.isCastRedundant((PsiTypeCastExpression)castExpr)) { - final PsiExpression operand = ((PsiTypeCastExpression)castExpr).getOperand(); - LOG.assertTrue(operand != null); - castExpr = castExpr.replace(operand); + if (methodRefText != null) { + final String canonicalText = anonymousClass.getBaseClassType().getCanonicalText(); + final PsiExpression psiExpression = JavaPsiFacade.getElementFactory(project).createExpressionFromText("(" + canonicalText + ")" + methodRefText, anonymousClass); + + PsiElement castExpr = anonymousClass.getParent().replace(psiExpression); + if (RedundantCastUtil.isCastRedundant((PsiTypeCastExpression)castExpr)) { + final PsiExpression operand = ((PsiTypeCastExpression)castExpr).getOperand(); + LOG.assertTrue(operand != null); + castExpr = castExpr.replace(operand); + } + JavaCodeStyleManager.getInstance(project).shortenClassReferences(castExpr); } - JavaCodeStyleManager.getInstance(project).shortenClassReferences(castExpr); } } } diff --git a/java/java-analysis-impl/src/com/intellij/codeInspection/LambdaCanBeMethodReferenceInspection.java b/java/java-analysis-impl/src/com/intellij/codeInspection/LambdaCanBeMethodReferenceInspection.java index 8a8a6db0752e..9bc2a6ecacc2 100644 --- a/java/java-analysis-impl/src/com/intellij/codeInspection/LambdaCanBeMethodReferenceInspection.java +++ b/java/java-analysis-impl/src/com/intellij/codeInspection/LambdaCanBeMethodReferenceInspection.java @@ -88,27 +88,7 @@ public class LambdaCanBeMethodReferenceInspection extends BaseJavaBatchLocalInsp protected static PsiCallExpression canBeMethodReferenceProblem(@Nullable final PsiElement body, final PsiParameter[] parameters, PsiType functionalInterfaceType) { - PsiCallExpression methodCall = null; - if (body instanceof PsiCallExpression) { - methodCall = (PsiCallExpression)body; - } - else if (body instanceof PsiCodeBlock) { - final PsiStatement[] statements = ((PsiCodeBlock)body).getStatements(); - if (statements.length == 1) { - if (statements[0] instanceof PsiReturnStatement) { - final PsiExpression returnValue = ((PsiReturnStatement)statements[0]).getReturnValue(); - if (returnValue instanceof PsiCallExpression) { - methodCall = (PsiCallExpression)returnValue; - } - } - else if (statements[0] instanceof PsiExpressionStatement) { - final PsiExpression expr = ((PsiExpressionStatement)statements[0]).getExpression(); - if (expr instanceof PsiCallExpression) { - methodCall = (PsiCallExpression)expr; - } - } - } - } + PsiCallExpression methodCall = extractMethodCallFromBlock(body); if (methodCall != null) { final PsiExpressionList argumentList = methodCall.getArgumentList(); @@ -218,6 +198,40 @@ public class LambdaCanBeMethodReferenceInspection extends BaseJavaBatchLocalInsp return null; } + public static PsiCallExpression extractMethodCallFromBlock(PsiElement body) { + PsiCallExpression methodCall = null; + if (body instanceof PsiCallExpression) { + methodCall = (PsiCallExpression)body; + } + else if (body instanceof PsiCodeBlock) { + final PsiStatement[] statements = ((PsiCodeBlock)body).getStatements(); + if (statements.length == 1) { + if (statements[0] instanceof PsiReturnStatement) { + final PsiExpression returnValue = ((PsiReturnStatement)statements[0]).getReturnValue(); + if (returnValue instanceof PsiCallExpression) { + methodCall = (PsiCallExpression)returnValue; + } + } + else if (statements[0] instanceof PsiExpressionStatement) { + final PsiExpression expr = ((PsiExpressionStatement)statements[0]).getExpression(); + if (expr instanceof PsiCallExpression) { + methodCall = (PsiCallExpression)expr; + } + } + } + } + else if (body instanceof PsiBlockStatement) { + return extractMethodCallFromBlock(((PsiBlockStatement)body).getCodeBlock()); + } + else if (body instanceof PsiExpressionStatement) { + final PsiExpression expression = ((PsiExpressionStatement)body).getExpression(); + if (expression instanceof PsiCallExpression) { + methodCall = (PsiCallExpression)expression; + } + } + return methodCall; + } + @Nullable private static PsiMethod ensureNonAmbiguousMethod(PsiParameter[] parameters, @NotNull PsiMethod psiMethod) { String methodName = psiMethod.getName(); diff --git a/java/java-analysis-impl/src/com/intellij/codeInspection/StreamApiMigrationInspection.java b/java/java-analysis-impl/src/com/intellij/codeInspection/StreamApiMigrationInspection.java index 8101d16f5a1e..919fb4ea28db 100644 --- a/java/java-analysis-impl/src/com/intellij/codeInspection/StreamApiMigrationInspection.java +++ b/java/java-analysis-impl/src/com/intellij/codeInspection/StreamApiMigrationInspection.java @@ -21,7 +21,9 @@ import com.intellij.openapi.diagnostic.Logger; import com.intellij.openapi.project.Project; import com.intellij.pom.java.LanguageLevel; import com.intellij.psi.*; +import com.intellij.psi.codeStyle.JavaCodeStyleManager; import com.intellij.psi.controlFlow.*; +import com.intellij.psi.search.GlobalSearchScope; import com.intellij.psi.util.InheritanceUtil; import com.intellij.psi.util.PsiTreeUtil; import com.intellij.psi.util.PsiUtil; @@ -106,9 +108,14 @@ public class StreamApiMigrationInspection extends BaseJavaBatchLocalInspectionTo } }); - if (effectivelyFinal[0] && !isTrivial(body, statement.getIterationParameter(), iteratedValueType)) { - holder.registerProblem(iteratedValue, "Can be replaced with foreach call", - ProblemHighlightType.GENERIC_ERROR_OR_WARNING, new ReplaceWithForeachCallFix()); + if (effectivelyFinal[0]) { + if (isCollectCall(body)) { + holder.registerProblem(iteratedValue, "Can be replaced with collect call", + ProblemHighlightType.GENERIC_ERROR_OR_WARNING, new ReplaceWithCollectCallFix()); + } else if (!isTrivial(body, statement.getIterationParameter(), iteratedValueType)) { + holder.registerProblem(iteratedValue, "Can be replaced with foreach call", + ProblemHighlightType.GENERIC_ERROR_OR_WARNING, new ReplaceWithForeachCallFix()); + } } } } @@ -121,10 +128,46 @@ public class StreamApiMigrationInspection extends BaseJavaBatchLocalInspectionTo }; } + private static boolean isCollectCall(PsiStatement body) { + final PsiMethodCallExpression methodCallExpression = extractAddCall(body); + if (methodCallExpression != null) { + final PsiReferenceExpression methodExpression = methodCallExpression.getMethodExpression(); + final PsiExpression qualifierExpression = methodExpression.getQualifierExpression(); + PsiClass qualifierClass = null; + if (qualifierExpression instanceof PsiReferenceExpression) { + qualifierClass = PsiUtil.resolveClassInType(qualifierExpression.getType()); + } else if (qualifierExpression == null) { + final PsiClass enclosingClass = PsiTreeUtil.getParentOfType(body, PsiClass.class); + if (PsiUtil.getEnclosingStaticElement(body, enclosingClass) == null) { + qualifierClass = enclosingClass; + } + } + + if (qualifierClass != null && + InheritanceUtil.isInheritor(qualifierClass, false, CommonClassNames.JAVA_UTIL_COLLECTION)) { + + final PsiElement resolve = methodExpression.resolve(); + if (resolve instanceof PsiMethod && + "add".equals(((PsiMethod)resolve).getName()) && + ((PsiMethod)resolve).getParameterList().getParametersCount() == 1) { + final PsiExpression[] args = methodCallExpression.getArgumentList().getExpressions(); + if (args.length == 1) { + if (args[0] instanceof PsiCallExpression) { + final PsiMethod method = ((PsiCallExpression)args[0]).resolveMethod(); + return method != null && !method.hasTypeParameters(); + } + return true; + } + } + } + } + return false; + } + private static boolean isTrivial(PsiStatement body, PsiParameter parameter, PsiType iteratedValueType) { final PsiIfStatement ifStatement = extractIfStatement(body); //stream - if (ifStatement != null && ifStatement.getElseBranch() == null && ifStatement.getThenBranch() != null && + if (ifStatement != null && InheritanceUtil.isInheritor(iteratedValueType, CommonClassNames.JAVA_UTIL_COLLECTION)) { return false; } @@ -157,11 +200,12 @@ public class StreamApiMigrationInspection extends BaseJavaBatchLocalInspectionTo String foreEachText = body.getText(); String iterated = iteratedValue.getText(); - if (ifStmt != null && ifStmt.getElseBranch() == null) { + if (ifStmt != null) { final PsiExpression condition = ifStmt.getCondition(); if (condition != null) { final PsiStatement thenBranch = ifStmt.getThenBranch(); - if (thenBranch != null && InheritanceUtil.isInheritor(iteratedValue.getType(), CommonClassNames.JAVA_UTIL_COLLECTION)) { + LOG.assertTrue(thenBranch != null); + if (InheritanceUtil.isInheritor(iteratedValue.getType(), CommonClassNames.JAVA_UTIL_COLLECTION)) { body = thenBranch; foreEachText = thenBranch.getText(); iterated += ".stream().filter(" + parameter.getName() + " -> " + condition.getText() +")"; @@ -170,8 +214,16 @@ public class StreamApiMigrationInspection extends BaseJavaBatchLocalInspectionTo } final PsiParameter[] parameters = {parameter}; - final PsiCallExpression expression = LambdaCanBeMethodReferenceInspection.canBeMethodReferenceProblem(body instanceof PsiBlockStatement ? ((PsiBlockStatement)body).getCodeBlock() : body, parameters, null); - final String methodReferenceText = LambdaCanBeMethodReferenceInspection.createMethodReferenceText(expression, null, parameters); + String methodReferenceText = null; + final PsiCallExpression callExpression = LambdaCanBeMethodReferenceInspection.extractMethodCallFromBlock(body); + if (callExpression != null) { + final JavaPsiFacade psiFacade = JavaPsiFacade.getInstance(project); + final PsiClass consumerClass = psiFacade.findClass("java.util.function.Consumer", GlobalSearchScope.allScope(project)); + final PsiClassType functionalType = consumerClass != null ? psiFacade.getElementFactory().createType(consumerClass, callExpression.getType()) : null; + + final PsiCallExpression toConvertCall = LambdaCanBeMethodReferenceInspection.canBeMethodReferenceProblem(body instanceof PsiBlockStatement ? ((PsiBlockStatement)body).getCodeBlock() : body, parameters, functionalType); + methodReferenceText = LambdaCanBeMethodReferenceInspection.createMethodReferenceText(toConvertCall, functionalType, parameters); + } final String lambdaText = parameter.getName() + " -> " + foreEachText; final String codeBlock8 = methodReferenceText != null ? methodReferenceText : lambdaText; PsiExpressionStatement callStatement = (PsiExpressionStatement)JavaPsiFacade.getElementFactory(project).createStatementFromText(iterated + ".forEach(" + codeBlock8 + ");", foreachStatement); @@ -191,6 +243,98 @@ public class StreamApiMigrationInspection extends BaseJavaBatchLocalInspectionTo } } + private static class ReplaceWithCollectCallFix implements LocalQuickFix { + @NotNull + @Override + public String getName() { + return getFamilyName(); + } + + @NotNull + @Override + public String getFamilyName() { + return "Replace with collect"; + } + + @Override + public void applyFix(@NotNull Project project, @NotNull ProblemDescriptor descriptor) { + final PsiForeachStatement foreachStatement = PsiTreeUtil.getParentOfType(descriptor.getPsiElement(), PsiForeachStatement.class); + if (foreachStatement != null) { + PsiStatement body = foreachStatement.getBody(); + final PsiExpression iteratedValue = foreachStatement.getIteratedValue(); + if (body != null && iteratedValue != null) { + final PsiParameter parameter = foreachStatement.getIterationParameter(); + + final PsiIfStatement ifStatement = extractIfStatement(body); + final PsiMethodCallExpression methodCallExpression = extractAddCall(body); + String iteration = iteratedValue.getText() + ".stream()"; + if (ifStatement != null) { + final PsiExpression condition = ifStatement.getCondition(); + if (condition != null) { + iteration += ".filter(" + parameter.getName() + " -> " + condition.getText() +")"; + } + } + iteration +=".map("; + + final PsiExpression mapperCall = methodCallExpression.getArgumentList().getExpressions()[0]; + + final JavaPsiFacade psiFacade = JavaPsiFacade.getInstance(project); + final PsiClass functionClass = psiFacade.findClass("java.util.function.Function", GlobalSearchScope.allScope(project)); + final PsiClassType functionalInterfaceType = functionClass != null ? psiFacade.getElementFactory().createType(functionClass, parameter.getType(), mapperCall.getType()) : null; + final PsiCallExpression toConvertCall = LambdaCanBeMethodReferenceInspection.canBeMethodReferenceProblem(mapperCall, + new PsiParameter[]{ + parameter}, + functionalInterfaceType); + final String methodReferenceText = LambdaCanBeMethodReferenceInspection.createMethodReferenceText(toConvertCall, functionalInterfaceType, new PsiParameter[]{parameter}); + if (methodReferenceText != null) { + iteration += methodReferenceText; + } else { + iteration += parameter.getName() + " -> " + mapperCall.getText(); + } + iteration += ").collect(java.util.stream.Collectors."; + + String variableName = null; + PsiExpression initializer = null; + final PsiExpression qualifierExpression = methodCallExpression.getMethodExpression().getQualifierExpression(); + if (qualifierExpression instanceof PsiReferenceExpression) { + final PsiElement resolve = ((PsiReferenceExpression)qualifierExpression).resolve(); + if (resolve instanceof PsiVariable) { + if (resolve instanceof PsiLocalVariable && foreachStatement.equals(PsiTreeUtil.skipSiblingsForward(resolve.getParent(), PsiWhiteSpace.class))) { + initializer = ((PsiVariable)resolve).getInitializer(); + } + variableName = ((PsiVariable)resolve).getName() + "."; + } + } else if (qualifierExpression == null) { + variableName = ""; + } + + PsiElement result = null; + if (initializer != null) { + final PsiType initializerType = initializer.getType(); + final PsiClassType rawType = initializerType instanceof PsiClassType ? ((PsiClassType)initializerType).rawType() : null; + if (rawType != null && rawType.equalsToText(CommonClassNames.JAVA_UTIL_ARRAY_LIST)) { + iteration += "toList()"; + } else if (rawType != null && rawType.equalsToText(CommonClassNames.JAVA_UTIL_HASH_SET)) { + iteration += "toSet()"; + } else { + iteration += "toCollection(() -> " + initializer.getText() +")"; + } + iteration += ")"; + result = initializer.replace(JavaPsiFacade.getElementFactory(project).createExpressionFromText(iteration, foreachStatement)); + foreachStatement.delete(); + } else if (variableName != null){ + iteration += "toList())"; + result = foreachStatement.replace(JavaPsiFacade.getElementFactory(project).createStatementFromText(variableName + "addAll(" + iteration +");", foreachStatement)); + } + + if (result != null) { + result = JavaCodeStyleManager.getInstance(project).shortenClassReferences(result); + } + } + } + } + } + public static PsiIfStatement extractIfStatement(PsiStatement body) { PsiIfStatement ifStmt = null; if (body instanceof PsiIfStatement) { @@ -201,6 +345,34 @@ public class StreamApiMigrationInspection extends BaseJavaBatchLocalInspectionTo ifStmt = (PsiIfStatement)statements[0]; } } - return ifStmt; + if (ifStmt != null && ifStmt.getElseBranch() == null && ifStmt.getThenBranch() != null) { + return ifStmt; + } + return null; + } + + private static PsiMethodCallExpression extractAddCall(PsiStatement body) { + final PsiIfStatement ifStatement = extractIfStatement(body); + if (ifStatement != null) { + return extractAddCall(ifStatement.getThenBranch()); + } + PsiExpressionStatement stmt = null; + if (body instanceof PsiBlockStatement) { + final PsiStatement[] statements = ((PsiBlockStatement)body).getCodeBlock().getStatements(); + if (statements.length == 1 && statements[0] instanceof PsiExpressionStatement) { + stmt = (PsiExpressionStatement)statements[0]; + } + } + else if (body instanceof PsiExpressionStatement) { + stmt = (PsiExpressionStatement)body; + } + + if (stmt != null) { + final PsiExpression expression = stmt.getExpression(); + if (expression instanceof PsiMethodCallExpression) { + return (PsiMethodCallExpression)expression; + } + } + return null; } } diff --git a/java/java-analysis-impl/src/com/intellij/codeInspection/dataFlow/DataFlowInspectionBase.java b/java/java-analysis-impl/src/com/intellij/codeInspection/dataFlow/DataFlowInspectionBase.java index cc1b2ae059cd..2e15ac48f7f3 100644 --- a/java/java-analysis-impl/src/com/intellij/codeInspection/dataFlow/DataFlowInspectionBase.java +++ b/java/java-analysis-impl/src/com/intellij/codeInspection/dataFlow/DataFlowInspectionBase.java @@ -506,12 +506,16 @@ public class DataFlowInspectionBase extends BaseJavaBatchLocalInspectionTool { final String text = isNullLiteralExpression(expr) ? InspectionsBundle.message("dataflow.message.return.null.from.notnullable", presentableNullable) : InspectionsBundle.message("dataflow.message.return.nullable.from.notnullable", presentableNullable); - holder.registerProblem(expr, text, new AnnotateMethodFix(defaultNullable, ArrayUtil.toStringArray(manager.getNotNulls())){ - @Override - public int shouldAnnotateBaseMethod(PsiMethod method, PsiMethod superMethod, Project project) { - return 1; - } - }); + final LocalQuickFix[] fixes = + PsiTreeUtil.skipParentsOfType(expr, PsiCodeBlock.class, PsiReturnStatement.class) instanceof PsiLambdaExpression + ? LocalQuickFix.EMPTY_ARRAY + : new LocalQuickFix[]{ new AnnotateMethodFix(defaultNullable, ArrayUtil.toStringArray(manager.getNotNulls())) { + @Override + public int shouldAnnotateBaseMethod(PsiMethod method, PsiMethod superMethod, Project project) { + return 1; + } + }}; + holder.registerProblem(expr, text, fixes); } } } |