mirror of
https://gitflic.ru/project/openide/openide.git
synced 2026-09-27 10:03:11 +07:00
java redundant cast: normalize captured wildcard contexts (IDEA-162080)
GitOrigin-RevId: 7265302824d034690900bb1a58a90c4021da8f67
This commit is contained in:
committed by
intellij-monorepo-bot
parent
1bc4cda08e
commit
7107a6a626
@@ -9,8 +9,10 @@ import com.intellij.openapi.diagnostic.Logger;
|
||||
import com.intellij.openapi.progress.ProgressManager;
|
||||
import com.intellij.openapi.project.Project;
|
||||
import com.intellij.openapi.util.Comparing;
|
||||
import com.intellij.openapi.util.Key;
|
||||
import com.intellij.psi.*;
|
||||
import com.intellij.psi.codeStyle.JavaCodeStyleManager;
|
||||
import com.intellij.psi.impl.source.PsiImmediateClassType;
|
||||
import com.intellij.psi.impl.source.resolve.graphInference.PsiPolyExpressionUtil;
|
||||
import com.intellij.psi.infos.MethodCandidateInfo;
|
||||
import com.intellij.psi.tree.IElementType;
|
||||
@@ -21,13 +23,11 @@ import com.siyeh.ig.psiutils.ExpressionUtils;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
import java.util.*;
|
||||
|
||||
public class RedundantCastUtil {
|
||||
private static final Logger LOG = Logger.getInstance(RedundantCastUtil.class);
|
||||
private static final Key<PsiElement> SELF_REFERENCE = Key.create("SELF_REFERENCE");
|
||||
|
||||
private RedundantCastUtil() { }
|
||||
|
||||
@@ -258,7 +258,7 @@ public class RedundantCastUtil {
|
||||
if (opType == null) return;
|
||||
|
||||
if (!(operand instanceof PsiFunctionalExpression || opType instanceof PsiPrimitiveType) &&
|
||||
isCastInReferenceQualifierRedundant(expression, operand)) {
|
||||
isCastInReferenceQualifierRedundant(expression)) {
|
||||
addToResults(typeCast);
|
||||
}
|
||||
}
|
||||
@@ -273,8 +273,7 @@ public class RedundantCastUtil {
|
||||
return castOperand;
|
||||
}
|
||||
|
||||
private static boolean isCastInReferenceQualifierRedundant(final PsiReferenceExpression refExpression,
|
||||
final PsiExpression castOperand) {
|
||||
private static boolean isCastInReferenceQualifierRedundant(final PsiReferenceExpression refExpression) {
|
||||
final JavaResolveResult resolveResult = refExpression.advancedResolve(false);
|
||||
PsiElement oldMember = resolveResult.getElement();
|
||||
if (oldMember == null) return false;
|
||||
@@ -288,22 +287,22 @@ public class RedundantCastUtil {
|
||||
PsiMethodCallExpression newCall =
|
||||
(PsiMethodCallExpression)copyCallExpression(((PsiMethodCallExpression)parent), PsiTypesUtil.getExpectedTypeByParent(parent));
|
||||
if (newCall == null) return false;
|
||||
PsiExpression newQualifier = newCall.getMethodExpression().getQualifierExpression();
|
||||
PsiExpression newQualifier = deparenthesizeExpression(newCall.getMethodExpression().getQualifierExpression());
|
||||
LOG.assertTrue(newQualifier != null);
|
||||
newQualifier.replace(castOperand);
|
||||
PsiElement oldReference = newQualifier.getCopyableUserData(SELF_REFERENCE);
|
||||
PsiElement replace = newQualifier.replace(getInnerMostOperand(newQualifier));
|
||||
replace.putCopyableUserData(SELF_REFERENCE, oldReference);
|
||||
|
||||
final JavaResolveResult newResult = newCall.getMethodExpression().advancedResolve(false);
|
||||
if (!newResult.isValidResult()) return false;
|
||||
|
||||
final PsiMethod newTargetMethod = (PsiMethod)newResult.getElement();
|
||||
LOG.assertTrue(newTargetMethod != null, "isValidResult() check above should find this");
|
||||
|
||||
PsiType newReturnType = newCall.getType();
|
||||
PsiType oldReturnType = ((PsiMethodCallExpression)parent).getType();
|
||||
if (newReturnType instanceof PsiCapturedWildcardType && oldReturnType instanceof PsiCapturedWildcardType) {
|
||||
oldReturnType = ((PsiCapturedWildcardType)oldReturnType).getUpperBound();
|
||||
newReturnType = ((PsiCapturedWildcardType)newReturnType).getUpperBound();
|
||||
}
|
||||
|
||||
if (Comparing.equal(PsiUtil.recaptureWildcards(newReturnType, parent), oldReturnType) &&
|
||||
if (Comparing.equal(newReturnType == null ? null : createTypeMapper().mapType(newReturnType), oldReturnType) &&
|
||||
(Comparing.equal(newTargetMethod, targetMethod) ||
|
||||
!(newTargetMethod.isDeprecated() && !targetMethod.isDeprecated()) &&
|
||||
MethodSignatureUtil.isSuperMethod(newTargetMethod, targetMethod) &&
|
||||
@@ -315,11 +314,11 @@ public class RedundantCastUtil {
|
||||
return false;
|
||||
}
|
||||
else {
|
||||
final PsiElementFactory elementFactory = JavaPsiFacade.getElementFactory(castOperand.getProject());
|
||||
final PsiReferenceExpression newExpression = (PsiReferenceExpression)elementFactory.createExpressionFromText(refExpression.getText(), castOperand);
|
||||
final PsiElementFactory elementFactory = JavaPsiFacade.getElementFactory(refExpression.getProject());
|
||||
final PsiReferenceExpression newExpression = (PsiReferenceExpression)elementFactory.createExpressionFromText(refExpression.getText(), refExpression);
|
||||
final PsiExpression newQualifier = newExpression.getQualifierExpression();
|
||||
LOG.assertTrue(newQualifier != null);
|
||||
newQualifier.replace(castOperand);
|
||||
newQualifier.replace(getInnerMostOperand(newQualifier));
|
||||
|
||||
JavaResolveResult newResult = newExpression.advancedResolve(false);
|
||||
if (!newResult.isValidResult()) return false;
|
||||
@@ -388,7 +387,7 @@ public class RedundantCastUtil {
|
||||
if (oldMethod.equals(newResult.getElement()) &&
|
||||
newResult.isValidResult() &&
|
||||
!(newResult instanceof MethodCandidateInfo && ((MethodCandidateInfo)newResult).getInferenceErrorMessage() != null) &&
|
||||
newResult.getSubstitutor().equals(oldResult.getSubstitutor())) {
|
||||
recapture(newResult.getSubstitutor()).equals(oldResult.getSubstitutor())) {
|
||||
PsiExpression newArg = PsiUtil.deparenthesizeExpression(newArgs[i]);
|
||||
if (newArg instanceof PsiFunctionalExpression) {
|
||||
final boolean varargs = newResult instanceof MethodCandidateInfo && ((MethodCandidateInfo)newResult).isVarargs();
|
||||
@@ -452,6 +451,50 @@ public class RedundantCastUtil {
|
||||
processNestedCasts(args);
|
||||
}
|
||||
|
||||
private static PsiSubstitutor recapture(PsiSubstitutor substitutor) {
|
||||
PsiTypeMapper typeMapper = createTypeMapper();
|
||||
PsiSubstitutor result = PsiSubstitutor.EMPTY;
|
||||
for (Map.Entry<PsiTypeParameter, PsiType> entry : substitutor.getSubstitutionMap().entrySet()) {
|
||||
PsiType value = entry.getValue();
|
||||
result = result.put(entry.getKey(), value == null ? null : typeMapper.mapType(value));
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
private static PsiTypeMapper createTypeMapper() {
|
||||
return new PsiTypeMapper() {
|
||||
@Override
|
||||
public PsiType visitType(@NotNull PsiType type) {
|
||||
return type;
|
||||
}
|
||||
|
||||
@Override
|
||||
public PsiType visitClassType(@NotNull PsiClassType classType) {
|
||||
final PsiClassType.ClassResolveResult classResolveResult = classType.resolveGenerics();
|
||||
final PsiClass psiClass = classResolveResult.getElement();
|
||||
final PsiSubstitutor substitutor = classResolveResult.getSubstitutor();
|
||||
if (psiClass == null) return classType;
|
||||
return new PsiImmediateClassType(psiClass, recapture(substitutor));
|
||||
}
|
||||
|
||||
@Override
|
||||
public PsiType visitCapturedWildcardType(@NotNull PsiCapturedWildcardType capturedWildcardType) {
|
||||
PsiElement context = capturedWildcardType.getContext();
|
||||
@Nullable PsiElement original = context.getCopyableUserData(SELF_REFERENCE);
|
||||
if (original != null) {
|
||||
context = original;
|
||||
}
|
||||
PsiCapturedWildcardType mapped =
|
||||
PsiCapturedWildcardType.create(capturedWildcardType.getWildcard(), context, capturedWildcardType.getTypeParameter());
|
||||
|
||||
mapped.setUpperBound(capturedWildcardType.getUpperBound(false).accept(this));
|
||||
|
||||
return mapped;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
private void processNestedCasts(PsiExpression[] args) {
|
||||
for (PsiExpression arg : args) {
|
||||
arg = deparenthesizeExpression(arg);
|
||||
@@ -466,23 +509,58 @@ public class RedundantCastUtil {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static void encode(PsiElement expression) {
|
||||
expression.accept(new JavaRecursiveElementWalkingVisitor() {
|
||||
@Override
|
||||
public void visitElement(@NotNull PsiElement element) {
|
||||
if (element instanceof PsiExpression) {
|
||||
element.putCopyableUserData(SELF_REFERENCE, element);
|
||||
}
|
||||
super.visitElement(element);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private static void clean(PsiElement expression) {
|
||||
expression.accept(new JavaRecursiveElementWalkingVisitor() {
|
||||
@Override
|
||||
public void visitElement(@NotNull PsiElement element) {
|
||||
if (element instanceof PsiExpression) {
|
||||
element.putCopyableUserData(SELF_REFERENCE, null);
|
||||
}
|
||||
super.visitElement(element);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@Nullable
|
||||
private static PsiCall copyCallExpression(PsiCall expression, PsiType typeByParent) {
|
||||
if (typeByParent != null) {
|
||||
return (PsiCall)LambdaUtil.copyWithExpectedType(expression, typeByParent);
|
||||
}
|
||||
else {
|
||||
final PsiCall call = LambdaUtil.treeWalkUp(expression);
|
||||
if (call != null) {
|
||||
Object marker = new Object();
|
||||
PsiTreeUtil.mark(expression, marker);
|
||||
final PsiCall callCopy = LambdaUtil.copyTopLevelCall(call);
|
||||
if (callCopy == null) return null;
|
||||
return (PsiCall)PsiTreeUtil.releaseMark(callCopy, marker);
|
||||
PsiElement encoded = null;
|
||||
try {
|
||||
if (typeByParent != null) {
|
||||
encode(encoded = expression);
|
||||
return (PsiCall)LambdaUtil.copyWithExpectedType(expression, typeByParent);
|
||||
}
|
||||
else {
|
||||
return (PsiCall)expression.copy();
|
||||
final PsiCall call = LambdaUtil.treeWalkUp(expression);
|
||||
if (call != null) {
|
||||
encode(encoded = call);
|
||||
Object marker = new Object();
|
||||
PsiTreeUtil.mark(expression, marker);
|
||||
final PsiCall callCopy = LambdaUtil.copyTopLevelCall(call);
|
||||
if (callCopy == null) return null;
|
||||
return (PsiCall)PsiTreeUtil.releaseMark(callCopy, marker);
|
||||
}
|
||||
else {
|
||||
encode(encoded = expression);
|
||||
return (PsiCall)expression.copy();
|
||||
}
|
||||
}
|
||||
}
|
||||
finally {
|
||||
if (encoded != null) {
|
||||
clean(encoded);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -864,38 +864,6 @@ public final class PsiUtil extends PsiUtilCore {
|
||||
return type;
|
||||
}
|
||||
|
||||
/**
|
||||
* Opens top level captured wildcards and remap them according to the context.
|
||||
* The only valid purpose: allow to speculate on non-physical expressions about types, e.g. to detect redundant casts with 'wildcards'
|
||||
*/
|
||||
static PsiType recaptureWildcards(PsiType type, PsiElement context) {
|
||||
if (type instanceof PsiClassType) {
|
||||
final PsiClassType.ClassResolveResult resolveResult = ((PsiClassType)type).resolveGenerics();
|
||||
final PsiClass aClass = resolveResult.getElement();
|
||||
if (aClass != null) {
|
||||
final PsiSubstitutor substitutor = resolveResult.getSubstitutor();
|
||||
|
||||
PsiSubstitutor resultSubstitution = null;
|
||||
for (PsiTypeParameter parameter : substitutor.getSubstitutionMap().keySet()) {
|
||||
final PsiType substitute = substitutor.substitute(parameter);
|
||||
if (substitute instanceof PsiCapturedWildcardType) {
|
||||
if (resultSubstitution == null) resultSubstitution = substitutor;
|
||||
resultSubstitution = resultSubstitution.put(parameter, ((PsiCapturedWildcardType)substitute).getWildcard());
|
||||
}
|
||||
}
|
||||
|
||||
if (resultSubstitution != null) {
|
||||
final PsiElementFactory factory = JavaPsiFacade.getElementFactory(context.getProject());
|
||||
return captureToplevelWildcards(factory.createType(aClass, resultSubstitution), context);
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (type instanceof PsiArrayType) {
|
||||
return recaptureWildcards(((PsiArrayType)type).getComponentType(), context).createArrayType();
|
||||
}
|
||||
return type;
|
||||
}
|
||||
|
||||
public static boolean isInsideJavadocComment(PsiElement element) {
|
||||
return PsiTreeUtil.getParentOfType(element, PsiDocComment.class, true) != null;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,18 @@
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
import java.util.function.Function;
|
||||
|
||||
class SameQualifiers {
|
||||
|
||||
void m(List<? extends Foo> l) {
|
||||
map(l, (<warning descr="Casting 'getFunction()' to 'Function<Foo, String>' is redundant">Function<Foo, String></warning>)getFunction());
|
||||
}
|
||||
|
||||
static class Foo {}
|
||||
|
||||
static <T,V> void map(Collection<? extends T> collection, Function<? super T, ? extends V> mapping) {}
|
||||
private <T extends Foo> Function<T, String> getFunction() {
|
||||
return node -> node.toString();
|
||||
}
|
||||
}
|
||||
@@ -51,4 +51,5 @@ public class RedundantCast18Test extends LightDaemonAnalyzerTestCase {
|
||||
public void testCastToPrimitive() { doTest();}
|
||||
public void testParenthesisAroundConditional() { doTest();}
|
||||
public void testMiscStatements() { doTest();}
|
||||
public void testSameSubstitutor() { doTest();}
|
||||
}
|
||||
Reference in New Issue
Block a user