mirror of
https://gitflic.ru/project/openide/openide.git
synced 2026-09-27 10:03:11 +07:00
[Java. Inspections] IDEA-381448 better type presentation when nullability conflict occurs
GitOrigin-RevId: 353527faa14b194d45ee50a2a637ceba11b4e175
This commit is contained in:
committed by
intellij-monorepo-bot
parent
6cf75b8725
commit
61c9277c17
@@ -140,7 +140,8 @@ returning.a.class.with.notnull.arguments=\
|
||||
<html><body>\
|
||||
Returning a class with not-null type arguments when a class with nullable type arguments is expected{0}\
|
||||
</body></html>
|
||||
returning.a.type.nullability.conflict.message=Return type:
|
||||
expected.type.nullability.conflict.message=Expected type:
|
||||
actual.type.nullability.conflict.message=Actual type:
|
||||
conflicting.nullability.annotations=Conflicting nullability annotations
|
||||
nullable.stuff.error.overriding.nullable.with.notnull=Overriding a collection of nullable elements with a collection of non-null elements
|
||||
nullable.stuff.error.overriding.notnull.with.nullable=Overriding a collection of non-null elements with a collection of nullable elements
|
||||
|
||||
+1
-1
@@ -542,7 +542,7 @@ public class NullableStuffInspectionBase extends AbstractBaseJavaLocalInspection
|
||||
|
||||
reportProblem(holder, returnValue, LocalQuickFix.EMPTY_ARRAY,
|
||||
messageKey, new Object[]{""},
|
||||
messageKey, new Object[]{NullableStuffInspectionUtil.getTypePresentationInNullabilityConflict(context)});
|
||||
messageKey, new Object[]{NullableStuffInspectionUtil.getNullabilityConflictPresentation(context)});
|
||||
}
|
||||
|
||||
private void checkCollectionNullityOnAssignment(@NotNull PsiElement errorElement,
|
||||
|
||||
+40
-25
@@ -16,38 +16,47 @@ import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
final class NullableStuffInspectionUtil {
|
||||
static @NotNull @NlsSafe String getTypePresentationInNullabilityConflict(@NotNull JavaTypeNullabilityUtil.NullabilityConflictContext context) {
|
||||
PsiElement place = context.getPlace();
|
||||
if (place == null) return "";
|
||||
int dimensionsInArray = (context.type() instanceof PsiArrayType type) ? type.getArrayDimensions() : 0;
|
||||
static @NotNull @NlsSafe String getNullabilityConflictPresentation(@NotNull JavaTypeNullabilityUtil.NullabilityConflictContext context) {
|
||||
HtmlChunk expectedChunk = getSideChunk(context, JavaTypeNullabilityUtil.Side.EXPECTED);
|
||||
HtmlChunk actualChunk = getSideChunk(context, JavaTypeNullabilityUtil.Side.ACTUAL);
|
||||
if (expectedChunk.isEmpty() || actualChunk.isEmpty()) return "";
|
||||
return HtmlChunk.tag("table")
|
||||
.children(expectedChunk, actualChunk)
|
||||
.toString();
|
||||
}
|
||||
|
||||
private static @NotNull HtmlChunk getSideChunk(@NotNull JavaTypeNullabilityUtil.NullabilityConflictContext context,
|
||||
@NotNull JavaTypeNullabilityUtil.Side side) {
|
||||
PsiElement place = context.getPlace(side);
|
||||
if (place == null) return HtmlChunk.empty();
|
||||
int dimensionsInArray = (context.getType(side) instanceof PsiArrayType type) ? type.getArrayDimensions() : 0;
|
||||
|
||||
PsiTypeElement topmostType = PsiTreeUtil.getTopmostParentOfType(place, PsiTypeElement.class);
|
||||
if (topmostType == null) return "";
|
||||
if (topmostType == null) return HtmlChunk.empty();
|
||||
|
||||
PsiTypeElement target = PsiTreeUtil.getParentOfType(place, PsiTypeElement.class, false);
|
||||
if (target == null) return "";
|
||||
if (target == null) return HtmlChunk.empty();
|
||||
|
||||
PsiType outerType = topmostType.getType();
|
||||
if (target == topmostType) {
|
||||
// Presentation is not handling with the whole type
|
||||
return "";
|
||||
return HtmlChunk.empty();
|
||||
}
|
||||
|
||||
List<Integer> path = computeTypeArgumentPath(topmostType, target, dimensionsInArray);
|
||||
if (path == null) return "";
|
||||
if (path == null) return HtmlChunk.empty();
|
||||
|
||||
Context presentationContext = getPresentationContext(outerType, path, outerType instanceof PsiArrayType);
|
||||
|
||||
String typeText = presentationContext.sb.toString();
|
||||
String annotationText = getAnnotationText(context);
|
||||
if (presentationContext.position == null || annotationText == null) return "";
|
||||
HtmlChunk result = generateHtmlChunk(typeText, "@" + annotationText, presentationContext.position);
|
||||
|
||||
return result.toString();
|
||||
String annotationText = getAnnotationText(context, side);
|
||||
if (presentationContext.position == null || annotationText == null) return HtmlChunk.empty();
|
||||
HtmlChunk result = generateHtmlChunk(typeText, "@" + annotationText, side, presentationContext.position);
|
||||
return result;
|
||||
}
|
||||
|
||||
private static @Nullable String getAnnotationText(@NotNull JavaTypeNullabilityUtil.NullabilityConflictContext context) {
|
||||
PsiAnnotation annotation = context.getAnnotation();
|
||||
|
||||
private static @Nullable String getAnnotationText(@NotNull JavaTypeNullabilityUtil.NullabilityConflictContext context, @NotNull JavaTypeNullabilityUtil.Side side) {
|
||||
PsiAnnotation annotation = context.getAnnotation(side);
|
||||
if (annotation == null) return null;
|
||||
PsiJavaCodeReferenceElement ref = annotation.getNameReferenceElement();
|
||||
if (ref == null) return null;
|
||||
@@ -56,18 +65,24 @@ final class NullableStuffInspectionUtil {
|
||||
|
||||
private static @NotNull HtmlChunk generateHtmlChunk(@NlsSafe String text,
|
||||
@NlsSafe String annotationText,
|
||||
@NotNull JavaTypeNullabilityUtil.Side side,
|
||||
int position) {
|
||||
HtmlChunk result = HtmlChunk
|
||||
.p()
|
||||
return HtmlChunk.tag("tr")
|
||||
.children(
|
||||
HtmlChunk.text(JavaAnalysisBundle.message("returning.a.type.nullability.conflict.message")),
|
||||
HtmlChunk.text(" "),
|
||||
HtmlChunk.text(text.substring(0, position)),
|
||||
HtmlChunk.tag("b").addText(annotationText),
|
||||
HtmlChunk.text(" "),
|
||||
HtmlChunk.text(text.substring(position))
|
||||
HtmlChunk.tag("td")
|
||||
.addText(
|
||||
JavaAnalysisBundle.message(
|
||||
side == JavaTypeNullabilityUtil.Side.EXPECTED ? "expected.type.nullability.conflict.message"
|
||||
: "actual.type.nullability.conflict.message"
|
||||
)
|
||||
),
|
||||
HtmlChunk.tag("td").children(
|
||||
HtmlChunk.text(text.substring(0, position)),
|
||||
HtmlChunk.tag("b").addText(annotationText),
|
||||
HtmlChunk.text(" "),
|
||||
HtmlChunk.text(text.substring(position))
|
||||
)
|
||||
);
|
||||
return result;
|
||||
}
|
||||
|
||||
private static @Nullable List<@NotNull Integer> computeTypeArgumentPath(@NotNull PsiTypeElement top, @NotNull PsiTypeElement target, int firstArrayDepth) {
|
||||
|
||||
@@ -240,29 +240,30 @@ public final class JavaTypeNullabilityUtil {
|
||||
Nullability rightNullability = rightTypeNullability.nullability();
|
||||
|
||||
if (leftNullability == Nullability.NOT_NULL && rightNullability == Nullability.NULLABLE) {
|
||||
return new NullabilityConflictContext(NullabilityConflict.NULL_TO_NOT_NULL, rightType);
|
||||
return new NullabilityConflictContext(NullabilityConflict.NULL_TO_NOT_NULL, leftType, rightType);
|
||||
}
|
||||
// It is not possible to have NOT_NULL_TO_NULL conflict when left type is wildcard with upper bound,
|
||||
// e.g., this assignment is legal {@code List<? extends @Nullable Object> = List<@NotNull String>}
|
||||
else if (leftNullability == Nullability.NULLABLE && rightNullability == Nullability.NOT_NULL && !GenericsUtil.isWildcardWithExtendsBound(leftType)) {
|
||||
return new NullabilityConflictContext(NullabilityConflict.NOT_NULL_TO_NULL, rightType);
|
||||
return new NullabilityConflictContext(NullabilityConflict.NOT_NULL_TO_NULL, leftType, rightType);
|
||||
}
|
||||
return NullabilityConflictContext.UNKNOWN;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Holds information about the nullability conflict that might be used to provide more descriptive error messages.
|
||||
*/
|
||||
public static class NullabilityConflictContext {
|
||||
private final @NotNull NullabilityConflict nullabilityConflict;
|
||||
private final @Nullable PsiType type;
|
||||
private final @Nullable PsiType expectedType;
|
||||
private final @Nullable PsiType actualType;
|
||||
|
||||
public static final NullabilityConflictContext UNKNOWN = new NullabilityConflictContext(NullabilityConflict.UNKNOWN, null);
|
||||
public static final NullabilityConflictContext UNKNOWN = new NullabilityConflictContext(NullabilityConflict.UNKNOWN, null, null);
|
||||
|
||||
public NullabilityConflictContext(@NotNull NullabilityConflict nullabilityConflict, @Nullable PsiType type) {
|
||||
this.type = type;
|
||||
public NullabilityConflictContext(@NotNull NullabilityConflict nullabilityConflict, @Nullable PsiType expectedType, @Nullable PsiType actualType) {
|
||||
this.nullabilityConflict = nullabilityConflict;
|
||||
this.expectedType = expectedType;
|
||||
this.actualType = actualType;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -276,14 +277,16 @@ public final class JavaTypeNullabilityUtil {
|
||||
/**
|
||||
* @return part of the actual {@code PsiType} in which the conflict is occurred.
|
||||
*/
|
||||
public PsiType type() {
|
||||
return type;
|
||||
public PsiType getType(@NotNull Side side) {
|
||||
if (side == Side.EXPECTED) return expectedType;
|
||||
return actualType;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return type argument or array type in which the conflict is occurred.
|
||||
*/
|
||||
public @Nullable PsiElement getPlace() {
|
||||
public @Nullable PsiElement getPlace(@NotNull Side side) {
|
||||
PsiType type = getType(side);
|
||||
return getPlace(type);
|
||||
}
|
||||
|
||||
@@ -291,7 +294,8 @@ public final class JavaTypeNullabilityUtil {
|
||||
/**
|
||||
* @return nullability annotation that produces the conflict.
|
||||
*/
|
||||
public @Nullable PsiAnnotation getAnnotation() {
|
||||
public @Nullable PsiAnnotation getAnnotation(@NotNull Side side) {
|
||||
PsiType type = getType(side);
|
||||
if (type == null) return null;
|
||||
TypeNullability nullability = type.getNullability();
|
||||
NullabilityAnnotationInfo info = nullability.toNullabilityAnnotationInfo();
|
||||
@@ -316,6 +320,15 @@ public final class JavaTypeNullabilityUtil {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Represents the side of nullability conflict.
|
||||
* @see NullabilityConflictContext
|
||||
*/
|
||||
public enum Side {
|
||||
EXPECTED,
|
||||
ACTUAL,
|
||||
}
|
||||
|
||||
/**
|
||||
* Represents a conflict in nullability between 2 types
|
||||
*/
|
||||
|
||||
+12
-12
@@ -7,15 +7,15 @@ import java.util.Collection;
|
||||
|
||||
class B<T> {
|
||||
B<@NotNull String> simpleNullableToNotNull(B<@Nullable String> arg) {
|
||||
return <warning descr="Returning a class with nullable type arguments when a class with not-null type arguments is expected" tooltip="Returning a class with nullable type arguments when a class with not-null type arguments is expected<p>Return type: B<<b>@Nullable</b> String></p>">arg</warning>;
|
||||
return <warning descr="Returning a class with nullable type arguments when a class with not-null type arguments is expected" tooltip="Returning a class with nullable type arguments when a class with not-null type arguments is expected<table><tr><td>Expected type:</td><td>B<<b>@NotNull</b> String></td></tr><tr><td>Actual type:</td><td>B<<b>@Nullable</b> String></td></tr></table>">arg</warning>;
|
||||
}
|
||||
|
||||
B<@Nullable String> simpleNotNullToNullable(B<@NotNull String> arg) {
|
||||
return <warning descr="Returning a class with not-null type arguments when a class with nullable type arguments is expected" tooltip="Returning a class with not-null type arguments when a class with nullable type arguments is expected<p>Return type: B<<b>@NotNull</b> String></p>">arg</warning>;
|
||||
return <warning descr="Returning a class with not-null type arguments when a class with nullable type arguments is expected" tooltip="Returning a class with not-null type arguments when a class with nullable type arguments is expected<table><tr><td>Expected type:</td><td>B<<b>@Nullable</b> String></td></tr><tr><td>Actual type:</td><td>B<<b>@NotNull</b> String></td></tr></table>">arg</warning>;
|
||||
}
|
||||
|
||||
B<B<@NotNull String>> nested(B<B<@Nullable String>> arg) {
|
||||
return <warning descr="Returning a class with nullable type arguments when a class with not-null type arguments is expected" tooltip="Returning a class with nullable type arguments when a class with not-null type arguments is expected<p>Return type: B<B<<b>@Nullable</b> String>></p>">arg</warning>;
|
||||
return <warning descr="Returning a class with nullable type arguments when a class with not-null type arguments is expected" tooltip="Returning a class with nullable type arguments when a class with not-null type arguments is expected<table><tr><td>Expected type:</td><td>B<B<<b>@NotNull</b> String>></td></tr><tr><td>Actual type:</td><td>B<B<<b>@Nullable</b> String>></td></tr></table>">arg</warning>;
|
||||
}
|
||||
|
||||
B<? extends @Nullable Object> extendsWildcardNullable(B<@NotNull Object> arg) {
|
||||
@@ -23,7 +23,7 @@ class B<T> {
|
||||
}
|
||||
|
||||
B<? extends @NotNull Object> extendsWildcardNotNull(B<@Nullable String> arg) {
|
||||
return <warning descr="Returning a class with nullable type arguments when a class with not-null type arguments is expected" tooltip="Returning a class with nullable type arguments when a class with not-null type arguments is expected<p>Return type: B<<b>@Nullable</b> String></p>">arg</warning>;
|
||||
return <warning descr="Returning a class with nullable type arguments when a class with not-null type arguments is expected" tooltip="Returning a class with nullable type arguments when a class with not-null type arguments is expected<table><tr><td>Expected type:</td><td>B<? extends <b>@NotNull</b> Object></td></tr><tr><td>Actual type:</td><td>B<<b>@Nullable</b> String></td></tr></table>">arg</warning>;
|
||||
}
|
||||
|
||||
B<? super @NotNull String> SupperWildcard(B<@Nullable Object> arg) {
|
||||
@@ -31,7 +31,7 @@ class B<T> {
|
||||
}
|
||||
|
||||
B<? extends @NotNull Object> extendsWildcardBothNotNull(B<? extends @Nullable Object> arg) {
|
||||
return <warning descr="Returning a class with nullable type arguments when a class with not-null type arguments is expected" tooltip="Returning a class with nullable type arguments when a class with not-null type arguments is expected<p>Return type: B<? extends <b>@Nullable</b> Object></p>">arg</warning>;
|
||||
return <warning descr="Returning a class with nullable type arguments when a class with not-null type arguments is expected" tooltip="Returning a class with nullable type arguments when a class with not-null type arguments is expected<table><tr><td>Expected type:</td><td>B<? extends <b>@NotNull</b> Object></td></tr><tr><td>Actual type:</td><td>B<? extends <b>@Nullable</b> Object></td></tr></table>">arg</warning>;
|
||||
}
|
||||
|
||||
B<? extends @Nullable Object> extendsWildcardBothNullable(B<? extends @NotNull Object> arg) {
|
||||
@@ -43,28 +43,28 @@ class B<T> {
|
||||
}
|
||||
|
||||
B<? extends B<@NotNull Object>> nestedWithWildcard(B<B<@Nullable Object>> arg) {
|
||||
return <warning descr="Returning a class with nullable type arguments when a class with not-null type arguments is expected" tooltip="Returning a class with nullable type arguments when a class with not-null type arguments is expected<p>Return type: B<B<<b>@Nullable</b> Object>></p>">arg</warning>;
|
||||
return <warning descr="Returning a class with nullable type arguments when a class with not-null type arguments is expected" tooltip="Returning a class with nullable type arguments when a class with not-null type arguments is expected<table><tr><td>Expected type:</td><td>B<? extends B<<b>@NotNull</b> Object>></td></tr><tr><td>Actual type:</td><td>B<B<<b>@Nullable</b> Object>></td></tr></table>">arg</warning>;
|
||||
}
|
||||
|
||||
Map<List<String>, List<@Nullable String>> checkIsPerformedIfSecondTypeArgumentIsTheSame(Map<List<String>, List<@NotNull String>> arg) {
|
||||
return <warning descr="Returning a class with not-null type arguments when a class with nullable type arguments is expected" tooltip="Returning a class with not-null type arguments when a class with nullable type arguments is expected<p>Return type: Map<List<String>, List<<b>@NotNull</b> String>></p>">arg</warning>;
|
||||
return <warning descr="Returning a class with not-null type arguments when a class with nullable type arguments is expected" tooltip="Returning a class with not-null type arguments when a class with nullable type arguments is expected<table><tr><td>Expected type:</td><td>Map<List<String>, List<<b>@Nullable</b> String>></td></tr><tr><td>Actual type:</td><td>Map<List<String>, List<<b>@NotNull</b> String>></td></tr></table>">arg</warning>;
|
||||
}
|
||||
|
||||
B<@NotNull String>[] array(B<@Nullable String>[] arg) {
|
||||
return <warning descr="Returning a class with nullable type arguments when a class with not-null type arguments is expected" tooltip="Returning a class with nullable type arguments when a class with not-null type arguments is expected<p>Return type: B<<b>@Nullable</b> String>[]</p>">arg</warning>;
|
||||
return <warning descr="Returning a class with nullable type arguments when a class with not-null type arguments is expected" tooltip="Returning a class with nullable type arguments when a class with not-null type arguments is expected<table><tr><td>Expected type:</td><td>B<<b>@NotNull</b> String>[]</td></tr><tr><td>Actual type:</td><td>B<<b>@Nullable</b> String>[]</td></tr></table>">arg</warning>;
|
||||
}
|
||||
|
||||
Object[] @NotNull [] nullabilityInNestedArray(Object[] @Nullable [] arg) {
|
||||
return <warning descr="Returning a class with nullable type arguments when a class with not-null type arguments is expected" tooltip="Returning a class with nullable type arguments when a class with not-null type arguments is expected<p>Return type: Object[] <b>@Nullable</b> []</p>">arg</warning>;
|
||||
return <warning descr="Returning a class with nullable type arguments when a class with not-null type arguments is expected" tooltip="Returning a class with nullable type arguments when a class with not-null type arguments is expected<table><tr><td>Expected type:</td><td>Object[] <b>@NotNull</b> []</td></tr><tr><td>Actual type:</td><td>Object[] <b>@Nullable</b> []</td></tr></table>">arg</warning>;
|
||||
}
|
||||
|
||||
B<@NotNull String>[][] multiDimensionalArray(B<@Nullable String>[][] arg) {
|
||||
return <warning descr="Returning a class with nullable type arguments when a class with not-null type arguments is expected" tooltip="Returning a class with nullable type arguments when a class with not-null type arguments is expected<p>Return type: B<<b>@Nullable</b> String>[][]</p>">arg</warning>;
|
||||
return <warning descr="Returning a class with nullable type arguments when a class with not-null type arguments is expected" tooltip="Returning a class with nullable type arguments when a class with not-null type arguments is expected<table><tr><td>Expected type:</td><td>B<<b>@NotNull</b> String>[][]</td></tr><tr><td>Actual type:</td><td>B<<b>@Nullable</b> String>[][]</td></tr></table>">arg</warning>;
|
||||
}
|
||||
|
||||
static class C<T, V> {
|
||||
C<Object, @NotNull String> secondArgument(C<Object, @Nullable String> arg) {
|
||||
return <warning descr="Returning a class with nullable type arguments when a class with not-null type arguments is expected" tooltip="Returning a class with nullable type arguments when a class with not-null type arguments is expected<p>Return type: C<Object, <b>@Nullable</b> String></p>">arg</warning>;
|
||||
return <warning descr="Returning a class with nullable type arguments when a class with not-null type arguments is expected" tooltip="Returning a class with nullable type arguments when a class with not-null type arguments is expected<table><tr><td>Expected type:</td><td>C<Object, <b>@NotNull</b> String></td></tr><tr><td>Actual type:</td><td>C<Object, <b>@Nullable</b> String></td></tr></table>">arg</warning>;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -101,7 +101,7 @@ class B<T> {
|
||||
@NullMarked
|
||||
static class ReturnWithNullMarked {
|
||||
static List<@Nullable String> f(List<String> arg) {
|
||||
return <warning descr="Returning a class with not-null type arguments when a class with nullable type arguments is expected" tooltip="Returning a class with not-null type arguments when a class with nullable type arguments is expected<p>Return type: List<<b>@NullMarked</b> String></p>">arg</warning>;
|
||||
return <warning descr="Returning a class with not-null type arguments when a class with nullable type arguments is expected" tooltip="Returning a class with not-null type arguments when a class with nullable type arguments is expected<table><tr><td>Expected type:</td><td>List<<b>@Nullable</b> String></td></tr><tr><td>Actual type:</td><td>List<<b>@NullMarked</b> String></td></tr></table>">arg</warning>;
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user