From 0e2be796b74fd3af5126be87ddcf6c4469cd1655 Mon Sep 17 00:00:00 2001 From: Tagir Valeev Date: Thu, 9 Jan 2025 10:33:54 +0100 Subject: [PATCH] [java-highlighting] Rework JavaErrorKind creation to builder-like interface Part of IDEA-365344 Create a new Java error highlighter with minimal dependencies (PSI only) GitOrigin-RevId: a8993faa8492f6e104323689f524739d467b7c2a --- .../highlighting/AnnotationChecker.java | 10 +- .../errors/JavaAnnotationValueErrorKind.java | 35 -- .../highlighting/errors/JavaErrorKind.java | 282 +++++++++++++- .../highlighting/errors/JavaErrorKinds.java | 343 ++++++++---------- .../errors/JavaParameterizedErrorKind.java | 31 -- .../errors/JavaSimpleErrorKind.java | 49 --- .../impl/analysis/JavaErrorFixProvider.java | 3 +- 7 files changed, 437 insertions(+), 316 deletions(-) delete mode 100644 java/codeserver/highlighting/src/com/intellij/java/codeserver/highlighting/errors/JavaAnnotationValueErrorKind.java delete mode 100644 java/codeserver/highlighting/src/com/intellij/java/codeserver/highlighting/errors/JavaParameterizedErrorKind.java delete mode 100644 java/codeserver/highlighting/src/com/intellij/java/codeserver/highlighting/errors/JavaSimpleErrorKind.java diff --git a/java/codeserver/highlighting/src/com/intellij/java/codeserver/highlighting/AnnotationChecker.java b/java/codeserver/highlighting/src/com/intellij/java/codeserver/highlighting/AnnotationChecker.java index ffbad6d3499f..60627e578f61 100644 --- a/java/codeserver/highlighting/src/com/intellij/java/codeserver/highlighting/AnnotationChecker.java +++ b/java/codeserver/highlighting/src/com/intellij/java/codeserver/highlighting/AnnotationChecker.java @@ -3,6 +3,7 @@ package com.intellij.java.codeserver.highlighting; import com.intellij.codeInsight.AnnotationTargetUtil; import com.intellij.java.codeserver.highlighting.errors.JavaErrorKinds; +import com.intellij.java.codeserver.highlighting.errors.JavaErrorKinds.AnnotationValueErrorContext; import com.intellij.openapi.util.Comparing; import com.intellij.patterns.ElementPattern; import com.intellij.pom.java.JavaFeature; @@ -458,12 +459,14 @@ final class AnnotationChecker { } } - myVisitor.report(JavaErrorKinds.ANNOTATION_ATTRIBUTE_INCOMPATIBLE_TYPE.create(annotation, method, expectedType)); + myVisitor.report(JavaErrorKinds.ANNOTATION_ATTRIBUTE_INCOMPATIBLE_TYPE.create( + annotation, AnnotationValueErrorContext.from(annotation, method, expectedType))); return; } if (value instanceof PsiArrayInitializerMemberValue arrayValue && !(expectedType instanceof PsiArrayType)) { - myVisitor.report(JavaErrorKinds.ANNOTATION_ATTRIBUTE_ILLEGAL_ARRAY_INITIALIZER.create(arrayValue, method, expectedType)); + myVisitor.report(JavaErrorKinds.ANNOTATION_ATTRIBUTE_ILLEGAL_ARRAY_INITIALIZER.create( + arrayValue, AnnotationValueErrorContext.from(arrayValue, method, expectedType))); return; } @@ -484,7 +487,8 @@ final class AnnotationChecker { return; } - myVisitor.report(JavaErrorKinds.ANNOTATION_ATTRIBUTE_INCOMPATIBLE_TYPE.create(expr, method, expectedType)); + myVisitor.report(JavaErrorKinds.ANNOTATION_ATTRIBUTE_INCOMPATIBLE_TYPE.create( + expr, AnnotationValueErrorContext.from(expr, method, expectedType))); } } diff --git a/java/codeserver/highlighting/src/com/intellij/java/codeserver/highlighting/errors/JavaAnnotationValueErrorKind.java b/java/codeserver/highlighting/src/com/intellij/java/codeserver/highlighting/errors/JavaAnnotationValueErrorKind.java deleted file mode 100644 index 1c947ad5a972..000000000000 --- a/java/codeserver/highlighting/src/com/intellij/java/codeserver/highlighting/errors/JavaAnnotationValueErrorKind.java +++ /dev/null @@ -1,35 +0,0 @@ -// Copyright 2000-2025 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license. -package com.intellij.java.codeserver.highlighting.errors; - -import com.intellij.psi.PsiAnnotationMemberValue; -import com.intellij.psi.PsiAnnotationMethod; -import com.intellij.psi.PsiType; -import com.intellij.psi.util.PsiTreeUtil; -import com.intellij.psi.util.PsiTypesUtil; -import org.jetbrains.annotations.NotNull; - -/** - * Java compilation error connected to annotation attribute value - * @param type of annotation attribute value - */ -public abstract class JavaAnnotationValueErrorKind - extends JavaParameterizedErrorKind { - JavaAnnotationValueErrorKind(@NotNull String key) { - super(key); - } - - public @NotNull JavaCompilationError create(@NotNull Psi value, - @NotNull PsiAnnotationMethod method, - @NotNull PsiType expectedType) { - boolean fromDefaultValue = PsiTreeUtil.isAncestor(method.getDefaultValue(), value, false); - return super.create(value, new AnnotationValueErrorContext(method, expectedType, fromDefaultValue)); - } - - public record AnnotationValueErrorContext(@NotNull PsiAnnotationMethod method, - @NotNull PsiType expectedType, - boolean fromDefaultValue) { - public @NotNull String typeText() { - return PsiTypesUtil.removeExternalAnnotations(expectedType()).getInternalCanonicalText(); - } - } -} diff --git a/java/codeserver/highlighting/src/com/intellij/java/codeserver/highlighting/errors/JavaErrorKind.java b/java/codeserver/highlighting/src/com/intellij/java/codeserver/highlighting/errors/JavaErrorKind.java index f4471171a726..351e05275934 100644 --- a/java/codeserver/highlighting/src/com/intellij/java/codeserver/highlighting/errors/JavaErrorKind.java +++ b/java/codeserver/highlighting/src/com/intellij/java/codeserver/highlighting/errors/JavaErrorKind.java @@ -5,17 +5,23 @@ import com.intellij.java.codeserver.highlighting.JavaCompilationErrorBundle; import com.intellij.openapi.util.TextRange; import com.intellij.openapi.util.text.HtmlChunk; import com.intellij.psi.PsiElement; +import org.jetbrains.annotations.Nls; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import org.jetbrains.annotations.PropertyKey; +import java.util.function.BiConsumer; +import java.util.function.BiFunction; +import java.util.function.Consumer; +import java.util.function.Function; + /** * A kind of Java compilation error * * @param type of context PSI element required for this error * @param additional context required for a particular kind, if any */ -public sealed interface JavaErrorKind permits JavaParameterizedErrorKind, JavaSimpleErrorKind { +public sealed interface JavaErrorKind { /** * @return a key, which uniquely identifies the error kind */ @@ -64,10 +70,276 @@ public sealed interface JavaErrorKind permits J } /** - * @param context context to bind an instance to - * @return create an instance of this error + * Simple kind of error without context + * @param type of PSI element where the error could be attached */ - default @NotNull JavaCompilationError create(@NotNull Psi psi, Context context) { - return new JavaCompilationError<>(this, psi, context); + final class Simple implements JavaErrorKind { + private final @NotNull @PropertyKey(resourceBundle = JavaCompilationErrorBundle.BUNDLE) String myKey; + private final @NotNull Function myDescription; + private final @NotNull Function myAnchor; + private final @NotNull Function myRange; + private final @NotNull Function myHighlightType; + private final @NotNull Consumer myValidator; + + private Simple(@NotNull @PropertyKey(resourceBundle = JavaCompilationErrorBundle.BUNDLE) String key, + @NotNull Function description, + @NotNull Function anchor, + @NotNull Function range, + @NotNull Function type, + @NotNull Consumer validator) { + myKey = key; + myDescription = description; + myAnchor = anchor; + myRange = range; + myHighlightType = type; + myValidator = validator; + } + + Simple(@NotNull @PropertyKey(resourceBundle = JavaCompilationErrorBundle.BUNDLE) String key) { + this(key, + psi -> HtmlChunk.raw(JavaCompilationErrorBundle.message(key)), + Function.identity(), + psi -> null, + psi -> JavaErrorHighlightType.ERROR, + psi -> { }); + } + + @Override + public @NotNull @PropertyKey(resourceBundle = JavaCompilationErrorBundle.BUNDLE) String key() { + return myKey; + } + + @Override + public @NotNull HtmlChunk description(@NotNull Psi psi, Void unused) { + return myDescription.apply(psi); + } + + @Override + public @NotNull PsiElement anchor(@NotNull Psi psi, Void unused) { + return myAnchor.apply(psi); + } + + @Override + public @Nullable TextRange range(@NotNull Psi psi, Void unused) { + return myRange.apply(psi); + } + + @Override + public @NotNull JavaErrorHighlightType highlightType(@NotNull Psi psi, Void unused) { + return myHighlightType.apply(psi); + } + + @Override + public void validate(@NotNull Psi psi, Void unused) throws IllegalArgumentException { + myValidator.accept(psi); + } + + /** + * Creates a new instance of Simple with the specified anchor function. + * + * @param anchor a function that determines the {@link PsiElement} to be used + * as an anchor for a given Psi object. + * @return a new Simple instance with the updated anchor function. + */ + public Simple withAnchor(@NotNull Function anchor) { + return new Simple<>(myKey, myDescription, anchor, myRange, myHighlightType, myValidator); + } + + /** + * Creates a new instance of Simple with the specified range function. + * + * @param range a function that determines the {@link TextRange} for a given Psi object. + * The range is relative to anchor returned from {@link #anchor(PsiElement, Void)} + * @return a new Simple instance with the updated range function. + */ + public Simple withRange(@NotNull Function range) { + return new Simple<>(myKey, myDescription, myAnchor, range, myHighlightType, myValidator); + } + + /** + * Creates a new instance of Simple with the specified highlight type function. + * + * @param type a function that determines the {@link JavaErrorHighlightType} for a given Psi object. + * @return a new Simple instance with the updated highlight type function. + */ + public Simple withHighlightType(@NotNull Function type) { + return new Simple<>(myKey, myDescription, myAnchor, myRange, type, myValidator); + } + + /** + * Creates a new instance of Simple with the specified validator function. + * + * @param validator a consumer that performs validation on a given Psi object + * and potentially throws an {@link IllegalArgumentException} if validation fails. + * @return a new Simple instance with the updated validator function. + */ + public Simple withValidator(@NotNull Consumer validator) { + return new Simple<>(myKey, myDescription, myAnchor, myRange, myHighlightType, validator); + } + + /** + * Creates a new instance of Simple with the specified description function. + * + * @param description a Function that generates a description as an HtmlChunk + * based on the provided Psi object. + * @return a new Simple instance with the updated description function. + */ + public Simple withDescription(@NotNull Function description) { + return new Simple<>(myKey, description, myAnchor, myRange, myHighlightType, myValidator); + } + + /** + * Creates a new instance of Simple with a specified description function. + * + * @param description a Function that computes a description based on the given Psi and Context. + * @return a new Simple instance with the specified description function. + */ + public Simple withRawDescription(@NotNull Function description) { + return withDescription(psi -> HtmlChunk.raw(description.apply(psi))); + } + + public Parameterized withContext() { + return new Parameterized<>(myKey, (psi, ctx) -> myDescription.apply(psi), + (psi, ctx) -> myAnchor.apply(psi), + (psi, ctx) -> myRange.apply(psi), + (psi, ctx) -> myHighlightType.apply(psi), + (psi, ctx) -> myValidator.accept(psi)); + } + + /** + * @param psi psi element to bind an error instance to + * @return an instance of this error + */ + public @NotNull JavaCompilationError create(@NotNull Psi psi) { + return new JavaCompilationError<>(this, psi, null); + } + + @Override + public String toString() { + return "JavaErrorKind[" + myKey + "]"; + } + } + + /** + * Kind of error with context + * @param type of PSI element where the error could be attached + * @param type of context + */ + final class Parameterized implements JavaErrorKind { + private final @NotNull @PropertyKey(resourceBundle = JavaCompilationErrorBundle.BUNDLE) String myKey; + private final @NotNull BiFunction myDescription; + private final @NotNull BiFunction myAnchor; + private final @NotNull BiFunction myRange; + private final @NotNull BiFunction myHighlightType; + private final @NotNull BiConsumer myValidator; + + private Parameterized(@NotNull @PropertyKey(resourceBundle = JavaCompilationErrorBundle.BUNDLE) String key, + @NotNull BiFunction description, + @NotNull BiFunction anchor, + @NotNull BiFunction range, + @NotNull BiFunction type, + @NotNull BiConsumer validator) { + myKey = key; + myDescription = description; + myAnchor = anchor; + myRange = range; + myHighlightType = type; + myValidator = validator; + } + + Parameterized(@NotNull @PropertyKey(resourceBundle = JavaCompilationErrorBundle.BUNDLE) String key) { + this(key, + (psi, ctx) -> HtmlChunk.raw(JavaCompilationErrorBundle.message(key)), + (psi, ctx) -> psi, + (psi, ctx) -> null, + (psi, ctx) -> JavaErrorHighlightType.ERROR, + (psi, ctx) -> { }); + } + + @Override + public @NotNull @PropertyKey(resourceBundle = JavaCompilationErrorBundle.BUNDLE) String key() { + return myKey; + } + + @Override + public @NotNull HtmlChunk description(@NotNull Psi psi, Context context) { + return myDescription.apply(psi, context); + } + + @Override + public @NotNull PsiElement anchor(@NotNull Psi psi, Context context) { + return myAnchor.apply(psi, context); + } + + @Override + public @Nullable TextRange range(@NotNull Psi psi, Context context) { + return myRange.apply(psi, context); + } + + @Override + public @NotNull JavaErrorHighlightType highlightType(@NotNull Psi psi, Context context) { + return myHighlightType.apply(psi, context); + } + + @Override + public void validate(@NotNull Psi psi, Context context) throws IllegalArgumentException { + myValidator.accept(psi, context); + } + + /** + * @param psi psi element to bind an error instance to + * @param context context to bind an error instance to + * @return an instance of this error + */ + public @NotNull JavaCompilationError create(@NotNull Psi psi, Context context) { + return new JavaCompilationError<>(this, psi, context); + } + + /** + * Creates a new instance of Parameterized with a specified anchor function. + * + * @param anchor a BiFunction that computes an anchor based on the given Psi and Context + * @return a new Parameterized instance with the specified anchor function + */ + public Parameterized withAnchor(@NotNull BiFunction anchor) { + return new Parameterized<>(myKey, myDescription, anchor, myRange, myHighlightType, myValidator); + } + + /** + * Creates a new instance of Parameterized with a specified validator function. + * + * @param validator a BiConsumer that performs validation based on the given Psi and Context + * and potentially throws {@link IllegalArgumentException} if validation fails. + * @return a new Parameterized instance with the specified validator function. + */ + public Parameterized withValidator(@NotNull BiConsumer validator) { + return new Parameterized<>(myKey, myDescription, myAnchor, myRange, myHighlightType, validator); + } + + /** + * Creates a new instance of Parameterized with a specified description function. + * + * @param description a BiFunction that computes a description based on the given Psi and Context. + * @return a new Parameterized instance with the specified description function. + */ + public Parameterized withDescription(@NotNull BiFunction description) { + return new Parameterized<>(myKey, description, myAnchor, myRange, myHighlightType, myValidator); + } + + /** + * Creates a new instance of Parameterized with a raw description function. + * + * @param description a BiFunction that computes a raw description (localized string containing HTML) + * based on the given Psi and Context. + * @return a new Parameterized instance with the specified raw description function. + */ + public Parameterized withRawDescription(@NotNull BiFunction description) { + return withDescription((psi, context) -> HtmlChunk.raw(description.apply(psi, context))); + } + + @Override + public String toString() { + return "JavaErrorKind[" + myKey + "]"; + } } } diff --git a/java/codeserver/highlighting/src/com/intellij/java/codeserver/highlighting/errors/JavaErrorKinds.java b/java/codeserver/highlighting/src/com/intellij/java/codeserver/highlighting/errors/JavaErrorKinds.java index 8c420473df45..c1a1c0d64311 100644 --- a/java/codeserver/highlighting/src/com/intellij/java/codeserver/highlighting/errors/JavaErrorKinds.java +++ b/java/codeserver/highlighting/src/com/intellij/java/codeserver/highlighting/errors/JavaErrorKinds.java @@ -2,11 +2,12 @@ package com.intellij.java.codeserver.highlighting.errors; import com.intellij.core.JavaPsiBundle; +import com.intellij.java.codeserver.highlighting.errors.JavaErrorKind.Parameterized; +import com.intellij.java.codeserver.highlighting.errors.JavaErrorKind.Simple; import com.intellij.openapi.projectRoots.JavaSdkVersion; -import com.intellij.openapi.util.TextRange; -import com.intellij.openapi.util.text.HtmlChunk; import com.intellij.pom.java.JavaFeature; import com.intellij.psi.*; +import com.intellij.psi.util.PsiTreeUtil; import com.intellij.psi.util.PsiTypesUtil; import com.intellij.psi.util.PsiUtil; import org.jetbrains.annotations.NotNull; @@ -16,7 +17,8 @@ import java.util.List; import java.util.stream.Collectors; import static com.intellij.java.codeserver.highlighting.JavaCompilationErrorBundle.message; -import static com.intellij.java.codeserver.highlighting.errors.JavaErrorFormatUtil.*; +import static com.intellij.java.codeserver.highlighting.errors.JavaErrorFormatUtil.formatClass; +import static com.intellij.java.codeserver.highlighting.errors.JavaErrorFormatUtil.formatMethod; import static java.util.Objects.requireNonNull; import static java.util.Objects.requireNonNullElse; @@ -25,208 +27,167 @@ import static java.util.Objects.requireNonNullElse; */ public final class JavaErrorKinds { private JavaErrorKinds() {} - - public static final JavaErrorKind UNSUPPORTED_FEATURE = - new JavaParameterizedErrorKind<>("insufficient.language.level") { - @Override - public @NotNull HtmlChunk description(@NotNull PsiElement element, @NotNull JavaFeature feature) { + + public static final Parameterized UNSUPPORTED_FEATURE = + parameterized(PsiElement.class, JavaFeature.class, "insufficient.language.level") + .withRawDescription((element, feature) -> { String name = feature.getFeatureName(); String version = JavaSdkVersion.fromLanguageLevel(PsiUtil.getLanguageLevel(element)).getDescription(); - return HtmlChunk.raw(message("insufficient.language.level", name, version)); - } - }; + return message("insufficient.language.level", name, version); + }); - public static final JavaSimpleErrorKind ANNOTATION_NOT_ALLOWED_HERE = - new JavaSimpleErrorKind<>("annotation.not.allowed.here"); - public static final JavaSimpleErrorKind ANNOTATION_NOT_ALLOWED_ON_PACKAGE = - new JavaSimpleErrorKind("annotation.not.allowed.on.package") + public static final Simple ANNOTATION_NOT_ALLOWED_HERE = error("annotation.not.allowed.here"); + public static final Simple ANNOTATION_NOT_ALLOWED_ON_PACKAGE = + error(PsiPackageStatement.class, "annotation.not.allowed.on.package") .withAnchor(statement -> requireNonNull(statement.getAnnotationList())); - public static final JavaSimpleErrorKind ANNOTATION_MEMBER_THROWS_NOT_ALLOWED = - new JavaSimpleErrorKind("annotation.member.may.not.have.throws.list") - .withAnchor(list -> requireNonNull(list.getFirstChild())); - public static final JavaSimpleErrorKind ANNOTATION_NOT_ALLOWED_EXTENDS = - new JavaSimpleErrorKind("annotation.may.not.have.extends.list") - .withAnchor(list -> requireNonNull(list.getFirstChild())); - public static final JavaSimpleErrorKind ANNOTATION_NOT_ALLOWED_VAR = - new JavaSimpleErrorKind<>("annotation.not.allowed.var"); - public static final JavaSimpleErrorKind ANNOTATION_NOT_ALLOWED_VOID = - new JavaSimpleErrorKind<>("annotation.not.allowed.void"); - public static final JavaSimpleErrorKind ANNOTATION_NOT_ALLOWED_CLASS = - new JavaSimpleErrorKind<>("annotation.not.allowed.class"); - public static final JavaSimpleErrorKind ANNOTATION_NOT_ALLOWED_REF = - new JavaSimpleErrorKind<>("annotation.not.allowed.ref"); - public static final JavaSimpleErrorKind ANNOTATION_NOT_ALLOWED_STATIC = - new JavaSimpleErrorKind<>("annotation.not.allowed.static"); - public static final JavaSimpleErrorKind ANNOTATION_TYPE_EXPECTED = - new JavaSimpleErrorKind<>("annotation.type.expected"); - public static final JavaSimpleErrorKind ANNOTATION_REPEATED_TARGET = - new JavaSimpleErrorKind<>("annotation.repeated.target"); - public static final JavaSimpleErrorKind ANNOTATION_ATTRIBUTE_ANNOTATION_NAME_IS_MISSING = - new JavaSimpleErrorKind<>("annotation.attribute.annotation.name.is.missing"); - public static final JavaSimpleErrorKind ANNOTATION_ATTRIBUTE_NON_CLASS_LITERAL = - new JavaSimpleErrorKind<>("annotation.attribute.non.class.literal"); - public static final JavaSimpleErrorKind ANNOTATION_ATTRIBUTE_NON_ENUM_CONSTANT = - new JavaSimpleErrorKind<>("annotation.attribute.non.enum.constant"); - public static final JavaSimpleErrorKind ANNOTATION_ATTRIBUTE_NON_CONSTANT = - new JavaSimpleErrorKind<>("annotation.attribute.non.constant"); - public static final JavaSimpleErrorKind ANNOTATION_CYCLIC_TYPE = - new JavaSimpleErrorKind<>("annotation.cyclic.element.type"); - public static final JavaErrorKind ANNOTATION_MEMBER_CLASH = - new JavaParameterizedErrorKind<>("annotation.member.clash") { - @Override - public @NotNull HtmlChunk description(@NotNull PsiMethod curMethod, PsiMethod clashMethod) { + public static final Simple ANNOTATION_MEMBER_THROWS_NOT_ALLOWED = + error(PsiReferenceList.class, "annotation.member.may.not.have.throws.list").withAnchor(list -> requireNonNull(list.getFirstChild())); + public static final Simple ANNOTATION_NOT_ALLOWED_EXTENDS = + error(PsiReferenceList.class, "annotation.may.not.have.extends.list").withAnchor(list -> requireNonNull(list.getFirstChild())); + public static final Simple ANNOTATION_NOT_ALLOWED_VAR = error("annotation.not.allowed.var"); + public static final Simple ANNOTATION_NOT_ALLOWED_VOID = error("annotation.not.allowed.void"); + public static final Simple ANNOTATION_NOT_ALLOWED_CLASS = error("annotation.not.allowed.class"); + public static final Simple ANNOTATION_NOT_ALLOWED_REF = error("annotation.not.allowed.ref"); + public static final Simple ANNOTATION_NOT_ALLOWED_STATIC = error("annotation.not.allowed.static"); + public static final Simple ANNOTATION_TYPE_EXPECTED = error("annotation.type.expected"); + public static final Simple ANNOTATION_REPEATED_TARGET = error("annotation.repeated.target"); + public static final Simple ANNOTATION_ATTRIBUTE_ANNOTATION_NAME_IS_MISSING = + error("annotation.attribute.annotation.name.is.missing"); + public static final Simple ANNOTATION_ATTRIBUTE_NON_CLASS_LITERAL = + error("annotation.attribute.non.class.literal"); + public static final Simple ANNOTATION_ATTRIBUTE_NON_ENUM_CONSTANT = error("annotation.attribute.non.enum.constant"); + public static final Simple ANNOTATION_ATTRIBUTE_NON_CONSTANT = error("annotation.attribute.non.constant"); + public static final Simple ANNOTATION_CYCLIC_TYPE = error("annotation.cyclic.element.type"); + public static final Parameterized ANNOTATION_MEMBER_CLASH = + error(PsiMethod.class, "annotation.member.clash") + .withAnchor(curMethod -> requireNonNull(curMethod.getNameIdentifier())) + .withContext() + .withRawDescription((curMethod, clashMethod) -> { PsiClass containingClass = requireNonNull(clashMethod.getContainingClass()); - return HtmlChunk.raw(message("annotation.member.clash", formatMethod(clashMethod), formatClass(containingClass))); - } - - @Override - public @NotNull PsiElement anchor(@NotNull PsiMethod curMethod, PsiMethod clashMethod) { - return requireNonNull(curMethod.getNameIdentifier()); - } - }; - public static final JavaErrorKind ANNOTATION_METHOD_INVALID_TYPE = - new JavaParameterizedErrorKind<>("annotation.member.invalid.type") { - @Override - public @NotNull HtmlChunk description(@NotNull PsiTypeElement element, PsiType type) { - return HtmlChunk.raw(message("annotation.member.invalid.type", - type == null ? null : type.getPresentableText())); - } - }; - public static final JavaAnnotationValueErrorKind ANNOTATION_ATTRIBUTE_INCOMPATIBLE_TYPE = - new JavaAnnotationValueErrorKind<>("annotation.attribute.incompatible.type") { - @Override - public @NotNull HtmlChunk description(@NotNull PsiAnnotationMemberValue value, - JavaAnnotationValueErrorKind.@NotNull AnnotationValueErrorContext context) { - String text = value instanceof PsiAnnotation annotation ? requireNonNull(annotation.getNameReferenceElement()).getText() : - PsiTypesUtil.removeExternalAnnotations(requireNonNull(((PsiExpression)value).getType())).getInternalCanonicalText(); - return HtmlChunk.raw(message("annotation.attribute.incompatible.type", context.typeText(), text)); - } - }; - public static final JavaAnnotationValueErrorKind ANNOTATION_ATTRIBUTE_ILLEGAL_ARRAY_INITIALIZER = - new JavaAnnotationValueErrorKind<>("annotation.attribute.illegal.array.initializer") { - @Override - public @NotNull HtmlChunk description(@NotNull PsiArrayInitializerMemberValue element, AnnotationValueErrorContext context) { - return HtmlChunk.raw(message("annotation.attribute.illegal.array.initializer", context.typeText())); - } - }; - public static final JavaErrorKind ANNOTATION_ATTRIBUTE_DUPLICATE = - new JavaParameterizedErrorKind<>("annotation.attribute.duplicate") { - @Override - public @NotNull HtmlChunk description(@NotNull PsiNameValuePair element, String attribute) { - return HtmlChunk.raw(message("annotation.attribute.duplicate", attribute)); - } - }; - public static final JavaErrorKind ANNOTATION_ATTRIBUTE_UNKNOWN_METHOD = - new JavaParameterizedErrorKind<>("annotation.attribute.unknown.method") { - @Override - public @NotNull JavaErrorHighlightType highlightType(@NotNull PsiNameValuePair pair, String s) { - return pair.getName() == null ? JavaErrorHighlightType.ERROR : JavaErrorHighlightType.WRONG_REF; - } - - @Override - public @NotNull PsiElement anchor(@NotNull PsiNameValuePair pair, String s) { - return requireNonNull(pair.getReference()).getElement(); - } - - @Override - public @NotNull HtmlChunk description(@NotNull PsiNameValuePair pair, String methodName) { - return HtmlChunk.raw(message("annotation.attribute.unknown.method", methodName)); - } - }; + return message("annotation.member.clash", formatMethod(clashMethod), formatClass(containingClass)); + }); + public static final Parameterized ANNOTATION_METHOD_INVALID_TYPE = + parameterized(PsiTypeElement.class, PsiType.class, "annotation.member.invalid.type") + .withRawDescription((element, type) -> + message("annotation.member.invalid.type", type == null ? null : type.getPresentableText())); + public static final Parameterized ANNOTATION_ATTRIBUTE_INCOMPATIBLE_TYPE = + parameterized(PsiAnnotationMemberValue.class, AnnotationValueErrorContext.class, + "annotation.attribute.incompatible.type").withRawDescription((value, context) -> { + String text = value instanceof PsiAnnotation annotation ? requireNonNull(annotation.getNameReferenceElement()).getText() : + PsiTypesUtil.removeExternalAnnotations(requireNonNull(((PsiExpression)value).getType())).getInternalCanonicalText(); + return message("annotation.attribute.incompatible.type", context.typeText(), text); + }); + public static final Parameterized ANNOTATION_ATTRIBUTE_ILLEGAL_ARRAY_INITIALIZER = + parameterized(PsiArrayInitializerMemberValue.class, AnnotationValueErrorContext.class, + "annotation.attribute.illegal.array.initializer").withRawDescription((element, context) -> { + return message("annotation.attribute.illegal.array.initializer", context.typeText()); + }); + public static final Parameterized ANNOTATION_ATTRIBUTE_DUPLICATE = + parameterized(PsiNameValuePair.class, String.class, "annotation.attribute.duplicate") + .withRawDescription((pair, attribute) -> message("annotation.attribute.duplicate", attribute)); + public static final Parameterized ANNOTATION_ATTRIBUTE_UNKNOWN_METHOD = + error(PsiNameValuePair.class, "annotation.attribute.unknown.method") + .withAnchor(pair -> requireNonNull(pair.getReference()).getElement()) + .withHighlightType(pair -> pair.getName() == null ? JavaErrorHighlightType.ERROR : JavaErrorHighlightType.WRONG_REF) + .withContext() + .withRawDescription((pair, methodName) -> message("annotation.attribute.unknown.method", methodName)); // Can be anchored on @FunctionalInterface annotation or at call site - public static final JavaErrorKind LAMBDA_NOT_FUNCTIONAL_INTERFACE = - new JavaParameterizedErrorKind<>("lambda.not.a.functional.interface") { - @Override - public @NotNull HtmlChunk description(@NotNull PsiElement element, PsiClass aClass) { - return HtmlChunk.raw(message("lambda.not.a.functional.interface", aClass.getName())); - } - }; + public static final Parameterized LAMBDA_NOT_FUNCTIONAL_INTERFACE = + parameterized(PsiElement.class, PsiClass.class, "lambda.not.a.functional.interface") + .withRawDescription((element, aClass) -> message("lambda.not.a.functional.interface", aClass.getName())); // Can be anchored on @FunctionalInterface annotation or at call site - public static final JavaErrorKind LAMBDA_NO_TARGET_METHOD = - new JavaParameterizedErrorKind<>("lambda.no.target.method.found"); + public static final Parameterized LAMBDA_NO_TARGET_METHOD = + parameterized("lambda.no.target.method.found"); // Can be anchored on @FunctionalInterface annotation or at call site - public static final JavaErrorKind LAMBDA_MULTIPLE_TARGET_METHODS = - new JavaParameterizedErrorKind<>("lambda.multiple.sam.candidates") { - @Override - public @NotNull HtmlChunk description(@NotNull PsiElement element, PsiClass aClass) { - return HtmlChunk.raw(message("lambda.multiple.sam.candidates", aClass.getName())); - } - }; - public static final JavaErrorKind LAMBDA_FUNCTIONAL_INTERFACE_SEALED = - new JavaParameterizedErrorKind<>("lambda.sealed.functional.interface"); - public static final JavaErrorKind> ANNOTATION_NOT_APPLICABLE = - new JavaParameterizedErrorKind<>("annotation.not.applicable") { - @Override - public void validate(@NotNull PsiAnnotation annotation, @NotNull List types) - throws IllegalArgumentException { + public static final Parameterized LAMBDA_MULTIPLE_TARGET_METHODS = + parameterized(PsiElement.class, PsiClass.class, "lambda.multiple.sam.candidates") + .withRawDescription((psi, aClass) -> message("lambda.multiple.sam.candidates", aClass.getName())); + public static final Parameterized LAMBDA_FUNCTIONAL_INTERFACE_SEALED = + parameterized("lambda.sealed.functional.interface"); + public static final Parameterized> ANNOTATION_NOT_APPLICABLE = + error(PsiAnnotation.class, "annotation.not.applicable").<@NotNull List>withContext() + .withValidator((annotation, types) -> { if (types.isEmpty()) { throw new IllegalArgumentException("types must not be empty"); } - } - - @Override - public @NotNull HtmlChunk description(@NotNull PsiAnnotation annotation, @NotNull List types) { + }) + .withRawDescription((annotation, types) -> { String target = JavaPsiBundle.message("annotation.target." + types.get(0)); PsiJavaCodeReferenceElement nameRef = annotation.getNameReferenceElement(); - return HtmlChunk.raw(message( - "annotation.not.applicable", nameRef != null ? nameRef.getText() : annotation.getText(), target)); - } - }; - public static final JavaErrorKind> ANNOTATION_MISSING_ATTRIBUTE = - new JavaParameterizedErrorKind<>("annotation.missing.attribute") { - @Override - public @NotNull PsiElement anchor(@NotNull PsiAnnotation annotation, @NotNull List strings) { - return requireNonNull(annotation.getNameReferenceElement()); - } - - @Override - public @NotNull HtmlChunk description(@NotNull PsiAnnotation annotation, @NotNull List attributeNames) { - return HtmlChunk.raw(message( + return message("annotation.not.applicable", nameRef != null ? nameRef.getText() : annotation.getText(), target); + }); + public static final Parameterized> ANNOTATION_MISSING_ATTRIBUTE = + error(PsiAnnotation.class, "annotation.missing.attribute") + .withAnchor(annotation -> annotation.getNameReferenceElement()) + .<@NotNull List>withContext() + .withRawDescription((annotation, attributeNames) -> message( "annotation.missing.attribute", attributeNames.stream().map(attr -> "'" + attr + "'").collect(Collectors.joining(", ")))); - } - }; - public static final JavaSimpleErrorKind SAFE_VARARGS_ON_RECORD_COMPONENT = - new JavaSimpleErrorKind<>("safe.varargs.on.record.component"); - public static final JavaErrorKind SAFE_VARARGS_ON_FIXED_ARITY = - new JavaParameterizedErrorKind<>("safe.varargs.on.fixed.arity"); - public static final JavaErrorKind SAFE_VARARGS_ON_NON_FINAL_METHOD = - new JavaParameterizedErrorKind<>("safe.varargs.on.non.final.method"); - public static final JavaErrorKind OVERRIDE_ON_STATIC_METHOD = - new JavaParameterizedErrorKind<>("override.on.static.method"); - public static final JavaErrorKind OVERRIDE_ON_NON_OVERRIDING_METHOD = - new JavaParameterizedErrorKind<>("override.on.non-overriding.method"); + public static final Simple SAFE_VARARGS_ON_RECORD_COMPONENT = + error("safe.varargs.on.record.component"); + public static final Parameterized SAFE_VARARGS_ON_FIXED_ARITY = parameterized("safe.varargs.on.fixed.arity"); + public static final Parameterized SAFE_VARARGS_ON_NON_FINAL_METHOD = + parameterized("safe.varargs.on.non.final.method"); + public static final Parameterized OVERRIDE_ON_STATIC_METHOD = parameterized("override.on.static.method"); + public static final Parameterized OVERRIDE_ON_NON_OVERRIDING_METHOD = + parameterized("override.on.non-overriding.method"); - public static final JavaSimpleErrorKind METHOD_DUPLICATE = - new JavaSimpleErrorKind<>("method.duplicate") { - @Override - public @NotNull TextRange range(@NotNull PsiMethod method, Void unused) { - return getMethodDeclarationTextRange(method); - } + public static final Simple METHOD_DUPLICATE = + error(PsiMethod.class, "method.duplicate") + .withRange(JavaErrorFormatUtil::getMethodDeclarationTextRange) + .withRawDescription( + method -> message("method.duplicate", formatMethod(method), formatClass(requireNonNull(method.getContainingClass())))); + public static final Simple RECEIVER_WRONG_CONTEXT = + error(PsiReceiverParameter.class, "receiver.wrong.context").withAnchor(PsiReceiverParameter::getIdentifier); + public static final Simple RECEIVER_STATIC_CONTEXT = + error(PsiReceiverParameter.class, "receiver.static.context").withAnchor(PsiReceiverParameter::getIdentifier); + public static final Simple RECEIVER_WRONG_POSITION = + error(PsiReceiverParameter.class, "receiver.wrong.position").withAnchor(PsiReceiverParameter::getIdentifier); + public static final Parameterized RECEIVER_TYPE_MISMATCH = + error(PsiReceiverParameter.class, "receiver.type.mismatch") + .withAnchor(parameter -> requireNonNullElse(parameter.getTypeElement(), parameter)).withContext(); + public static final Parameterized RECEIVER_NAME_MISMATCH = + error(PsiReceiverParameter.class, "receiver.name.mismatch").withAnchor(PsiReceiverParameter::getIdentifier).withContext(); - @Override - public @NotNull HtmlChunk description(@NotNull PsiMethod method, Void unused) { - PsiClass aClass = requireNonNull(method.getContainingClass()); - return HtmlChunk.raw(message("method.duplicate", formatMethod(method), formatClass(aClass))); - } - }; - public static final JavaSimpleErrorKind RECEIVER_WRONG_CONTEXT = - new JavaSimpleErrorKind("receiver.wrong.context").withAnchor(PsiReceiverParameter::getIdentifier); - public static final JavaSimpleErrorKind RECEIVER_STATIC_CONTEXT = - new JavaSimpleErrorKind("receiver.static.context").withAnchor(PsiReceiverParameter::getIdentifier); - public static final JavaSimpleErrorKind RECEIVER_WRONG_POSITION = - new JavaSimpleErrorKind<>("receiver.wrong.position"); - public static final JavaErrorKind RECEIVER_TYPE_MISMATCH = - new JavaParameterizedErrorKind<>("receiver.type.mismatch") { - @Override - public @NotNull PsiElement anchor(@NotNull PsiReceiverParameter parameter, PsiType type) { - return requireNonNullElse(parameter.getTypeElement(), parameter); - } - }; - public static final JavaErrorKind RECEIVER_NAME_MISMATCH = - new JavaParameterizedErrorKind<>("receiver.name.mismatch") { - @Override - public @NotNull PsiElement anchor(@NotNull PsiReceiverParameter parameter, @Nullable String s) { - return parameter.getIdentifier(); - } - }; + private static @NotNull Simple error(@NotNull String key) { + return new Simple<>(key); + } + + private static @NotNull Simple error(@SuppressWarnings("unused") @NotNull Class psiClass, + @NotNull String key) { + return error(key); + } + + private static @NotNull Parameterized parameterized( + @SuppressWarnings("unused") @NotNull Class psiClass, + @SuppressWarnings("unused") @NotNull Class contextClass, + @NotNull String key) { + return new Parameterized<>(key); + } + + private static @NotNull Parameterized parameterized( + @NotNull String key) { + return new Parameterized<>(key); + } + + /** + * Context for errors related to annotation value + * @param method corresponding annotation method + * @param expectedType expected value type + * @param fromDefaultValue if true, the error is reported for the method default value, rather than for use site + */ + public record AnnotationValueErrorContext(@NotNull PsiAnnotationMethod method, + @NotNull PsiType expectedType, + boolean fromDefaultValue) { + public @NotNull String typeText() { + return PsiTypesUtil.removeExternalAnnotations(expectedType()).getInternalCanonicalText(); + } + + public static @NotNull AnnotationValueErrorContext from(@NotNull PsiAnnotationMemberValue value, + @NotNull PsiAnnotationMethod method, + @NotNull PsiType expectedType) { + boolean fromDefaultValue = PsiTreeUtil.isAncestor(method.getDefaultValue(), value, false); + AnnotationValueErrorContext context = new AnnotationValueErrorContext(method, expectedType, fromDefaultValue); + return context; + } + } } diff --git a/java/codeserver/highlighting/src/com/intellij/java/codeserver/highlighting/errors/JavaParameterizedErrorKind.java b/java/codeserver/highlighting/src/com/intellij/java/codeserver/highlighting/errors/JavaParameterizedErrorKind.java deleted file mode 100644 index dab590528e59..000000000000 --- a/java/codeserver/highlighting/src/com/intellij/java/codeserver/highlighting/errors/JavaParameterizedErrorKind.java +++ /dev/null @@ -1,31 +0,0 @@ -// Copyright 2000-2025 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license. -package com.intellij.java.codeserver.highlighting.errors; - -import com.intellij.java.codeserver.highlighting.JavaCompilationErrorBundle; -import com.intellij.openapi.util.text.HtmlChunk; -import com.intellij.psi.PsiElement; -import org.jetbrains.annotations.NotNull; -import org.jetbrains.annotations.PropertyKey; - -non-sealed class JavaParameterizedErrorKind implements JavaErrorKind { - private final @NotNull @PropertyKey(resourceBundle = JavaCompilationErrorBundle.BUNDLE) String myKey; - - JavaParameterizedErrorKind(@NotNull @PropertyKey(resourceBundle = JavaCompilationErrorBundle.BUNDLE) String key) { - myKey = key; - } - - @Override - public @PropertyKey(resourceBundle = JavaCompilationErrorBundle.BUNDLE) @NotNull String key() { - return myKey; - } - - @Override - public String toString() { - return "JavaErrorKind[" + myKey + "]"; - } - - @Override - public @NotNull HtmlChunk description(@NotNull Psi element, Context context) { - return HtmlChunk.raw(JavaCompilationErrorBundle.message(myKey)); - } -} diff --git a/java/codeserver/highlighting/src/com/intellij/java/codeserver/highlighting/errors/JavaSimpleErrorKind.java b/java/codeserver/highlighting/src/com/intellij/java/codeserver/highlighting/errors/JavaSimpleErrorKind.java deleted file mode 100644 index 24df8dc81351..000000000000 --- a/java/codeserver/highlighting/src/com/intellij/java/codeserver/highlighting/errors/JavaSimpleErrorKind.java +++ /dev/null @@ -1,49 +0,0 @@ -// Copyright 2000-2025 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license. -package com.intellij.java.codeserver.highlighting.errors; - -import com.intellij.java.codeserver.highlighting.JavaCompilationErrorBundle; -import com.intellij.openapi.util.text.HtmlChunk; -import com.intellij.psi.PsiElement; -import org.jetbrains.annotations.NotNull; -import org.jetbrains.annotations.PropertyKey; - -import java.util.function.Function; - -/** - * Java compilation error without additional context - */ -public non-sealed class JavaSimpleErrorKind implements JavaErrorKind { - private final @NotNull @PropertyKey(resourceBundle = JavaCompilationErrorBundle.BUNDLE) String myKey; - - JavaSimpleErrorKind(@NotNull @PropertyKey(resourceBundle = JavaCompilationErrorBundle.BUNDLE) String key) { - myKey = key; - } - - @Override - public @PropertyKey(resourceBundle = JavaCompilationErrorBundle.BUNDLE) @NotNull String key() { - return myKey; - } - - @Override - public @NotNull HtmlChunk description(@NotNull Psi psi, Void unused) { - return HtmlChunk.raw(JavaCompilationErrorBundle.message(myKey)); - } - - public @NotNull JavaCompilationError create(@NotNull Psi psi) { - return JavaErrorKind.super.create(psi, null); - } - - JavaSimpleErrorKind withAnchor(@NotNull Function anchorExtractor) { - return new JavaSimpleErrorKind<>(myKey) { - @Override - public @NotNull PsiElement anchor(@NotNull Psi psi, Void unused) { - return anchorExtractor.apply(psi); - } - }; - } - - @Override - public String toString() { - return "JavaErrorKind[" + myKey + "]"; - } -} diff --git a/java/java-analysis-impl/src/com/intellij/codeInsight/daemon/impl/analysis/JavaErrorFixProvider.java b/java/java-analysis-impl/src/com/intellij/codeInsight/daemon/impl/analysis/JavaErrorFixProvider.java index cf99a0f3dae9..e410788bbff1 100644 --- a/java/java-analysis-impl/src/com/intellij/codeInsight/daemon/impl/analysis/JavaErrorFixProvider.java +++ b/java/java-analysis-impl/src/com/intellij/codeInsight/daemon/impl/analysis/JavaErrorFixProvider.java @@ -8,7 +8,6 @@ import com.intellij.codeInsight.intention.CommonIntentionAction; import com.intellij.codeInsight.intention.QuickFixFactory; import com.intellij.codeInsight.intention.impl.BaseIntentionAction; import com.intellij.java.analysis.JavaAnalysisBundle; -import com.intellij.java.codeserver.highlighting.errors.JavaAnnotationValueErrorKind; import com.intellij.java.codeserver.highlighting.errors.JavaCompilationError; import com.intellij.java.codeserver.highlighting.errors.JavaErrorKind; import com.intellij.psi.*; @@ -122,7 +121,7 @@ final class JavaErrorFixProvider { }); single(ANNOTATION_ATTRIBUTE_UNKNOWN_METHOD, error -> factory.createCreateAnnotationMethodFromUsageFix(error.psi())); single(ANNOTATION_ATTRIBUTE_DUPLICATE, error -> factory.createMergeDuplicateAttributesFix(error.psi())); - JavaFixProvider incompatibleTypeFix = error -> { + JavaFixProvider incompatibleTypeFix = error -> { PsiAnnotationMemberValue value = error.psi(); PsiAnnotationMethod method = error.context().method(); PsiType type = null;