Skip to content

Commit

Permalink
added code from next IntelliJ version to get better generic types
Browse files Browse the repository at this point in the history
  • Loading branch information
mplushnikov committed Aug 30, 2020
1 parent 53223bb commit fe54cea
Show file tree
Hide file tree
Showing 2 changed files with 203 additions and 1 deletion.
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
import com.intellij.psi.util.TypeConversionUtil;
import de.plushnikov.intellij.plugin.problem.LombokProblem;
import de.plushnikov.intellij.plugin.settings.ProjectSettings;
import de.plushnikov.intellij.plugin.util.JavaVarTypeUtilBP;
import lombok.val;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
Expand Down Expand Up @@ -234,7 +235,7 @@ private PsiType processLocalVariableInitializer(final PsiExpression psiExpressio
result = RecursionManager.doPreventingRecursion(psiExpression, true, psiExpression::getType);
}
}
return result;
return result != null ? JavaVarTypeUtilBP.getUpwardProjection(result) : result;
}

private PsiType processParameterDeclaration(PsiElement parentDeclarationScope) {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,201 @@
package de.plushnikov.intellij.plugin.util;

import com.intellij.openapi.util.RecursionGuard;
import com.intellij.openapi.util.RecursionManager;
import com.intellij.psi.*;
import com.intellij.psi.impl.source.resolve.graphInference.PsiPolyExpressionUtil;
import com.intellij.psi.util.PsiUtil;
import com.intellij.util.ArrayUtil;
import com.intellij.util.containers.ContainerUtil;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;

import java.util.Arrays;

public class JavaVarTypeUtilBP {
public static final RecursionGuard ourVarGuard = RecursionManager.createGuard("var.guard");

public static PsiType getUpwardProjection(@NotNull PsiType t) {
return t.accept(new UpwardProjectionTypeVisitor());
}

public static PsiType getDownwardProjection(@NotNull PsiType type) {
return type.accept(new DownwardProjectionTypeVisitor());
}

private static boolean mentionsRestrictedTypeVariables(PsiType type) {
return type.accept(new PsiTypeVisitor<Boolean>() {
@Override
public Boolean visitType(PsiType type) {
return false;
}

@Override
public Boolean visitCapturedWildcardType(PsiCapturedWildcardType capturedWildcardType) {
return true;
}
});
}

private static class UpwardProjectionTypeVisitor extends PsiTypeVisitorEx<PsiType> {
private static final RecursionGuard upwardGuard = RecursionManager.createGuard("upwardProjectionGuard");

@Override
public PsiType visitType(PsiType type) {
return type;
}

@Nullable
@Override
public PsiType visitCapturedWildcardType(PsiCapturedWildcardType capturedWildcardType) {
return capturedWildcardType.getUpperBound().accept(this);
}

@Override
public PsiType visitArrayType(PsiArrayType arrayType) {
PsiType componentType = arrayType.getComponentType();
return componentType.accept(this).createArrayType();
}

@Nullable
@Override
public PsiType visitLambdaExpressionType(PsiLambdaExpressionType lambdaExpressionType) {
return lambdaExpressionType;
}

@Override
public PsiType visitMethodReferenceType(PsiMethodReferenceType methodReferenceType) {
return methodReferenceType;
}

@Override
public PsiType visitIntersectionType(PsiIntersectionType intersectionType) {
return PsiIntersectionType.createIntersection(Arrays.stream(intersectionType.getConjuncts())
.map(conjunct -> conjunct.accept(this))
.toArray(PsiType[]::new));
}

@Override
public PsiType visitClassType(PsiClassType classType) {
PsiClassType.ClassResolveResult result = classType.resolveGenerics();
PsiClass aClass = result.getElement();
if (aClass != null) {
PsiManager manager = aClass.getManager();
PsiSubstitutor targetSubstitutor = PsiSubstitutor.EMPTY;
PsiSubstitutor substitutor = result.getSubstitutor();
for (PsiTypeParameter parameter : PsiUtil.typeParametersIterable(aClass)) {
PsiType ai = substitutor.substitute(parameter);
targetSubstitutor = targetSubstitutor.put(parameter, ai);
if (ai != null && mentionsRestrictedTypeVariables(ai)) {
if (ai instanceof PsiWildcardType) {

if (((PsiWildcardType) ai).isExtends()) {
targetSubstitutor = targetSubstitutor.put(parameter,
PsiWildcardType.createExtends(manager, ((PsiWildcardType) ai).getExtendsBound().accept(this)));
}

if (((PsiWildcardType) ai).isSuper()) {
targetSubstitutor = targetSubstitutor.put(parameter, createDownwardProjection(manager, ((PsiWildcardType) ai).getSuperBound()));
}

} else {
PsiType U = upwardGuard.doPreventingRecursion(ai, true, () -> ai.accept(this));
if (U == null) {
targetSubstitutor = targetSubstitutor.put(parameter, PsiWildcardType.createUnbounded(manager));
} else if (!U.equalsToText(CommonClassNames.JAVA_LANG_OBJECT) && tryUpperBound(aClass, parameter, U)) {
targetSubstitutor = targetSubstitutor.put(parameter, PsiWildcardType.createExtends(manager, U));
} else {
targetSubstitutor = targetSubstitutor.put(parameter, createDownwardProjection(manager, ai));
}
}
}
}
return JavaPsiFacade.getInstance(aClass.getProject()).getElementFactory().createType(aClass, targetSubstitutor);
}
return classType;
}

private static PsiWildcardType createDownwardProjection(PsiManager manager, PsiType bound) {
PsiType downwardProjection = getDownwardProjection(bound);
return downwardProjection != PsiType.NULL ? PsiWildcardType.createSuper(manager, downwardProjection)
: PsiWildcardType.createUnbounded(manager);
}

private static boolean tryUpperBound(PsiClass aClass, PsiTypeParameter parameter, PsiType U) {
PsiClassType[] extendsListTypes = parameter.getExtendsListTypes();
if (extendsListTypes.length == 0) return true;
PsiType bi = PsiIntersectionType.createIntersection(extendsListTypes);
return PsiPolyExpressionUtil.mentionsTypeParameters(bi, ContainerUtil.newHashSet(aClass.getTypeParameters())) ||
!U.isAssignableFrom(bi);
}
}

private static class DownwardProjectionTypeVisitor extends PsiTypeVisitor<PsiType> {
@Override
public PsiType visitType(PsiType type) {
return type;
}

@Override
public PsiType visitCapturedWildcardType(PsiCapturedWildcardType capturedWildcardType) {
return capturedWildcardType.getLowerBound().accept(this);
}

@Override
public PsiType visitArrayType(PsiArrayType arrayType) {
PsiType projection = arrayType.getComponentType().accept(this);
if (projection == PsiType.NULL) return PsiType.NULL;
return projection.createArrayType();
}

@Override
public PsiType visitIntersectionType(PsiIntersectionType intersectionType) {
PsiType[] conjuncts = Arrays.stream(intersectionType.getConjuncts()).map(conjunct -> conjunct.accept(this)).toArray(PsiType[]::new);
if (ArrayUtil.find(conjuncts, PsiType.NULL) > -1) return PsiType.NULL;
return PsiIntersectionType.createIntersection(conjuncts);
}

@Override
public PsiType visitLambdaExpressionType(PsiLambdaExpressionType lambdaExpressionType) {
return lambdaExpressionType;
}

@Override
public PsiType visitMethodReferenceType(PsiMethodReferenceType methodReferenceType) {
return methodReferenceType;
}

@Override
public PsiType visitClassType(PsiClassType classType) {
PsiClassType.ClassResolveResult result = classType.resolveGenerics();
PsiClass aClass = result.getElement();
if (aClass != null) {
PsiSubstitutor substitutor = result.getSubstitutor();
PsiSubstitutor targetSubstitutor = PsiSubstitutor.EMPTY;
for (PsiTypeParameter parameter : PsiUtil.typeParametersIterable(aClass)) {
PsiType ai = substitutor.substitute(parameter);
if (ai == null) return PsiType.NULL;
if (!mentionsRestrictedTypeVariables(ai)) {
targetSubstitutor = targetSubstitutor.put(parameter, ai);
} else if (ai instanceof PsiWildcardType) {
if (((PsiWildcardType) ai).isExtends()) {
PsiType extendsBound = ((PsiWildcardType) ai).getExtendsBound();
PsiType projection = extendsBound.accept(this);
if (projection == PsiType.NULL) return PsiType.NULL;
targetSubstitutor = targetSubstitutor.put(parameter, PsiWildcardType.createExtends(parameter.getManager(), projection));
} else if (((PsiWildcardType) ai).isSuper()) {
PsiType superBound = ((PsiWildcardType) ai).getSuperBound();
targetSubstitutor = targetSubstitutor.put(parameter, getUpwardProjection(superBound));
} else {
return PsiType.NULL;
}
} else {
return PsiType.NULL;
}
}
return JavaPsiFacade.getInstance(aClass.getProject()).getElementFactory().createType(aClass, targetSubstitutor);
}
return PsiType.NULL;
}
}
}

0 comments on commit fe54cea

Please sign in to comment.