anonym -> lambda, redundant cast, redundant type args: process top level overloads (IDEA-152766);

rearrange utils
This commit is contained in:
Anna.Kozlova
2016-04-15 18:14:02 +02:00
parent 09f46115cf
commit 43fc40c4a5
14 changed files with 195 additions and 145 deletions

View File

@@ -33,9 +33,7 @@ import com.intellij.psi.codeStyle.JavaCodeStyleManager;
import com.intellij.psi.controlFlow.AnalysisCanceledException;
import com.intellij.psi.controlFlow.ControlFlow;
import com.intellij.psi.controlFlow.ControlFlowUtil;
import com.intellij.psi.impl.source.resolve.graphInference.FunctionalInterfaceParameterizationUtil;
import com.intellij.psi.util.*;
import com.intellij.util.ArrayUtilRt;
import com.intellij.util.containers.ContainerUtil;
import com.intellij.util.containers.hash.LinkedHashMap;
import com.intellij.util.text.UniqueNameGenerator;
@@ -137,7 +135,11 @@ public class AnonymousCanBeLambdaInspection extends BaseJavaBatchLocalInspection
}
public static boolean hasForbiddenRefsInsideBody(PsiMethod method, PsiAnonymousClass aClass) {
final ForbiddenRefsChecker checker = new ForbiddenRefsChecker(method, aClass);
final PsiType inferredType = getInferredType(aClass, method);
if (inferredType == null) {
return true;
}
final ForbiddenRefsChecker checker = new ForbiddenRefsChecker(method, aClass, inferredType != PsiType.NULL ? inferredType : null);
final PsiCodeBlock body = method.getBody();
LOG.assertTrue(body != null);
body.accept(checker);
@@ -156,27 +158,29 @@ public class AnonymousCanBeLambdaInspection extends BaseJavaBatchLocalInspection
topExpr = (PsiExpression)topExpr.getParent();
}
final PsiElement parent = topExpr.getParent();
if (parent instanceof PsiExpressionList) {
PsiExpressionList expressionList = (PsiExpressionList)parent;
final PsiElement callExpr = expressionList.getParent();
if (callExpr instanceof PsiCallExpression) {
PsiExpression[] expressions = expressionList.getExpressions();
int i = ArrayUtilRt.find(expressions, topExpr);
if (i < 0) return null;
final PsiCallExpression copy = (PsiCallExpression)callExpr.copy();
final PsiExpressionList argumentList = copy.getArgumentList();
if (argumentList != null) {
final PsiExpression classArg = argumentList.getExpressions()[i];
PsiExpression lambda = JavaPsiFacade.getElementFactory(aClass.getProject())
.createExpressionFromText(ReplaceWithLambdaFix.composeLambdaText(method), expression);
lambda = (PsiExpression)classArg.replace(lambda);
((PsiLambdaExpression)lambda).getBody().replace(method.getBody());
return LambdaUtil.getFunctionalInterfaceType(lambda, true);
final PsiCall call = LambdaUtil.treeWalkUp(topExpr);
if (call != null) {
final int offsetInTopCall = aClass.getTextRange().getStartOffset() - call.getTextRange().getStartOffset();
final PsiCall copyCall = (PsiCall)call.copy();
final PsiAnonymousClass classArg = PsiTreeUtil.getParentOfType(copyCall.findElementAt(offsetInTopCall), PsiAnonymousClass.class);
if (classArg != null) {
PsiExpression lambda = JavaPsiFacade.getElementFactory(aClass.getProject())
.createExpressionFromText(ReplaceWithLambdaFix.composeLambdaText(method), expression);
lambda = (PsiExpression)classArg.getParent().replace(lambda);
((PsiLambdaExpression)lambda).getBody().replace(method.getBody());
final PsiType interfaceType;
if (copyCall.resolveMethod() == null) {
interfaceType = null;
}
else {
interfaceType = ((PsiLambdaExpression)lambda).getFunctionalInterfaceType();
}
return interfaceType;
}
}
return null;
return PsiType.NULL;
}
public static boolean canBeConvertedToLambda(PsiAnonymousClass aClass,
@@ -247,9 +251,6 @@ public class AnonymousCanBeLambdaInspection extends BaseJavaBatchLocalInspection
final PsiCodeBlock body = method.getBody();
if (body == null) return null;
final ForbiddenRefsChecker checker = new ForbiddenRefsChecker(method, anonymousClass);
body.accept(checker);
final Project project = element.getProject();
final PsiElementFactory elementFactory = JavaPsiFacade.getElementFactory(project);
@@ -457,10 +458,10 @@ public class AnonymousCanBeLambdaInspection extends BaseJavaBatchLocalInspection
private final PsiType myInferredType;
public ForbiddenRefsChecker(PsiMethod method,
PsiAnonymousClass aClass) {
PsiAnonymousClass aClass,
PsiType inferredType) {
myMethod = method;
myAnonymClass = aClass;
final PsiType inferredType = FunctionalInterfaceParameterizationUtil.getGroundTargetType(getInferredType(aClass, method));
final PsiClassType baseClassType = aClass.getBaseClassType();
myInferredType = !baseClassType.equals(inferredType) ? inferredType : null;
}

View File

@@ -23,7 +23,6 @@ import com.intellij.openapi.util.NullableComputable;
import com.intellij.psi.*;
import com.intellij.psi.codeStyle.JavaCodeStyleManager;
import com.intellij.psi.codeStyle.VariableKind;
import com.intellij.psi.impl.PsiDiamondTypeUtil;
import com.intellij.psi.impl.source.resolve.CompletionParameterTypeInferencePolicy;
import com.intellij.psi.impl.source.resolve.DefaultParameterTypeInferencePolicy;
import com.intellij.psi.impl.source.resolve.ParameterTypeInferencePolicy;
@@ -597,7 +596,7 @@ public class ExpectedTypesProvider {
private void getExpectedArgumentsTypesForNewExpression(@NotNull final PsiNewExpression newExpr,
@NotNull final PsiExpressionList list) {
if (PsiDiamondTypeUtil.hasDiamond(newExpr)) {
if (PsiDiamondType.hasDiamond(newExpr)) {
final JavaResolveResult[] candidates = PsiDiamondTypeImpl.collectStaticFactories(newExpr, DuplicateConflictResolver.INSTANCE);
if (candidates != null) {
final PsiExpressionList argumentList = newExpr.getArgumentList();

View File

@@ -22,7 +22,6 @@ import com.intellij.openapi.project.Project;
import com.intellij.openapi.util.Ref;
import com.intellij.psi.*;
import com.intellij.psi.codeStyle.JavaCodeStyleManager;
import com.intellij.psi.impl.PsiDiamondTypeUtil;
import com.intellij.psi.search.GlobalSearchScope;
import com.intellij.psi.search.searches.ReferencesSearch;
import com.intellij.psi.util.PsiUtil;
@@ -100,7 +99,7 @@ public class ChangeClassSignatureProcessor extends BaseRefactoringProcessor {
parent.getParent() instanceof PsiClassObjectAccessExpression)) {
continue;
}
if (parent instanceof PsiNewExpression && PsiDiamondTypeUtil.hasDiamond((PsiNewExpression)parent)) {
if (parent instanceof PsiNewExpression && PsiDiamondType.hasDiamond((PsiNewExpression)parent)) {
continue;
}
if (parent instanceof PsiTypeElement || parent instanceof PsiNewExpression || parent instanceof PsiAnonymousClass ||

View File

@@ -23,7 +23,6 @@ import com.intellij.openapi.util.Comparing;
import com.intellij.openapi.util.text.StringUtil;
import com.intellij.psi.*;
import com.intellij.psi.codeStyle.JavaCodeStyleManager;
import com.intellij.psi.impl.PsiDiamondTypeUtil;
import com.intellij.psi.search.LocalSearchScope;
import com.intellij.psi.search.searches.ReferencesSearch;
import com.intellij.psi.tree.IElementType;
@@ -392,7 +391,7 @@ public class InlineUtil {
private static PsiElement replaceDiamondWithInferredTypesIfNeeded(PsiExpression initializer, PsiElement ref) {
if (initializer instanceof PsiNewExpression) {
final PsiDiamondType diamondType = PsiDiamondTypeUtil.getDiamondType((PsiNewExpression)initializer);
final PsiDiamondType diamondType = PsiDiamondType.getDiamondType((PsiNewExpression)initializer);
if (diamondType != null) {
final PsiDiamondType.DiamondInferenceResult inferenceResult = diamondType.resolveInferredTypes();
if (inferenceResult.getErrorMessage() == null) {

View File

@@ -21,6 +21,7 @@ import com.intellij.openapi.util.RecursionGuard;
import com.intellij.openapi.util.RecursionManager;
import com.intellij.openapi.util.registry.Registry;
import com.intellij.pom.java.LanguageLevel;
import com.intellij.psi.impl.source.resolve.graphInference.PsiPolyExpressionUtil;
import com.intellij.psi.infos.MethodCandidateInfo;
import com.intellij.psi.util.*;
import com.intellij.util.Consumer;
@@ -697,6 +698,83 @@ public class LambdaUtil {
return !ourParameterGuard.currentStack().isEmpty();
}
@Nullable
public static PsiCall treeWalkUp(PsiElement context) {
PsiCall top = null;
PsiElement parent = PsiTreeUtil.getParentOfType(context,
PsiExpressionList.class,
PsiLambdaExpression.class,
PsiConditionalExpression.class,
PsiCodeBlock.class,
PsiCall.class);
while (true) {
if (parent instanceof PsiCall) {
break;
}
final PsiLambdaExpression lambdaExpression = PsiTreeUtil.getParentOfType(parent, PsiLambdaExpression.class);
if (parent instanceof PsiCodeBlock) {
if (lambdaExpression == null) {
break;
}
else {
boolean inReturnExpressions = false;
for (PsiExpression expression : getReturnExpressions(lambdaExpression)) {
inReturnExpressions |= PsiTreeUtil.isAncestor(expression, context, false);
}
if (!inReturnExpressions) {
break;
}
if (getFunctionalTypeMap().containsKey(lambdaExpression)) {
break;
}
}
}
if (parent instanceof PsiConditionalExpression && !PsiPolyExpressionUtil.isPolyExpression((PsiExpression)parent)) {
break;
}
if (parent instanceof PsiLambdaExpression && getFunctionalTypeMap().containsKey(parent)) {
break;
}
final PsiCall psiCall = PsiTreeUtil.getParentOfType(parent, PsiCall.class);
if (psiCall == null) {
break;
}
final MethodCandidateInfo.CurrentCandidateProperties properties = MethodCandidateInfo.getCurrentMethod(psiCall.getArgumentList());
if (properties != null) {
if (properties.isApplicabilityCheck() ||
lambdaExpression != null && lambdaExpression.hasFormalParameterTypes()) {
break;
}
}
top = psiCall;
if (top instanceof PsiExpression && PsiPolyExpressionUtil.isPolyExpression((PsiExpression)top)) {
parent = PsiTreeUtil.getParentOfType(parent.getParent(), PsiExpressionList.class, PsiLambdaExpression.class, PsiCodeBlock.class);
}
else {
break;
}
}
if (top == null) {
return null;
}
final PsiExpressionList argumentList = top.getArgumentList();
if (argumentList == null) {
return null;
}
LOG.assertTrue(MethodCandidateInfo.getCurrentMethod(argumentList) == null);
return top;
}
public static class TypeParamsChecker extends PsiTypeVisitor<Boolean> {
private PsiMethod myMethod;
private final PsiClass myClass;

View File

@@ -17,6 +17,7 @@ package com.intellij.psi;
import com.intellij.openapi.util.RecursionGuard;
import com.intellij.openapi.util.RecursionManager;
import com.intellij.psi.util.PsiUtil;
import org.jetbrains.annotations.NotNull;
import java.util.ArrayList;
@@ -157,4 +158,25 @@ public abstract class PsiDiamondType extends PsiType {
return result;
}
}
public static boolean hasDiamond(PsiNewExpression expression) {
return getDiamondType(expression) != null;
}
public static PsiDiamondType getDiamondType(PsiNewExpression expression) {
if (PsiUtil.isLanguageLevel7OrHigher(expression)) {
final PsiJavaCodeReferenceElement classReference = expression.getClassOrAnonymousClassReference();
if (classReference != null) {
final PsiReferenceParameterList parameterList = classReference.getParameterList();
if (parameterList != null) {
final PsiTypeElement[] parameterElements = parameterList.getTypeParameterElements();
if (parameterElements.length == 1) {
final PsiType type = parameterElements[0].getType();
return type instanceof PsiDiamondType ? (PsiDiamondType)type : null;
}
}
}
}
return null;
}
}

View File

@@ -16,7 +16,6 @@
package com.intellij.psi.impl.source.resolve.graphInference;
import com.intellij.psi.*;
import com.intellij.psi.impl.PsiDiamondTypeUtil;
import com.intellij.psi.infos.MethodCandidateInfo;
import com.intellij.psi.util.PsiUtil;
import com.intellij.psi.util.TypeConversionUtil;
@@ -47,7 +46,7 @@ public class PsiPolyExpressionUtil {
else if (expression instanceof PsiParenthesizedExpression) {
return isPolyExpression(((PsiParenthesizedExpression)expression).getExpression());
}
else if (expression instanceof PsiNewExpression && PsiDiamondTypeUtil.hasDiamond((PsiNewExpression)expression)) {
else if (expression instanceof PsiNewExpression && PsiDiamondType.hasDiamond((PsiNewExpression)expression)) {
return isInAssignmentOrInvocationContext(expression);
}
else if (expression instanceof PsiMethodCallExpression) {
@@ -73,7 +72,7 @@ public class PsiPolyExpressionUtil {
return mentionsTypeParameters(returnType, typeParameters);
}
}
else if (method.isConstructor() && expression instanceof PsiNewExpression && PsiDiamondTypeUtil.hasDiamond((PsiNewExpression)expression)) {
else if (method.isConstructor() && expression instanceof PsiNewExpression && PsiDiamondType.hasDiamond((PsiNewExpression)expression)) {
return true;
}
} else {

View File

@@ -358,7 +358,14 @@ public class RedundantCastUtil {
newCall = (PsiCall)((PsiNewExpression)arrayDeclaration).getArrayInitializer().getInitializers()[0];
}
else {
newCall = (PsiCall)expression.copy();
final PsiCall call = LambdaUtil.treeWalkUp(expression);
if (call != null) {
final PsiCall callCopy = (PsiCall)call.copy();
newCall = PsiTreeUtil.getParentOfType(callCopy.findElementAt(expression.getTextRange().getStartOffset() - call.getTextRange().getStartOffset()), expression.getClass());
}
else {
newCall = (PsiCall)expression.copy();
}
}
final PsiExpressionList argList = newCall.getArgumentList();
LOG.assertTrue(argList != null);

View File

@@ -153,27 +153,6 @@ public class PsiDiamondTypeUtil {
return typeText;
}
public static boolean hasDiamond(PsiNewExpression expression) {
return getDiamondType(expression) != null;
}
public static PsiDiamondType getDiamondType(PsiNewExpression expression) {
if (PsiUtil.isLanguageLevel7OrHigher(expression)) {
final PsiJavaCodeReferenceElement classReference = expression.getClassOrAnonymousClassReference();
if (classReference != null) {
final PsiReferenceParameterList parameterList = classReference.getParameterList();
if (parameterList != null) {
final PsiTypeElement[] parameterElements = parameterList.getTypeParameterElements();
if (parameterElements.length == 1) {
final PsiType type = parameterElements[0].getType();
return type instanceof PsiDiamondType ? (PsiDiamondType)type : null;
}
}
}
}
return null;
}
public static boolean areTypeArgumentsRedundant(PsiType[] typeArguments,
PsiCallExpression expression,
boolean constructorRef,
@@ -191,13 +170,20 @@ public class PsiDiamondTypeUtil {
}
else {
final int offset = expression.getTextRange().getStartOffset();
final PsiFile containingFile = expression.getContainingFile();
final PsiFile fileCopy = (PsiFile)containingFile.copy();
copy = fileCopy.findElementAt(offset);
if (method != null && method.getContainingFile() == containingFile) {
final PsiElement startMethodElementInCopy = fileCopy.findElementAt(method.getTextOffset());
method = PsiTreeUtil.getParentOfType(startMethodElementInCopy, PsiMethod.class);
LOG.assertTrue(method != null, startMethodElementInCopy);
final PsiCall call = LambdaUtil.treeWalkUp(expression);
if (call instanceof PsiCallExpression) { //exclude EnumConstant
final PsiCall callCopy = (PsiCall)call.copy();
copy = callCopy.findElementAt(offset - call.getTextRange().getStartOffset());
}
else {
final PsiFile containingFile = expression.getContainingFile();
final PsiFile fileCopy = (PsiFile)containingFile.copy();
copy = fileCopy.findElementAt(offset);
if (method != null && method.getContainingFile() == containingFile) {
final PsiElement startMethodElementInCopy = fileCopy.findElementAt(method.getTextOffset());
method = PsiTreeUtil.getParentOfType(startMethodElementInCopy, PsiMethod.class);
LOG.assertTrue(method != null, startMethodElementInCopy);
}
}
}
final PsiCallExpression exprCopy = PsiTreeUtil.getParentOfType(copy, PsiCallExpression.class, false);

View File

@@ -21,7 +21,6 @@ import com.intellij.openapi.util.Key;
import com.intellij.openapi.util.Pair;
import com.intellij.openapi.util.text.StringUtil;
import com.intellij.psi.*;
import com.intellij.psi.impl.PsiDiamondTypeUtil;
import com.intellij.psi.impl.source.resolve.graphInference.constraints.*;
import com.intellij.psi.infos.MethodCandidateInfo;
import com.intellij.psi.search.GlobalSearchScope;
@@ -264,7 +263,7 @@ public class InferenceSession {
}
else if (method.isConstructor() && gParent instanceof PsiNewExpression) {
final PsiClass containingClass = method.getContainingClass();
if (containingClass != null && containingClass.hasTypeParameters() && PsiDiamondTypeUtil.hasDiamond((PsiNewExpression)gParent)) {
if (containingClass != null && containingClass.hasTypeParameters() && PsiDiamondType.hasDiamond((PsiNewExpression)gParent)) {
owner = containingClass;
}
}
@@ -538,7 +537,7 @@ public class InferenceSession {
}
public static JavaResolveResult getResolveResult(final PsiCall callExpression) {
if (callExpression instanceof PsiNewExpression && PsiDiamondTypeUtil.hasDiamond((PsiNewExpression)callExpression)) {
if (callExpression instanceof PsiNewExpression && PsiDiamondType.hasDiamond((PsiNewExpression)callExpression)) {
PsiUtilCore.ensureValid(callExpression);
return CachedValuesManager.getCachedValue(callExpression, new CachedValueProvider<JavaResolveResult>() {
@Nullable

View File

@@ -75,7 +75,7 @@ public class InferenceSessionContainer {
if (parent instanceof PsiExpression && !PsiPolyExpressionUtil.isPolyExpression((PsiExpression)parent)) {
return null;
}
return treeWalkUp(parent);
return LambdaUtil.treeWalkUp(parent);
}
});
if (topLevelCall != null) {
@@ -224,81 +224,4 @@ public class InferenceSessionContainer {
}
return substitutor;
}
@Nullable
public static PsiCall treeWalkUp(PsiElement context) {
PsiCall top = null;
PsiElement parent = PsiTreeUtil.getParentOfType(context,
PsiExpressionList.class,
PsiLambdaExpression.class,
PsiConditionalExpression.class,
PsiCodeBlock.class,
PsiCall.class);
while (true) {
if (parent instanceof PsiCall) {
break;
}
final PsiLambdaExpression lambdaExpression = PsiTreeUtil.getParentOfType(parent, PsiLambdaExpression.class);
if (parent instanceof PsiCodeBlock) {
if (lambdaExpression == null) {
break;
}
else {
boolean inReturnExpressions = false;
for (PsiExpression expression : LambdaUtil.getReturnExpressions(lambdaExpression)) {
inReturnExpressions |= PsiTreeUtil.isAncestor(expression, context, false);
}
if (!inReturnExpressions) {
break;
}
if (LambdaUtil.getFunctionalTypeMap().containsKey(lambdaExpression)) {
break;
}
}
}
if (parent instanceof PsiConditionalExpression && !PsiPolyExpressionUtil.isPolyExpression((PsiExpression)parent)) {
break;
}
if (parent instanceof PsiLambdaExpression && LambdaUtil.getFunctionalTypeMap().containsKey(parent)) {
break;
}
final PsiCall psiCall = PsiTreeUtil.getParentOfType(parent, PsiCall.class);
if (psiCall == null) {
break;
}
final MethodCandidateInfo.CurrentCandidateProperties properties = MethodCandidateInfo.getCurrentMethod(psiCall.getArgumentList());
if (properties != null) {
if (properties.isApplicabilityCheck() ||
lambdaExpression != null && lambdaExpression.hasFormalParameterTypes()) {
break;
}
}
top = psiCall;
if (top instanceof PsiExpression && PsiPolyExpressionUtil.isPolyExpression((PsiExpression)top)) {
parent = PsiTreeUtil.getParentOfType(parent.getParent(), PsiExpressionList.class, PsiLambdaExpression.class, PsiCodeBlock.class);
}
else {
break;
}
}
if (top == null) {
return null;
}
final PsiExpressionList argumentList = top.getArgumentList();
if (argumentList == null) {
return null;
}
LOG.assertTrue(MethodCandidateInfo.getCurrentMethod(argumentList) == null);
return top;
}
}

View File

@@ -0,0 +1,17 @@
import java.util.function.Function;
abstract class LambdaConvert {
public abstract <T> T query(Function<Double, T> rse);
public void with() {
addSingle(query((<warning descr="Casting 'resultSet -> \"\"' to 'Function<Double, String>' is redundant">Function<Double, String></warning>)resultSet -> ""));
add(query((Function<Double, String>)resultSet -> ""));
}
public void addSingle(String s) {}
public void add(String s) {}
public void add(Integer i) {}
}

View File

@@ -0,0 +1,20 @@
// "Replace with lambda" "false"
import java.util.function.Function;
abstract class LambdaConvert {
public abstract <T> T query(Function<Double, T> rse);
public void with() {
add(query(new Function<Do<caret>uble, String>() {
@Override
public String apply(Double resultSet) {
return "";
}
}));
}
public void add(String s) {}
public void add(Integer i) {}
}

View File

@@ -70,6 +70,7 @@ public class LambdaRedundantCastTest extends LightDaemonAnalyzerTestCase {
}
public void testEnumConstantWithFunctionalExpressionArg() throws Exception { doTest(); }
public void testSecondLevelOverload() throws Exception { doTest(); }
private void doTest() {
doTest(BASE_PATH + "/" + getTestName(false) + ".java", true, false);