mirror of
https://gitflic.ru/project/openide/openide.git
synced 2026-09-27 10:03:11 +07:00
IDEA-333441 [java-highlighting] Generic type extending primitive wrapper is highlighted in binary operation
GitOrigin-RevId: 6a6ccf257b953c9ab55cc3b46700a236694f1bd3
This commit is contained in:
committed by
intellij-monorepo-bot
parent
e374ba505c
commit
59a4acf2df
@@ -603,7 +603,25 @@ public final class TypeConversionUtil {
|
||||
@TypeRank
|
||||
@Contract(pure = true)
|
||||
public static int getTypeRank(@NotNull PsiType type) {
|
||||
return getTypeRank(type, false);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return 1..MAX_NUMERIC_TYPE if type is primitive numeric type,
|
||||
* BOOL_TYPE for boolean,
|
||||
* STRING_TYPE for String,
|
||||
* Integer.MAX_VALUE for others
|
||||
*/
|
||||
@TypeRank
|
||||
@Contract(pure = true)
|
||||
private static int getTypeRank(@NotNull PsiType type, boolean refWiden) {
|
||||
type = uncapture(type);
|
||||
if (type.equalsToText(JAVA_LANG_STRING)) {
|
||||
return STRING_RANK;
|
||||
}
|
||||
if (refWiden) {
|
||||
type = toUpperBound(type);
|
||||
}
|
||||
PsiPrimitiveType unboxedType = PsiPrimitiveType.getUnboxedType(type);
|
||||
if (unboxedType != null) {
|
||||
type = unboxedType;
|
||||
@@ -613,12 +631,26 @@ public final class TypeConversionUtil {
|
||||
if (rank != -1) {
|
||||
return rank;
|
||||
}
|
||||
if (type.equalsToText(JAVA_LANG_STRING)) {
|
||||
return STRING_RANK;
|
||||
}
|
||||
return UNKNOWN_RANK;
|
||||
}
|
||||
|
||||
private static @NotNull PsiType toUpperBound(@NotNull PsiType type) {
|
||||
Set<PsiClass> visited = null;
|
||||
while (true) {
|
||||
PsiClass psiClass = PsiUtil.resolveClassInClassTypeOnly(type);
|
||||
if (!(psiClass instanceof PsiTypeParameter)) return type;
|
||||
PsiClassType[] types = psiClass.getExtendsListTypes();
|
||||
if (types.length == 0) return type;
|
||||
type = types[0];
|
||||
if (visited == null) {
|
||||
visited = new HashSet<>();
|
||||
visited.add(psiClass);
|
||||
} else {
|
||||
if (!visited.add(psiClass)) return type;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param tokenType JavaTokenType enumeration
|
||||
* @param strict true if operator result type should be convertible to the left operand
|
||||
@@ -640,16 +672,16 @@ public final class TypeConversionUtil {
|
||||
if (ltype == null || rtype == null) return true;
|
||||
int resultTypeRank = BOOL_RANK;
|
||||
boolean isApplicable = false;
|
||||
final int ltypeRank = getTypeRank(ltype);
|
||||
final int rtypeRank = getTypeRank(rtype);
|
||||
final int ltypeRank = getTypeRank(ltype, true);
|
||||
final int rtypeRank = getTypeRank(rtype, true);
|
||||
|
||||
if (tokenType == JavaTokenType.LT || tokenType == JavaTokenType.LE || tokenType == JavaTokenType.GT || tokenType == JavaTokenType.GE) {
|
||||
if (isPrimitiveAndNotNullOrWrapper(ltype) && isPrimitiveAndNotNullOrWrapper(rtype)) {
|
||||
if (isPrimitiveAndNotNullOrWrapper(ltype, true) && isPrimitiveAndNotNullOrWrapper(rtype, true)) {
|
||||
isApplicable = ltypeRank <= MAX_NUMERIC_RANK && rtypeRank <= MAX_NUMERIC_RANK;
|
||||
}
|
||||
}
|
||||
else if (tokenType == JavaTokenType.EQEQ || tokenType == JavaTokenType.NE) {
|
||||
if (isPrimitiveAndNotNullOrWrapper(ltype) && isPrimitiveAndNotNullOrWrapper(rtype) &&
|
||||
if (isPrimitiveAndNotNullOrWrapper(ltype, true) && isPrimitiveAndNotNullOrWrapper(rtype, true) &&
|
||||
(isPrimitiveAndNotNull(ltype) || isPrimitiveAndNotNull(rtype))) {
|
||||
isApplicable = ltypeRank <= MAX_NUMERIC_RANK && rtypeRank <= MAX_NUMERIC_RANK
|
||||
|| ltypeRank == BOOL_RANK && rtypeRank == BOOL_RANK;
|
||||
@@ -685,33 +717,33 @@ public final class TypeConversionUtil {
|
||||
}
|
||||
return !strict || ltype.isAssignableFrom(rtype);
|
||||
}
|
||||
else if (isPrimitiveAndNotNullOrWrapper(ltype) && isPrimitiveAndNotNullOrWrapper(rtype)) {
|
||||
else if (isPrimitiveAndNotNullOrWrapper(ltype, true) && isPrimitiveAndNotNullOrWrapper(rtype, true)) {
|
||||
resultTypeRank = Math.max(ltypeRank, rtypeRank);
|
||||
isApplicable = ltypeRank <= MAX_NUMERIC_RANK && rtypeRank <= MAX_NUMERIC_RANK;
|
||||
}
|
||||
}
|
||||
else if (tokenType == JavaTokenType.ASTERISK || tokenType == JavaTokenType.DIV || tokenType == JavaTokenType.PERC ||
|
||||
tokenType == JavaTokenType.MINUS) {
|
||||
if (isPrimitiveAndNotNullOrWrapper(ltype) && isPrimitiveAndNotNullOrWrapper(rtype)) {
|
||||
if (isPrimitiveAndNotNullOrWrapper(ltype, true) && isPrimitiveAndNotNullOrWrapper(rtype, true)) {
|
||||
resultTypeRank = Math.max(ltypeRank, rtypeRank);
|
||||
isApplicable = ltypeRank <= MAX_NUMERIC_RANK && rtypeRank <= MAX_NUMERIC_RANK;
|
||||
}
|
||||
}
|
||||
else if (tokenType == JavaTokenType.LTLT || tokenType == JavaTokenType.GTGT || tokenType == JavaTokenType.GTGTGT) {
|
||||
if (isPrimitiveAndNotNullOrWrapper(ltype) && isPrimitiveAndNotNullOrWrapper(rtype)) {
|
||||
if (isPrimitiveAndNotNullOrWrapper(ltype, true) && isPrimitiveAndNotNullOrWrapper(rtype, true)) {
|
||||
isApplicable = ltypeRank <= LONG_RANK && rtypeRank <= LONG_RANK;
|
||||
resultTypeRank = INT_RANK;
|
||||
}
|
||||
}
|
||||
else if (tokenType == JavaTokenType.AND || tokenType == JavaTokenType.OR || tokenType == JavaTokenType.XOR) {
|
||||
if (isPrimitiveAndNotNullOrWrapper(ltype) && isPrimitiveAndNotNullOrWrapper(rtype)) {
|
||||
if (isPrimitiveAndNotNullOrWrapper(ltype, true) && isPrimitiveAndNotNullOrWrapper(rtype, true)) {
|
||||
isApplicable = ltypeRank <= LONG_RANK && rtypeRank <= LONG_RANK
|
||||
|| isBooleanType(ltype) && isBooleanType(rtype);
|
||||
resultTypeRank = ltypeRank <= LONG_RANK ? INT_RANK : BOOL_RANK;
|
||||
}
|
||||
}
|
||||
else if (tokenType == JavaTokenType.ANDAND || tokenType == JavaTokenType.OROR) {
|
||||
if (isPrimitiveAndNotNullOrWrapper(ltype) && isPrimitiveAndNotNullOrWrapper(rtype)) {
|
||||
if (isPrimitiveAndNotNullOrWrapper(ltype, true) && isPrimitiveAndNotNullOrWrapper(rtype, true)) {
|
||||
isApplicable = isBooleanType(ltype) && isBooleanType(rtype);
|
||||
}
|
||||
}
|
||||
@@ -722,9 +754,29 @@ public final class TypeConversionUtil {
|
||||
return isApplicable;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param type type to test
|
||||
* @return true if the type is a primitive type (not the null-type) or
|
||||
* a primitive wrapper (like {@link java.lang.Integer}).
|
||||
*/
|
||||
@Contract(pure = true)
|
||||
public static boolean isPrimitiveAndNotNullOrWrapper(PsiType type) {
|
||||
return isPrimitiveAndNotNullOrWrapper(type, false);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param type type to test
|
||||
* @param refWiden whether to widen reference types to their upper bounds
|
||||
* @return true if the type is a primitive type (not the null-type) or
|
||||
* a primitive wrapper (like {@link java.lang.Integer}). If refWiden is true,
|
||||
* generic types like {@code T extends Integer} are also considered to be primitive wrappers.
|
||||
*/
|
||||
@Contract(pure = true)
|
||||
private static boolean isPrimitiveAndNotNullOrWrapper(PsiType type, boolean refWiden) {
|
||||
type = uncapture(type);
|
||||
if (refWiden) {
|
||||
type = toUpperBound(type);
|
||||
}
|
||||
if (type instanceof PsiClassType) {
|
||||
return PsiPrimitiveType.getUnboxedType(type) != null;
|
||||
}
|
||||
@@ -742,7 +794,7 @@ public final class TypeConversionUtil {
|
||||
@Contract(pure = true)
|
||||
public static boolean isUnaryOperatorApplicable(@NotNull PsiJavaToken token, @NotNull PsiType type) {
|
||||
IElementType i = token.getTokenType();
|
||||
int typeRank = getTypeRank(type);
|
||||
int typeRank = getTypeRank(type, true);
|
||||
if (i == JavaTokenType.MINUSMINUS || i == JavaTokenType.PLUSPLUS) {
|
||||
return typeRank <= MAX_NUMERIC_RANK;
|
||||
}
|
||||
|
||||
+14
@@ -0,0 +1,14 @@
|
||||
class Main {
|
||||
private static final <X extends Integer, Y extends X> void add(X x, Y y) {
|
||||
System.out.println(x + y);
|
||||
}
|
||||
|
||||
private static <X extends String, Y extends X> void concat(X x, Y y) {
|
||||
System.out.println(<error descr="Operator '+' cannot be applied to 'X', 'Y'">x+y</error>);
|
||||
}
|
||||
|
||||
public static void main(String[] args) {
|
||||
add(10, 20);
|
||||
concat("Hello", "World");
|
||||
}
|
||||
}
|
||||
+1
@@ -1206,6 +1206,7 @@ public class GenericsHighlighting8Test extends LightDaemonAnalyzerTestCase {
|
||||
public void testInferenceErrorAttribution() {doTest();}
|
||||
public void testLocalClassParameters() {doTest();}
|
||||
public void testRawAtFBoundAtNew() { doTest(); }
|
||||
public void testUnboxingTrivialGeneric(){doTest();}
|
||||
|
||||
@TestFor(issues = "IDEA-378878")
|
||||
public void testWildcardAfterMethodRef() { doTest(); }
|
||||
|
||||
Reference in New Issue
Block a user