[java-highlighting] Initial draft of highlighting server; some annotation-related errors are reported by it

Part of IDEA-365344 Create a new Java error highlighter with minimal dependencies (PSI only)

GitOrigin-RevId: e815adec0be9017c8237bdd8fe7c67632f2438da
This commit is contained in:
Tagir Valeev
2025-01-07 11:54:43 +00:00
committed by intellij-monorepo-bot
parent 275753dbec
commit e711100561
24 changed files with 715 additions and 228 deletions
+1
View File
@@ -426,6 +426,7 @@
<module fileurl="file://$PROJECT_DIR$/java/java-analysis-api/intellij.java.analysis.iml" filepath="$PROJECT_DIR$/java/java-analysis-api/intellij.java.analysis.iml" />
<module fileurl="file://$PROJECT_DIR$/java/java-analysis-impl/intellij.java.analysis.impl.iml" filepath="$PROJECT_DIR$/java/java-analysis-impl/intellij.java.analysis.impl.iml" />
<module fileurl="file://$PROJECT_DIR$/plugins/ByteCodeViewer/intellij.java.byteCodeViewer.iml" filepath="$PROJECT_DIR$/plugins/ByteCodeViewer/intellij.java.byteCodeViewer.iml" />
<module fileurl="file://$PROJECT_DIR$/java/codeserver/highlighting/intellij.java.codeserver.highlighting.iml" filepath="$PROJECT_DIR$/java/codeserver/highlighting/intellij.java.codeserver.highlighting.iml" />
<module fileurl="file://$PROJECT_DIR$/java/compiler/openapi/intellij.java.compiler.iml" filepath="$PROJECT_DIR$/java/compiler/openapi/intellij.java.compiler.iml" />
<module fileurl="file://$PROJECT_DIR$/java/compiler/javac2/intellij.java.compiler.antTasks.iml" filepath="$PROJECT_DIR$/java/compiler/javac2/intellij.java.compiler.antTasks.iml" />
<module fileurl="file://$PROJECT_DIR$/java/compiler/charts/intellij.java.compiler.charts.iml" filepath="$PROJECT_DIR$/java/compiler/charts/intellij.java.compiler.charts.iml" />
@@ -0,0 +1,17 @@
<?xml version="1.0" encoding="UTF-8"?>
<module type="JAVA_MODULE" version="4">
<component name="NewModuleRootManager" inherit-compiler-output="true">
<exclude-output />
<content url="file://$MODULE_DIR$">
<sourceFolder url="file://$MODULE_DIR$/resources" type="java-resource" />
<sourceFolder url="file://$MODULE_DIR$/src" isTestSource="false" />
</content>
<orderEntry type="inheritedJdk" />
<orderEntry type="sourceFolder" forTests="false" />
<orderEntry type="library" name="jetbrains-annotations" level="project" />
<orderEntry type="module" module-name="intellij.java.frontback.psi" />
<orderEntry type="module" module-name="intellij.platform.util" />
<orderEntry type="module" module-name="intellij.java.psi" />
<orderEntry type="module" module-name="intellij.java.psi.impl" />
</component>
</module>
@@ -0,0 +1,11 @@
insufficient.language.level={0} are not supported at language level ''{1}''
annotation.not.allowed.here=Annotations are not allowed here
annotation.not.applicable=''@{0}'' not applicable to {1}
annotation.not.allowed.void='void' type may not be annotated
annotation.not.allowed.var='var' type may not be annotated
annotation.not.allowed.class=Class literal type may not be annotated
annotation.not.allowed.ref=Annotation is not applicable to this kind of reference
annotation.not.allowed.static=Static member qualifying type may not be annotated
annotation.annotation.type.expected=Annotation type expected
annotation.missing.attribute={0} missing though required
@@ -0,0 +1,188 @@
// 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;
import com.intellij.codeInsight.AnnotationTargetUtil;
import com.intellij.java.codeserver.highlighting.errors.JavaErrorKinds;
import com.intellij.patterns.ElementPattern;
import com.intellij.pom.java.JavaFeature;
import com.intellij.psi.*;
import com.intellij.psi.impl.source.PsiClassReferenceType;
import com.intellij.psi.util.PsiTreeUtil;
import com.intellij.psi.util.PsiUtil;
import org.jetbrains.annotations.Contract;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.*;
import static com.intellij.patterns.PsiJavaPatterns.psiElement;
final class AnnotationChecker {
private static final ElementPattern<PsiElement> ANY_ANNOTATION_ALLOWED = psiElement().andOr(
psiElement().withParent(PsiNameValuePair.class),
psiElement().withParents(PsiArrayInitializerMemberValue.class, PsiNameValuePair.class),
psiElement().withParents(PsiArrayInitializerMemberValue.class, PsiAnnotationMethod.class),
psiElement().withParent(PsiAnnotationMethod.class).afterLeaf(PsiKeyword.DEFAULT),
// Unterminated parameter list like "void test(@NotNull String)": error on annotation looks annoying here
psiElement().withParents(PsiModifierList.class, PsiParameterList.class)
);
private final JavaErrorVisitor myVisitor;
AnnotationChecker(JavaErrorVisitor visitor) { myVisitor = visitor; }
private void checkReferenceTarget(@NotNull PsiAnnotation annotation, @Nullable PsiJavaCodeReferenceElement ref) {
if (ref == null) return;
PsiElement refTarget = ref.resolve();
if (refTarget == null) return;
if (!(refTarget instanceof PsiClass)) {
myVisitor.report(JavaErrorKinds.ANNOTATION_NOT_ALLOWED_REF.create(annotation));
return;
}
PsiElement parent = ref.getParent();
while (parent instanceof PsiJavaCodeReferenceElement referenceElement) {
PsiElement qualified = referenceElement.resolve();
if (qualified instanceof PsiMember member && member.hasModifierProperty(PsiModifier.STATIC)) {
myVisitor.report(JavaErrorKinds.ANNOTATION_NOT_ALLOWED_STATIC.create(annotation));
return;
}
if (qualified instanceof PsiClass) {
parent = parent.getParent();
}
else {
break;
}
}
}
@Contract("null->null; !null->!null")
private static PsiJavaCodeReferenceElement getOutermostReferenceElement(@Nullable PsiJavaCodeReferenceElement ref) {
if (ref == null) return null;
while (ref.getQualifier() instanceof PsiJavaCodeReferenceElement referenceElement) {
ref = referenceElement;
}
return ref;
}
void checkAnnotation(@NotNull PsiAnnotation annotation) {
if (!myVisitor.hasErrorResults()) checkAnnotationApplicability(annotation);
if (!myVisitor.hasErrorResults()) checkAnnotationType(annotation);
if (!myVisitor.hasErrorResults()) checkMissingAttributes(annotation);
}
private void checkMissingAttributes(@NotNull PsiAnnotation annotation) {
PsiJavaCodeReferenceElement nameRef = annotation.getNameReferenceElement();
if (nameRef == null) return;
PsiElement aClass = nameRef.resolve();
if (aClass instanceof PsiClass psiClass && psiClass.isAnnotationType()) {
Set<String> names = new HashSet<>();
PsiNameValuePair[] attributes = annotation.getParameterList().getAttributes();
for (PsiNameValuePair attribute : attributes) {
String name = attribute.getName();
names.add(Objects.requireNonNullElse(name, PsiAnnotation.DEFAULT_REFERENCED_METHOD_NAME));
}
PsiMethod[] annotationMethods = psiClass.getMethods();
List<String> missed = new ArrayList<>();
for (PsiMethod method : annotationMethods) {
if (PsiUtil.isAnnotationMethod(method)) {
PsiAnnotationMethod annotationMethod = (PsiAnnotationMethod)method;
if (annotationMethod.getDefaultValue() == null) {
if (!names.contains(annotationMethod.getName())) {
missed.add(annotationMethod.getName());
}
}
}
}
if (!missed.isEmpty()) {
myVisitor.report(JavaErrorKinds.ANNOTATION_MISSING_ATTRIBUTE.create(annotation, missed));
}
}
}
private void checkAnnotationType(@NotNull PsiAnnotation annotation) {
PsiJavaCodeReferenceElement nameReferenceElement = annotation.getNameReferenceElement();
if (nameReferenceElement != null) {
PsiElement resolved = nameReferenceElement.resolve();
if (resolved != null && (!(resolved instanceof PsiClass psiClass) || !psiClass.isAnnotationType())) {
myVisitor.report(JavaErrorKinds.ANNOTATION_TYPE_EXPECTED.create(nameReferenceElement));
}
}
}
private void checkAnnotationApplicability(@NotNull PsiAnnotation annotation) {
if (ANY_ANNOTATION_ALLOWED.accepts(annotation)) return;
PsiJavaCodeReferenceElement nameRef = annotation.getNameReferenceElement();
if (nameRef == null) return;
PsiAnnotationOwner owner = annotation.getOwner();
PsiAnnotation.TargetType[] targets = AnnotationTargetUtil.getTargetsForLocation(owner);
if (owner == null || targets.length == 0) {
myVisitor.report(JavaErrorKinds.ANNOTATION_NOT_ALLOWED_HERE.create(annotation));
return;
}
if (!(owner instanceof PsiModifierList)) {
myVisitor.checkFeature(annotation, JavaFeature.TYPE_ANNOTATIONS);
if (myVisitor.hasErrorResults()) return;
}
PsiAnnotation.TargetType applicable = AnnotationTargetUtil.findAnnotationTarget(annotation, targets);
if (applicable == PsiAnnotation.TargetType.UNKNOWN) return;
if (applicable == null) {
if (targets.length == 1 && targets[0] == PsiAnnotation.TargetType.TYPE_USE) {
PsiElement parent = annotation.getParent();
if (parent instanceof PsiTypeElement) {
PsiElement modifierList =
PsiTreeUtil.skipSiblingsBackward(parent, PsiWhiteSpace.class, PsiComment.class, PsiTypeParameterList.class);
if (modifierList instanceof PsiModifierList psiModifierList) {
targets = AnnotationTargetUtil.getTargetsForLocation(psiModifierList);
if (AnnotationTargetUtil.findAnnotationTarget(annotation, targets) == null) {
myVisitor.report(JavaErrorKinds.ANNOTATION_NOT_APPLICABLE.create(annotation, Arrays.asList(targets)));
}
return;
}
}
}
myVisitor.report(JavaErrorKinds.ANNOTATION_NOT_APPLICABLE.create(annotation, Arrays.asList(targets)));
return;
}
if (applicable == PsiAnnotation.TargetType.TYPE_USE) {
if (PsiTreeUtil.skipParentsOfType(annotation, PsiTypeElement.class) instanceof PsiClassObjectAccessExpression) {
myVisitor.report(JavaErrorKinds.ANNOTATION_NOT_ALLOWED_CLASS.create(annotation));
return;
}
if (owner instanceof PsiClassReferenceType referenceType) {
PsiJavaCodeReferenceElement ref = referenceType.getReference();
checkReferenceTarget(annotation, ref);
}
else if (owner instanceof PsiModifierList || owner instanceof PsiTypeElement) {
PsiElement nextElement = owner instanceof PsiTypeElement typeElementOwner
? typeElementOwner
: PsiTreeUtil.skipSiblingsForward((PsiModifierList)owner, PsiComment.class, PsiWhiteSpace.class,
PsiTypeParameterList.class);
if (nextElement instanceof PsiTypeElement typeElement) {
PsiType type = typeElement.getType();
//see JLS 9.7.4 Where Annotations May Appear
if (PsiTypes.voidType().equals(type)) {
myVisitor.report(JavaErrorKinds.ANNOTATION_NOT_ALLOWED_VOID.create(annotation));
return;
}
if (typeElement.isInferredType()) {
myVisitor.report(JavaErrorKinds.ANNOTATION_NOT_ALLOWED_VAR.create(annotation));
return;
}
if (!(type instanceof PsiPrimitiveType || type instanceof PsiArrayType)) {
PsiJavaCodeReferenceElement ref = getOutermostReferenceElement(typeElement.getInnermostComponentReferenceElement());
checkReferenceTarget(annotation, ref);
}
}
}
}
}
}
@@ -0,0 +1,26 @@
// 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;
import com.intellij.DynamicBundle;
import org.jetbrains.annotations.Nls;
import org.jetbrains.annotations.NonNls;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.PropertyKey;
import java.util.function.Supplier;
public final class JavaCompilationErrorBundle {
public static final @NonNls String BUNDLE = "messages.JavaCompilationErrorBundle";
private static final DynamicBundle INSTANCE = new DynamicBundle(JavaCompilationErrorBundle.class, BUNDLE);
private JavaCompilationErrorBundle() {
}
public static @NotNull @Nls String message(@NotNull @PropertyKey(resourceBundle = BUNDLE) String key, Object @NotNull ... params) {
return INSTANCE.getMessage(key, params);
}
public static @NotNull Supplier<@Nls String> messagePointer(@NotNull @PropertyKey(resourceBundle = BUNDLE) String key, Object @NotNull ... params) {
return INSTANCE.getLazyMessage(key, params);
}
}
@@ -0,0 +1,37 @@
// 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;
import com.intellij.java.codeserver.highlighting.errors.JavaCompilationError;
import com.intellij.psi.PsiElement;
import com.intellij.psi.PsiFile;
import org.jetbrains.annotations.NotNull;
import java.util.function.Consumer;
/**
* A starting point to highlight errors in Java code
*/
public final class JavaErrorCollector {
private final JavaErrorVisitor myVisitor;
/**
* Create a collector to collect Java errors. After creation every single PSI element in the desired range should be passed into
* {@link #processElement(PsiElement)} to actually gather errors
*
* @param file Java file to process
* @param consumer a consumer to get errors
*/
public JavaErrorCollector(@NotNull PsiFile file, @NotNull Consumer<@NotNull JavaCompilationError<?, ?>> consumer) {
myVisitor = new JavaErrorVisitor(file, consumer);
}
/**
* Finds the errors related to a given element. One must call this method for every element recursively in the desired range
* to get all the errors.
*
* @param element element to find the errors at.
*/
public void processElement(@NotNull PsiElement element) {
element.accept(myVisitor);
}
}
@@ -0,0 +1,62 @@
// 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;
import com.intellij.java.codeserver.highlighting.errors.JavaCompilationError;
import com.intellij.java.codeserver.highlighting.errors.JavaErrorKinds;
import com.intellij.openapi.project.Project;
import com.intellij.pom.java.JavaFeature;
import com.intellij.pom.java.LanguageLevel;
import com.intellij.psi.JavaElementVisitor;
import com.intellij.psi.PsiAnnotation;
import com.intellij.psi.PsiElement;
import com.intellij.psi.PsiFile;
import com.intellij.psi.util.PsiUtil;
import org.jetbrains.annotations.Contract;
import org.jetbrains.annotations.NotNull;
import java.util.function.Consumer;
final class JavaErrorVisitor extends JavaElementVisitor {
private final @NotNull Consumer<JavaCompilationError<?, ?>> myErrorConsumer;
private final @NotNull Project myProject;
private final @NotNull PsiFile myFile;
private final @NotNull LanguageLevel myLanguageLevel;
private final @NotNull AnnotationChecker myAnnotationChecker = new AnnotationChecker(this);
private boolean myHasError; // true if myHolder.add() was called with HighlightInfo of >=ERROR severity. On each .visit(PsiElement) call this flag is reset. Useful to determine whether the error was already reported while visiting this PsiElement.
JavaErrorVisitor(@NotNull PsiFile file, @NotNull Consumer<JavaCompilationError<?, ?>> consumer) {
myFile = file;
myProject = file.getProject();
myLanguageLevel = PsiUtil.getLanguageLevel(file);
myErrorConsumer = consumer;
}
void report(@NotNull JavaCompilationError<?, ?> error) {
myErrorConsumer.accept(error);
myHasError = true;
}
@Contract(pure = true)
boolean hasErrorResults() {
return myHasError;
}
@Override
public void visitElement(@NotNull PsiElement element) {
super.visitElement(element);
myHasError = false;
}
@Override
public void visitAnnotation(@NotNull PsiAnnotation annotation) {
super.visitAnnotation(annotation);
if (!hasErrorResults()) checkFeature(annotation, JavaFeature.ANNOTATIONS);
myAnnotationChecker.checkAnnotation(annotation);
}
void checkFeature(@NotNull PsiElement element, @NotNull JavaFeature feature) {
if (!feature.isSufficient(myLanguageLevel)) {
report(JavaErrorKinds.UNSUPPORTED_FEATURE.create(element, feature));
}
}
}
@@ -0,0 +1,36 @@
// 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.openapi.util.text.HtmlChunk;
import com.intellij.psi.PsiElement;
import org.jetbrains.annotations.NotNull;
/**
* A concrete instance of a Java compilation error
*
* @param kind error kind
* @param psi PSI element where the error occurred
* @param context additional context necessary to properly render the error and the corresponding quick-fixes
* @param <Context> type of the context
*/
public record JavaCompilationError<Psi extends PsiElement, Context>(@NotNull JavaErrorKind<Psi, Context> kind,
@NotNull Psi psi,
Context context) {
public JavaCompilationError {
kind.validate(psi, context);
}
/**
* @return a desired anchor to put the error message at
*/
public @NotNull PsiElement anchor() {
return kind.anchor(psi, context);
}
/**
* @return a user-readable localized error description
*/
public @NotNull HtmlChunk description() {
return kind.description(psi, context);
}
}
@@ -0,0 +1,53 @@
// 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;
/**
* A kind of Java compilation error
*
* @param <Psi> type of context PSI element required for this error
* @param <Context> additional context required for a particular kind, if any
*/
public sealed interface JavaErrorKind<Psi extends PsiElement, Context> permits Parameterized, JavaSimpleErrorKind {
/**
* @return a key, which uniquely identifies the error kind
*/
@NotNull @PropertyKey(resourceBundle = JavaCompilationErrorBundle.BUNDLE) String key();
/**
* @param psi PSI element associated with an error
* @param context a context in which the error should be rendered
* @return rendered localized error description
*/
@NotNull HtmlChunk description(@NotNull Psi psi, Context context);
/**
* @param psi PSI element associated with an error
* @param context a context in which the error should be rendered
* @return error message anchor (must be within the psi)
*/
default @NotNull PsiElement anchor(@NotNull Psi psi, Context context) {
return psi;
}
/**
* @param psi PSI element associated with an error
* @param context a context in which the error should be rendered
* @throws IllegalArgumentException if the context or PSI element are not applicable to this error kind
*/
default void validate(@NotNull Psi psi, Context context) throws IllegalArgumentException {
}
/**
* @param context context to bind an instance to
* @return create an instance of this error
*/
default @NotNull JavaCompilationError<Psi, Context> create(@NotNull Psi psi, Context context) {
return new JavaCompilationError<>(this, psi, context);
}
}
@@ -0,0 +1,80 @@
// 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.core.JavaPsiBundle;
import com.intellij.java.codeserver.highlighting.JavaCompilationErrorBundle;
import com.intellij.openapi.projectRoots.JavaSdkVersion;
import com.intellij.openapi.util.text.HtmlChunk;
import com.intellij.pom.java.JavaFeature;
import com.intellij.psi.PsiAnnotation;
import com.intellij.psi.PsiElement;
import com.intellij.psi.PsiJavaCodeReferenceElement;
import com.intellij.psi.util.PsiUtil;
import org.jetbrains.annotations.NotNull;
import java.util.List;
import java.util.Objects;
import java.util.stream.Collectors;
/**
* All possible Java error kinds
*/
public final class JavaErrorKinds {
private JavaErrorKinds() {}
public static final JavaErrorKind<PsiElement, @NotNull JavaFeature> UNSUPPORTED_FEATURE =
new Parameterized<>("insufficient.language.level") {
@Override
public @NotNull HtmlChunk description(@NotNull PsiElement element, @NotNull JavaFeature feature) {
String name = feature.getFeatureName();
String version = JavaSdkVersion.fromLanguageLevel(PsiUtil.getLanguageLevel(element)).getDescription();
return HtmlChunk.raw(JavaCompilationErrorBundle.message("insufficient.language.level", name, version));
}
};
public static final JavaSimpleErrorKind<PsiAnnotation> ANNOTATION_NOT_ALLOWED_HERE =
new JavaSimpleErrorKind<>("annotation.not.allowed.here");
public static final JavaSimpleErrorKind<PsiAnnotation> ANNOTATION_NOT_ALLOWED_VAR =
new JavaSimpleErrorKind<>("annotation.not.allowed.var");
public static final JavaSimpleErrorKind<PsiAnnotation> ANNOTATION_NOT_ALLOWED_VOID =
new JavaSimpleErrorKind<>("annotation.not.allowed.void");
public static final JavaSimpleErrorKind<PsiAnnotation> ANNOTATION_NOT_ALLOWED_CLASS =
new JavaSimpleErrorKind<>("annotation.not.allowed.class");
public static final JavaSimpleErrorKind<PsiAnnotation> ANNOTATION_NOT_ALLOWED_REF =
new JavaSimpleErrorKind<>("annotation.not.allowed.ref");
public static final JavaSimpleErrorKind<PsiAnnotation> ANNOTATION_NOT_ALLOWED_STATIC =
new JavaSimpleErrorKind<>("annotation.not.allowed.static");
public static final JavaSimpleErrorKind<PsiJavaCodeReferenceElement> ANNOTATION_TYPE_EXPECTED =
new JavaSimpleErrorKind<>("annotation.annotation.type.expected");
public static final JavaErrorKind<PsiAnnotation, @NotNull List<PsiAnnotation.@NotNull TargetType>> ANNOTATION_NOT_APPLICABLE =
new Parameterized<>("annotation.not.applicable") {
@Override
public void validate(@NotNull PsiAnnotation annotation, @NotNull List<PsiAnnotation.@NotNull TargetType> types)
throws IllegalArgumentException {
if (types.isEmpty()) {
throw new IllegalArgumentException("types must not be empty");
}
}
@Override
public @NotNull HtmlChunk description(@NotNull PsiAnnotation annotation, @NotNull List<PsiAnnotation.@NotNull TargetType> types) {
String target = JavaPsiBundle.message("annotation.target." + types.get(0));
PsiJavaCodeReferenceElement nameRef = annotation.getNameReferenceElement();
return HtmlChunk.raw(JavaCompilationErrorBundle.message(
"annotation.not.applicable", nameRef != null ? nameRef.getText() : annotation.getText(), target));
}
};
public static final JavaErrorKind<PsiAnnotation, @NotNull List<String>> ANNOTATION_MISSING_ATTRIBUTE =
new Parameterized<>("annotation.missing.attribute") {
@Override
public @NotNull PsiElement anchor(@NotNull PsiAnnotation annotation, @NotNull List<String> strings) {
return Objects.requireNonNull(annotation.getNameReferenceElement());
}
@Override
public @NotNull HtmlChunk description(@NotNull PsiAnnotation annotation, @NotNull List<String> attributeNames) {
return HtmlChunk.raw(JavaCompilationErrorBundle.message(
"annotation.missing.attribute", attributeNames.stream().map(attr -> "'" + attr + "'").collect(Collectors.joining(", "))));
}
};
}
@@ -0,0 +1,38 @@
// 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;
/**
* Java compilation error without additional context
*/
public final class JavaSimpleErrorKind<Psi extends PsiElement> implements JavaErrorKind<Psi, Void> {
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<Psi, Void> create(@NotNull Psi psi) {
return JavaErrorKind.super.create(psi, null);
}
@Override
public String toString() {
return "JavaErrorKind[" + myKey + "]";
}
}
@@ -0,0 +1,25 @@
// 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.psi.PsiElement;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.PropertyKey;
non-sealed abstract class Parameterized<Psi extends PsiElement, Context> implements JavaErrorKind<Psi, Context> {
private final @NotNull @PropertyKey(resourceBundle = JavaCompilationErrorBundle.BUNDLE) String myKey;
Parameterized(@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 + "]";
}
}
@@ -0,0 +1,5 @@
// Copyright 2000-2025 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
@Experimental
package com.intellij.java.codeserver.highlighting.errors;
import org.jetbrains.annotations.ApiStatus.Experimental;
@@ -0,0 +1,8 @@
// Copyright 2000-2025 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
/**
* Error highlighting of Java code
*/
@Experimental
package com.intellij.java.codeserver.highlighting;
import static org.jetbrains.annotations.ApiStatus.Experimental;
@@ -312,6 +312,9 @@ public abstract class QuickFixFactory {
public abstract @NotNull List<@NotNull LocalQuickFix> registerOrderEntryFixes(@NotNull PsiReference reference,
@NotNull List<? super IntentionAction> registrar);
/**
* @param annotationMethods unused, could be empty array
*/
public abstract @NotNull IntentionAction createAddMissingRequiredAnnotationParametersFix(@NotNull PsiAnnotation annotation,
PsiMethod @NotNull [] annotationMethods,
@NotNull Collection<String> missedElements);
@@ -29,5 +29,6 @@
<orderEntry type="module" module-name="intellij.platform.backend.workspace" />
<orderEntry type="module" module-name="intellij.java.frontback.impl" exported="" />
<orderEntry type="module" module-name="intellij.platform.editor.ex" />
<orderEntry type="module" module-name="intellij.java.codeserver.highlighting" />
</component>
</module>
@@ -6,33 +6,27 @@ import com.intellij.codeInsight.AnnotationUtil;
import com.intellij.codeInsight.daemon.JavaErrorBundle;
import com.intellij.codeInsight.daemon.impl.HighlightInfo;
import com.intellij.codeInsight.daemon.impl.HighlightInfoType;
import com.intellij.codeInsight.daemon.impl.quickfix.MoveAnnotationOnStaticMemberQualifyingTypeFix;
import com.intellij.codeInsight.daemon.impl.quickfix.MoveAnnotationToPackageInfoFileFix;
import com.intellij.codeInsight.daemon.impl.quickfix.ReplaceVarWithExplicitTypeFix;
import com.intellij.codeInsight.intention.IntentionAction;
import com.intellij.codeInsight.intention.QuickFixFactory;
import com.intellij.codeInsight.intention.impl.BaseIntentionAction;
import com.intellij.codeInspection.LocalQuickFixAndIntentionActionOnPsiElement;
import com.intellij.core.JavaPsiBundle;
import com.intellij.java.analysis.JavaAnalysisBundle;
import com.intellij.modcommand.ModCommandAction;
import com.intellij.openapi.diagnostic.Logger;
import com.intellij.openapi.util.Comparing;
import com.intellij.openapi.util.NlsContexts;
import com.intellij.openapi.util.text.StringUtil;
import com.intellij.patterns.ElementPattern;
import com.intellij.pom.java.JavaFeature;
import com.intellij.pom.java.LanguageLevel;
import com.intellij.psi.*;
import com.intellij.psi.impl.IncompleteModelUtil;
import com.intellij.psi.impl.PsiImplUtil;
import com.intellij.psi.impl.source.PsiClassReferenceType;
import com.intellij.psi.impl.source.PsiImmediateClassType;
import com.intellij.psi.util.*;
import com.intellij.util.ArrayUtil;
import com.intellij.util.ObjectUtils;
import com.siyeh.ig.psiutils.TypeUtils;
import org.jetbrains.annotations.Contract;
import org.jetbrains.annotations.Nls;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
@@ -40,9 +34,9 @@ import org.jetbrains.annotations.Nullable;
import java.lang.annotation.Documented;
import java.lang.annotation.Inherited;
import java.lang.annotation.RetentionPolicy;
import java.util.*;
import static com.intellij.patterns.PsiJavaPatterns.psiElement;
import java.util.HashSet;
import java.util.Objects;
import java.util.Set;
public final class AnnotationsHighlightUtil {
private static final Logger LOG = Logger.getInstance(AnnotationsHighlightUtil.class);
@@ -313,51 +307,6 @@ public final class AnnotationsHighlightUtil {
return false;
}
static HighlightInfo.Builder checkMissingAttributes(@NotNull PsiAnnotation annotation) {
PsiJavaCodeReferenceElement nameRef = annotation.getNameReferenceElement();
if (nameRef == null) return null;
PsiElement aClass = nameRef.resolve();
if (aClass instanceof PsiClass psiClass && psiClass.isAnnotationType()) {
Set<String> names = new HashSet<>();
PsiNameValuePair[] attributes = annotation.getParameterList().getAttributes();
for (PsiNameValuePair attribute : attributes) {
String name = attribute.getName();
names.add(Objects.requireNonNullElse(name, PsiAnnotation.DEFAULT_REFERENCED_METHOD_NAME));
}
PsiMethod[] annotationMethods = psiClass.getMethods();
List<String> missed = new ArrayList<>();
for (PsiMethod method : annotationMethods) {
if (PsiUtil.isAnnotationMethod(method)) {
PsiAnnotationMethod annotationMethod = (PsiAnnotationMethod)method;
if (annotationMethod.getDefaultValue() == null) {
if (!names.contains(annotationMethod.getName())) {
missed.add(annotationMethod.getName());
}
}
}
}
if (!missed.isEmpty()) {
StringBuffer buff = new StringBuffer("'" + missed.get(0) + "'");
for (int i = 1; i < missed.size(); i++) {
buff.append(", ");
buff.append("'").append(missed.get(i)).append("'");
}
String description = JavaErrorBundle.message("annotation.missing.attribute", buff);
HighlightInfo.Builder info =
HighlightInfo.newHighlightInfo(HighlightInfoType.ERROR).range(nameRef).descriptionAndTooltip(description);
IntentionAction fix = QuickFixFactory.getInstance().createAddMissingRequiredAnnotationParametersFix(
annotation, annotationMethods, missed);
info.registerFix(fix, null, null, null, null);
return info;
}
}
return null;
}
static HighlightInfo.Builder checkConstantExpression(@NotNull PsiExpression expression) {
PsiElement parent = expression.getParent();
if (PsiUtil.isAnnotationMethod(parent) || parent instanceof PsiNameValuePair || parent instanceof PsiArrayInitializerMemberValue) {
@@ -383,115 +332,6 @@ public final class AnnotationsHighlightUtil {
return HighlightInfo.newHighlightInfo(HighlightInfoType.ERROR).range(typeElement).descriptionAndTooltip(description);
}
private static final ElementPattern<PsiElement> ANY_ANNOTATION_ALLOWED = psiElement().andOr(
psiElement().withParent(PsiNameValuePair.class),
psiElement().withParents(PsiArrayInitializerMemberValue.class, PsiNameValuePair.class),
psiElement().withParents(PsiArrayInitializerMemberValue.class, PsiAnnotationMethod.class),
psiElement().withParent(PsiAnnotationMethod.class).afterLeaf(PsiKeyword.DEFAULT),
// Unterminated parameter list like "void test(@NotNull String)": error on annotation looks annoying here
psiElement().withParents(PsiModifierList.class, PsiParameterList.class)
);
public static HighlightInfo.Builder checkApplicability(@NotNull PsiAnnotation annotation, @NotNull LanguageLevel level, @NotNull PsiFile file) {
if (ANY_ANNOTATION_ALLOWED.accepts(annotation)) {
return null;
}
PsiJavaCodeReferenceElement nameRef = annotation.getNameReferenceElement();
if (nameRef == null) return null;
PsiAnnotationOwner owner = annotation.getOwner();
PsiAnnotation.TargetType[] targets = AnnotationTargetUtil.getTargetsForLocation(owner);
if (owner == null || targets.length == 0) {
String message = JavaErrorBundle.message("annotation.not.allowed.here");
return createAnnotationError(annotation, message);
}
if (!(owner instanceof PsiModifierList)) {
HighlightInfo.Builder info = HighlightUtil.checkFeature(annotation, JavaFeature.TYPE_ANNOTATIONS, level, file);
if (info != null) return info;
}
PsiAnnotation.TargetType applicable = AnnotationTargetUtil.findAnnotationTarget(annotation, targets);
if (applicable == PsiAnnotation.TargetType.UNKNOWN) return null;
if (applicable == null) {
if (targets.length == 1 && targets[0] == PsiAnnotation.TargetType.TYPE_USE) {
PsiElement parent = annotation.getParent();
if (parent instanceof PsiTypeElement) {
PsiElement modifierList = PsiTreeUtil.skipSiblingsBackward(parent, PsiWhiteSpace.class, PsiComment.class, PsiTypeParameterList.class);
if (modifierList instanceof PsiModifierList psiModifierList) {
targets = AnnotationTargetUtil.getTargetsForLocation(psiModifierList);
if (AnnotationTargetUtil.findAnnotationTarget(annotation, targets) == null) {
return createNotApplicableTargetInfo(annotation, nameRef, targets);
}
return null;
}
}
}
return createNotApplicableTargetInfo(annotation, nameRef, targets);
}
if (applicable == PsiAnnotation.TargetType.TYPE_USE) {
if (owner instanceof PsiClassReferenceType referenceType) {
PsiJavaCodeReferenceElement ref = referenceType.getReference();
HighlightInfo.Builder info = checkReferenceTarget(annotation, ref);
if (info != null) return info;
}
else if (owner instanceof PsiModifierList || owner instanceof PsiTypeElement) {
PsiElement nextElement = owner instanceof PsiTypeElement typeElementOwner
? typeElementOwner
: PsiTreeUtil.skipSiblingsForward((PsiModifierList)owner, PsiComment.class, PsiWhiteSpace.class, PsiTypeParameterList.class);
if (nextElement instanceof PsiTypeElement typeElement) {
PsiType type = typeElement.getType();
//see JLS 9.7.4 Where Annotations May Appear
if (PsiTypes.voidType().equals(type)) {
String message = JavaErrorBundle.message("annotation.not.allowed.void");
return createAnnotationError(annotation, message);
}
if (typeElement.isInferredType()) {
HighlightInfo.Builder info = HighlightInfo.newHighlightInfo(HighlightInfoType.ERROR)
.range(annotation)
.descriptionAndTooltip(JavaErrorBundle.message("annotation.not.allowed.var"));
IntentionAction action1 =
QuickFixFactory.getInstance().createDeleteFix(annotation, JavaAnalysisBundle.message("intention.text.remove.annotation"));
info.registerFix(action1, null, null, null, null);
ModCommandAction action = new ReplaceVarWithExplicitTypeFix(typeElement);
info.registerFix(action, null, null, null, null);
return info;
}
if (!(type instanceof PsiPrimitiveType || type instanceof PsiArrayType)) {
PsiJavaCodeReferenceElement ref = getOutermostReferenceElement(typeElement.getInnermostComponentReferenceElement());
HighlightInfo.Builder info = checkReferenceTarget(annotation, ref);
if (info != null) return info;
}
}
}
if (PsiTreeUtil.skipParentsOfType(annotation, PsiTypeElement.class) instanceof PsiClassObjectAccessExpression) {
String message = JavaErrorBundle.message("annotation.not.allowed.class");
return createAnnotationError(annotation, message);
}
}
return null;
}
private static @NotNull HighlightInfo.Builder createNotApplicableTargetInfo(@NotNull PsiAnnotation annotation,
PsiJavaCodeReferenceElement nameRef,
PsiAnnotation.TargetType[] targets) {
String target = JavaPsiBundle.message("annotation.target." + targets[0]);
String message = JavaErrorBundle.message("annotation.not.applicable", nameRef.getText(), target);
HighlightInfo.Builder info = createAnnotationError(annotation, message);
if (BaseIntentionAction.canModify(Objects.requireNonNull(annotation.resolveAnnotationType()))) {
for (PsiAnnotation.TargetType targetType : targets) {
IntentionAction action = QuickFixFactory.getInstance().createAddAnnotationTargetFix(annotation, targetType);
info.registerFix(action, null, null, null, null);
}
}
return info;
}
private static @NotNull HighlightInfo.Builder createAnnotationError(@NotNull PsiAnnotation annotation, @NotNull @NlsContexts.DetailedDescription String message) {
LocalQuickFixAndIntentionActionOnPsiElement fix = QuickFixFactory.getInstance()
.createDeleteFix(annotation, JavaAnalysisBundle.message("intention.text.remove.annotation"));
@@ -508,55 +348,6 @@ public final class AnnotationsHighlightUtil {
return info;
}
private static @Nullable HighlightInfo.Builder checkReferenceTarget(@NotNull PsiAnnotation annotation, @Nullable PsiJavaCodeReferenceElement ref) {
if (ref == null) return null;
PsiElement refTarget = ref.resolve();
if (refTarget == null) return null;
if (!(refTarget instanceof PsiClass)) {
return createAnnotationError(annotation, JavaErrorBundle.message("annotation.not.allowed.ref"));
}
PsiElement parent = ref.getParent();
while (parent instanceof PsiJavaCodeReferenceElement referenceElement) {
PsiElement qualified = referenceElement.resolve();
if (qualified instanceof PsiMember member && member.hasModifierProperty(PsiModifier.STATIC)) {
return createAnnotationError(annotation,
JavaErrorBundle.message("annotation.not.allowed.static"),
new MoveAnnotationOnStaticMemberQualifyingTypeFix(annotation).asIntention());
}
if (qualified instanceof PsiClass) {
parent = parent.getParent();
}
else {
break;
}
}
return null;
}
@Contract("null->null; !null->!null")
private static PsiJavaCodeReferenceElement getOutermostReferenceElement(@Nullable PsiJavaCodeReferenceElement ref) {
if (ref == null) return null;
while (ref.getQualifier() instanceof PsiJavaCodeReferenceElement referenceElement) {
ref = referenceElement;
}
return ref;
}
static HighlightInfo.Builder checkAnnotationType(@NotNull PsiAnnotation annotation) {
PsiJavaCodeReferenceElement nameReferenceElement = annotation.getNameReferenceElement();
if (nameReferenceElement != null) {
PsiElement resolved = nameReferenceElement.resolve();
if (resolved != null && (!(resolved instanceof PsiClass psiClass) || !psiClass.isAnnotationType())) {
String description = JavaErrorBundle.message("annotation.annotation.type.expected");
return HighlightInfo.newHighlightInfo(HighlightInfoType.ERROR).range(nameReferenceElement).descriptionAndTooltip(description);
}
}
return null;
}
static HighlightInfo.Builder checkCyclicMemberType(@NotNull PsiTypeElement typeElement, @NotNull PsiClass aClass) {
PsiType type = typeElement.getType();
Set<PsiClass> checked = new HashSet<>();
@@ -9,9 +9,11 @@ import com.intellij.codeInsight.daemon.impl.HighlightInfoType;
import com.intellij.codeInsight.daemon.impl.HighlightVisitor;
import com.intellij.codeInsight.daemon.impl.quickfix.AdjustFunctionContextFix;
import com.intellij.codeInsight.daemon.impl.quickfix.QuickFixAction;
import com.intellij.codeInsight.intention.CommonIntentionAction;
import com.intellij.codeInsight.intention.IntentionAction;
import com.intellij.codeInsight.intention.QuickFixFactory;
import com.intellij.codeInspection.ex.GlobalInspectionContextBase;
import com.intellij.java.codeserver.highlighting.JavaErrorCollector;
import com.intellij.lang.annotation.HighlightSeverity;
import com.intellij.lang.injection.InjectedLanguageManager;
import com.intellij.lang.jvm.JvmModifier;
@@ -64,6 +66,7 @@ public class HighlightVisitorImpl extends JavaElementVisitor implements Highligh
private @NotNull PsiFile myFile;
private PsiJavaModule myJavaModule;
private JavaErrorCollector myCollector;
private PreviewFeatureUtil.PreviewFeatureVisitor myPreviewFeatureVisitor;
@@ -164,6 +167,7 @@ public class HighlightVisitorImpl extends JavaElementVisitor implements Highligh
public void visit(@NotNull PsiElement element) {
myHasError = false;
element.accept(this);
myCollector.processElement(element);
}
@Override
@@ -215,6 +219,15 @@ public class HighlightVisitorImpl extends JavaElementVisitor implements Highligh
.notNull(JavaVersionService.getInstance().getJavaSdkVersion(file), JavaSdkVersion.fromLanguageLevel(myLanguageLevel));
myJavaModule = JavaFeature.MODULES.isSufficient(myLanguageLevel) ? JavaModuleGraphUtil.findDescriptorByElement(file) : null;
myPreviewFeatureVisitor = myLanguageLevel.isPreview() ? null : new PreviewFeatureUtil.PreviewFeatureVisitor(myLanguageLevel, myErrorSink);
myCollector = new JavaErrorCollector(myFile, error -> {
HighlightInfo.Builder info = HighlightInfo.newHighlightInfo(HighlightInfoType.ERROR)
.range(error.anchor())
.descriptionAndTooltip(error.description().toString());
for (CommonIntentionAction fix : JavaErrorFixProvider.getFixes(error)) {
info.registerFix(fix.asIntention(), null, null, null, null);
}
add(info);
});
}
@Override
@@ -231,10 +244,6 @@ public class HighlightVisitorImpl extends JavaElementVisitor implements Highligh
@Override
public void visitAnnotation(@NotNull PsiAnnotation annotation) {
super.visitAnnotation(annotation);
if (!hasErrorResults()) add(checkFeature(annotation, JavaFeature.ANNOTATIONS));
if (!hasErrorResults()) add(AnnotationsHighlightUtil.checkApplicability(annotation, myLanguageLevel, myFile));
if (!hasErrorResults()) add(AnnotationsHighlightUtil.checkAnnotationType(annotation));
if (!hasErrorResults()) add(AnnotationsHighlightUtil.checkMissingAttributes(annotation));
if (!hasErrorResults()) add(AnnotationsHighlightUtil.checkTargetAnnotationDuplicates(annotation));
if (!hasErrorResults()) add(AnnotationsHighlightUtil.checkDuplicateAnnotations(annotation, myLanguageLevel));
if (!hasErrorResults()) add(AnnotationsHighlightUtil.checkFunctionalInterface(annotation, myLanguageLevel));
@@ -0,0 +1,90 @@
// 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.codeInsight.daemon.impl.analysis;
import com.intellij.codeInsight.daemon.impl.quickfix.MoveAnnotationOnStaticMemberQualifyingTypeFix;
import com.intellij.codeInsight.daemon.impl.quickfix.ReplaceVarWithExplicitTypeFix;
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.JavaCompilationError;
import com.intellij.java.codeserver.highlighting.errors.JavaErrorKind;
import com.intellij.psi.*;
import com.intellij.psi.util.PsiTreeUtil;
import com.intellij.util.containers.ContainerUtil;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.*;
import static com.intellij.java.codeserver.highlighting.errors.JavaErrorKinds.*;
/**
* Fixes attached to error messages provided by {@link com.intellij.java.codeserver.highlighting.JavaErrorCollector}
*/
final class JavaErrorFixProvider {
@FunctionalInterface
private interface JavaFixProvider<Psi extends PsiElement, Context> {
@Nullable CommonIntentionAction provide(@NotNull JavaCompilationError<? extends Psi, ? extends Context> error);
default JavaFixesProvider<Psi, Context> asMulti() {
return error -> ContainerUtil.createMaybeSingletonList(provide(error));
}
}
@FunctionalInterface
private interface JavaFixesProvider<Psi extends PsiElement, Context> {
@NotNull List<@NotNull CommonIntentionAction> provide(@NotNull JavaCompilationError<? extends Psi, ? extends Context> error);
}
private static final Map<JavaErrorKind<?, ?>, List<JavaFixesProvider<?, ?>>> FIXES = new HashMap<>();
static {
JavaFixProvider<PsiAnnotation, Object> annotationRemover = error ->
QuickFixFactory.getInstance().createDeleteFix(error.psi(), JavaAnalysisBundle.message("intention.text.remove.annotation"));
single(ANNOTATION_NOT_ALLOWED_CLASS, annotationRemover);
single(ANNOTATION_NOT_ALLOWED_HERE, annotationRemover);
single(ANNOTATION_NOT_ALLOWED_REF, annotationRemover);
single(ANNOTATION_NOT_ALLOWED_VAR, annotationRemover);
single(ANNOTATION_NOT_ALLOWED_VOID, annotationRemover);
single(ANNOTATION_NOT_APPLICABLE, annotationRemover);
single(ANNOTATION_NOT_ALLOWED_VAR, error -> {
PsiAnnotationOwner owner = error.psi().getOwner();
PsiTypeElement type = owner instanceof PsiTypeElement te ? te :
PsiTreeUtil.skipSiblingsForward((PsiModifierList)owner, PsiComment.class, PsiWhiteSpace.class,
PsiTypeParameterList.class) instanceof PsiTypeElement te ? te :
null;
return type != null && type.isInferredType() ? new ReplaceVarWithExplicitTypeFix(type) : null;
});
multi(ANNOTATION_NOT_APPLICABLE, error -> {
if (!BaseIntentionAction.canModify(Objects.requireNonNull(error.psi().resolveAnnotationType()))) return List.of();
return ContainerUtil.map(error.context(),
targetType -> QuickFixFactory.getInstance().createAddAnnotationTargetFix(error.psi(), targetType));
});
single(ANNOTATION_NOT_ALLOWED_STATIC, error -> new MoveAnnotationOnStaticMemberQualifyingTypeFix(error.psi()));
multi(UNSUPPORTED_FEATURE, error -> HighlightUtil.getIncreaseLanguageLevelFixes(error.psi(), error.context()));
single(ANNOTATION_MISSING_ATTRIBUTE, error -> QuickFixFactory.getInstance().createAddMissingRequiredAnnotationParametersFix(
error.psi(), PsiMethod.EMPTY_ARRAY, error.context()));
}
private static <Psi extends PsiElement, Context> void single(@NotNull JavaErrorKind<Psi, Context> kind,
@NotNull JavaFixProvider<? super Psi, ? super Context> fixProvider) {
multi(kind, fixProvider.asMulti());
}
private static <Psi extends PsiElement, Context> void multi(@NotNull JavaErrorKind<Psi, Context> kind,
@NotNull JavaFixesProvider<? super Psi, ? super Context> fixProvider) {
FIXES.computeIfAbsent(kind, k -> new ArrayList<>()).add(fixProvider);
}
static @NotNull List<CommonIntentionAction> getFixes(@NotNull JavaCompilationError<?, ?> error) {
var providers = FIXES.get(error.kind());
if (providers == null) return List.of();
List<CommonIntentionAction> fixes = new ArrayList<>();
for (var provider : providers) {
@SuppressWarnings("unchecked") var fn = (JavaFixesProvider<PsiElement, Object>)provider;
fixes.addAll(fn.provide(error));
}
return fixes;
}
}
@@ -3,6 +3,7 @@ package com.intellij.codeInsight.daemon.impl.quickfix;
import com.intellij.codeInsight.daemon.QuickFixBundle;
import com.intellij.codeInsight.template.impl.TextExpression;
import com.intellij.java.JavaBundle;
import com.intellij.modcommand.*;
import com.intellij.openapi.diagnostic.Logger;
import com.intellij.openapi.util.Pair;
@@ -25,17 +26,13 @@ import java.util.TreeSet;
public final class AddMissingRequiredAnnotationParametersFix extends PsiUpdateModCommandAction<PsiAnnotation> {
private static final Logger LOG = Logger.getInstance(AddMissingRequiredAnnotationParametersFix.class);
private final PsiMethod[] myAnnotationMethods;
private final Collection<String> myMissedElements;
public AddMissingRequiredAnnotationParametersFix(final PsiAnnotation annotation,
final PsiMethod[] annotationMethods,
final Collection<String> missedElements) {
public AddMissingRequiredAnnotationParametersFix(final PsiAnnotation annotation, final Collection<String> missedElements) {
super(annotation);
if (missedElements.isEmpty()) {
throw new IllegalArgumentException("missedElements can't be empty");
}
myAnnotationMethods = annotationMethods;
myMissedElements = missedElements;
}
@@ -55,7 +52,13 @@ public final class AddMissingRequiredAnnotationParametersFix extends PsiUpdateMo
protected void invoke(@NotNull ActionContext context, @NotNull PsiAnnotation annotation, @NotNull ModPsiUpdater updater) {
final PsiNameValuePair[] addedParameters = annotation.getParameterList().getAttributes();
final Object2IntMap<String> annotationsOrderMap = getAnnotationsOrderMap();
PsiClass aClass = annotation.resolveAnnotationType();
if (aClass == null) {
updater.cancel(JavaBundle.message("error.no.annotation.class.found"));
return;
}
PsiMethod[] methods = aClass.getMethods();
final Object2IntMap<String> annotationsOrderMap = getAnnotationsOrderMap(methods);
final SortedSet<Pair<String, PsiAnnotationMemberValue>> newParameters =
new TreeSet<>(Comparator.comparingInt(o -> annotationsOrderMap.getInt(o.getFirst())));
@@ -80,7 +83,7 @@ public final class AddMissingRequiredAnnotationParametersFix extends PsiUpdateMo
}
PsiElementFactory factory = JavaPsiFacade.getElementFactory(context.project());
for (PsiMethod method : myAnnotationMethods) {
for (PsiMethod method : methods) {
if (myMissedElements.contains(method.getName())) {
PsiType type = method.getReturnType();
String defaultValue;
@@ -106,10 +109,10 @@ public final class AddMissingRequiredAnnotationParametersFix extends PsiUpdateMo
}
}
private Object2IntMap<String> getAnnotationsOrderMap() {
private Object2IntMap<String> getAnnotationsOrderMap(PsiMethod[] methods) {
final Object2IntMap<String> map = new Object2IntOpenHashMap<>();
for (int i = 0; i < myAnnotationMethods.length; i++) {
map.put(myAnnotationMethods[i].getName(), i);
for (int i = 0; i < methods.length; i++) {
map.put(methods[i].getName(), i);
}
return map;
}
@@ -747,7 +747,7 @@ public final class QuickFixFactoryImpl extends QuickFixFactory {
public @NotNull IntentionAction createAddMissingRequiredAnnotationParametersFix(final @NotNull PsiAnnotation annotation,
final PsiMethod @NotNull [] annotationMethods,
final @NotNull Collection<String> missedElements) {
return new AddMissingRequiredAnnotationParametersFix(annotation, annotationMethods, missedElements).asIntention();
return new AddMissingRequiredAnnotationParametersFix(annotation, missedElements).asIntention();
}
@Override
@@ -1964,4 +1964,5 @@ advanced.setting.java.show.irrelevant.templates.in.source.roots=Show irrelevant
java.test.use.wall.time=Use Wall Time
java.test.use.live.time=Use Live Time
java.test.overall.time=Overall time: {0}
java.test.sum.time=Sum time: {0}
java.test.sum.time=Sum time: {0}
error.no.annotation.class.found=No annotation class found
+1
View File
@@ -37,6 +37,7 @@
- name: intellij.platform.uast
- name: intellij.platform.uast.ide
- name: intellij.java.uast.ide
- name: intellij.java.codeserver.highlighting
- name: intellij.java.compiler.impl
- name: intellij.java.debugger.impl
- name: intellij.java.debugger.memory.agent
@@ -59,6 +59,7 @@ object JavaPluginLayout {
}
spec.withModules(listOf(
"intellij.java.codeserver.highlighting",
"intellij.java.compiler.impl",
"intellij.java.debugger.impl",
"intellij.java.terminal",