redundant type arguments: recapture captured wildcards for original elements (IDEA-242856)

GitOrigin-RevId: 6f2da6bbdbc50708571177d6ff5f279a4fee8eb1
This commit is contained in:
Anna Kozlova
2020-06-08 13:45:45 +03:00
committed by intellij-monorepo-bot
parent e2f1377a65
commit 70232ecaa1
5 changed files with 144 additions and 87 deletions
@@ -8,26 +8,26 @@ import com.intellij.codeInsight.daemon.impl.analysis.JavaGenericsUtil;
import com.intellij.openapi.diagnostic.Logger;
import com.intellij.openapi.progress.ProgressManager;
import com.intellij.openapi.util.Comparing;
import com.intellij.openapi.util.Key;
import com.intellij.psi.*;
import com.intellij.psi.impl.source.PsiImmediateClassType;
import com.intellij.psi.impl.RecaptureTypeMapper;
import com.intellij.psi.impl.source.resolve.graphInference.PsiPolyExpressionUtil;
import com.intellij.psi.infos.MethodCandidateInfo;
import com.intellij.psi.tree.IElementType;
import com.intellij.util.CommonProcessors;
import com.intellij.util.IncorrectOperationException;
import com.intellij.util.Processor;
import com.intellij.util.containers.ContainerUtil;
import com.siyeh.ig.bugs.NullArgumentToVariableArgMethodInspection;
import com.siyeh.ig.psiutils.ExpectedTypeUtils;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.*;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Objects;
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() { }
@@ -269,9 +269,9 @@ public class RedundantCastUtil {
if (newCall == null) return false;
PsiExpression newQualifier = deparenthesizeExpression(newCall.getMethodExpression().getQualifierExpression());
LOG.assertTrue(newQualifier != null);
PsiElement oldReference = newQualifier.getCopyableUserData(SELF_REFERENCE);
PsiElement oldReference = newQualifier.getCopyableUserData(RecaptureTypeMapper.SELF_REFERENCE);
PsiElement replace = newQualifier.replace(getInnerMostOperand(newQualifier));
replace.putCopyableUserData(SELF_REFERENCE, oldReference);
replace.putCopyableUserData(RecaptureTypeMapper.SELF_REFERENCE, oldReference);
final JavaResolveResult newResult = newCall.getMethodExpression().advancedResolve(false);
if (!newResult.isValidResult()) return false;
@@ -282,7 +282,7 @@ public class RedundantCastUtil {
PsiType newReturnType = newCall.getType();
PsiType oldReturnType = ((PsiMethodCallExpression)parent).getType();
if (Comparing.equal(newReturnType == null ? null : new RedundantCastRecaptureMapper().mapType(newReturnType), oldReturnType) &&
if (Comparing.equal(newReturnType == null ? null : new RecaptureTypeMapper().mapType(newReturnType), oldReturnType) &&
(Comparing.equal(newTargetMethod, targetMethod) ||
!(newTargetMethod.isDeprecated() && !targetMethod.isDeprecated()) &&
MethodSignatureUtil.isSuperMethod(newTargetMethod, targetMethod) &&
@@ -391,7 +391,7 @@ public class RedundantCastUtil {
return oldMethod.equals(newResult.getElement()) &&
newResult.isValidResult() &&
!(newResult instanceof MethodCandidateInfo && ((MethodCandidateInfo)newResult).getInferenceErrorMessage() != null) &&
new RedundantCastRecaptureMapper().recapture(newResult.getSubstitutor()).equals(oldResult.getSubstitutor());
new RecaptureTypeMapper().recapture(newResult.getSubstitutor()).equals(oldResult.getSubstitutor());
}
private static boolean castForBoxing(PsiExpression operand, PsiType oppositeType, PsiType conditionalType) {
@@ -557,43 +557,16 @@ public class RedundantCastUtil {
@Nullable
private static PsiCall copyCallExpression(PsiCall expression, PsiType typeByParent) {
class Encoder {
private 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 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);
}
});
}
}
Encoder encoder = new Encoder();
PsiElement encoded = null;
try {
if (typeByParent != null && PsiTypesUtil.isDenotableType(typeByParent, expression)) {
encoder.encode(encoded = expression);
RecaptureTypeMapper.encode(encoded = expression);
return (PsiCall)LambdaUtil.copyWithExpectedType(expression, typeByParent);
}
else {
final PsiCall call = LambdaUtil.treeWalkUp(expression);
if (call != null) {
encoder.encode(encoded = call);
RecaptureTypeMapper.encode(encoded = call);
Object marker = new Object();
PsiTreeUtil.mark(expression, marker);
final PsiCall callCopy = LambdaUtil.copyTopLevelCall(call);
@@ -601,7 +574,7 @@ public class RedundantCastUtil {
return (PsiCall)PsiTreeUtil.releaseMark(callCopy, marker);
}
else {
encoder.encode(encoded = expression);
RecaptureTypeMapper.encode(encoded = expression);
return (PsiCall)expression.copy();
}
}
@@ -611,7 +584,7 @@ public class RedundantCastUtil {
}
finally {
if (encoded != null) {
encoder.clean(encoded);
RecaptureTypeMapper.clean(encoded);
}
}
}
@@ -966,49 +939,6 @@ public class RedundantCastUtil {
}
super.visitArrayAccessExpression(expression);
}
private static class RedundantCastRecaptureMapper extends PsiTypeMapper {
private final Set<PsiClassType> myVisited = ContainerUtil.newIdentityTroveSet();
@Override
public PsiType visitType(@NotNull PsiType type) {
return type;
}
@Override
public PsiType visitClassType(@NotNull PsiClassType classType) {
if (!myVisited.add(classType)) return 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));
}
PsiSubstitutor recapture(PsiSubstitutor substitutor) {
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 : mapType(value));
}
return result;
}
@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 static boolean isTypeCastSemantic(PsiTypeCastExpression typeCast) {
@@ -31,6 +31,7 @@ import org.jetbrains.annotations.ApiStatus;
import org.jetbrains.annotations.Nullable;
import java.util.List;
import java.util.Objects;
public class PsiDiamondTypeUtil {
private static final Logger LOG = Logger.getInstance(PsiDiamondTypeUtil.class);
@@ -180,13 +181,15 @@ public class PsiDiamondTypeUtil {
boolean constructorRef,
@Nullable PsiMethod method,
PsiTypeParameter[] typeParameters) {
PsiElement encoded = null;
try {
final PsiElement copy;
final PsiType typeByParent = PsiTypesUtil.getExpectedTypeByParent(context);
if (typeByParent != null) {
if (typeByParent != null && PsiTypesUtil.isDenotableType(typeByParent, context)) {
if (isAugmented(context)) {
return false;
}
RecaptureTypeMapper.encode(encoded = context);
copy = LambdaUtil.copyWithExpectedType(context, typeByParent);
}
else {
@@ -195,6 +198,7 @@ public class PsiDiamondTypeUtil {
PsiTreeUtil.mark(argumentList != null ? argumentList : context, marker);
final PsiCall call = LambdaUtil.treeWalkUp(context);
if (call != null) {
RecaptureTypeMapper.encode(encoded = call);
final PsiCall callCopy = LambdaUtil.copyTopLevelCall(call);
copy = callCopy != null ? PsiTreeUtil.releaseMark(callCopy, marker) : null;
}
@@ -245,15 +249,24 @@ public class PsiDiamondTypeUtil {
PsiCallExpression newParentCall = exprCopy != null ? PsiTreeUtil.getParentOfType(exprCopy, PsiCallExpression.class) : null;
PsiCallExpression oldParentCall = PsiTreeUtil.getParentOfType(context, PsiCallExpression.class);
if (newParentCall != null && oldParentCall != null &&
!newParentCall.resolveMethodGenerics().equals(oldParentCall.resolveMethodGenerics())) {
return false;
if (newParentCall != null && oldParentCall != null) {
JavaResolveResult newResult = newParentCall.resolveMethodGenerics();
JavaResolveResult oldResult = oldParentCall.resolveMethodGenerics();
if (!Objects.equals(newResult.getElement(), oldResult.getElement()) ||
!new RecaptureTypeMapper().recapture(newResult.getSubstitutor()).equals(oldResult.getSubstitutor())) {
return false;
}
}
}
catch (IncorrectOperationException e) {
LOG.info(e);
return false;
}
finally {
if (encoded != null) {
RecaptureTypeMapper.clean(encoded);
}
}
return true;
}
@@ -0,0 +1,80 @@
// Copyright 2000-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.
package com.intellij.psi.impl;
import com.intellij.openapi.util.Key;
import com.intellij.psi.*;
import com.intellij.psi.impl.source.PsiImmediateClassType;
import com.intellij.util.containers.ContainerUtil;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.Map;
import java.util.Set;
public class RecaptureTypeMapper extends PsiTypeMapper {
public static final Key<PsiElement> SELF_REFERENCE = Key.create("SELF_REFERENCE");
private final Set<PsiClassType> myVisited = ContainerUtil.newIdentityTroveSet();
@Override
public PsiType visitType(@NotNull PsiType type) {
return type;
}
@Override
public PsiType visitClassType(@NotNull PsiClassType classType) {
if (!myVisited.add(classType)) return 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));
}
public PsiSubstitutor recapture(PsiSubstitutor substitutor) {
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 : mapType(value));
}
return result;
}
@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;
}
public 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);
}
});
}
public 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);
}
});
}
}
@@ -0,0 +1,17 @@
// "Replace with <>" "true"
interface I<T> {
I<T> then(T t);
}
class J<T extends String> {}
class MyTest {
<K> I<K> when(K p) { return null;}
void m(J<?> l){
when(l).then((J) new J<>());
}
}
@@ -0,0 +1,17 @@
// "Replace with <>" "true"
interface I<T> {
I<T> then(T t);
}
class J<T extends String> {}
class MyTest {
<K> I<K> when(K p) { return null;}
void m(J<?> l){
when(l).then((J)new J<Str<caret>ing>());
}
}