diff --git a/src/main/java/com/redhat/devtools/intellij/lsp4mp4ij/psi/core/utils/PositionUtils.java b/src/main/java/com/redhat/devtools/intellij/lsp4mp4ij/psi/core/utils/PositionUtils.java index 8903088a2..c6ab47943 100644 --- a/src/main/java/com/redhat/devtools/intellij/lsp4mp4ij/psi/core/utils/PositionUtils.java +++ b/src/main/java/com/redhat/devtools/intellij/lsp4mp4ij/psi/core/utils/PositionUtils.java @@ -9,6 +9,7 @@ *******************************************************************************/ package com.redhat.devtools.intellij.lsp4mp4ij.psi.core.utils; +import com.intellij.codeInsight.daemon.impl.analysis.HighlightNamesUtil; import com.intellij.openapi.util.TextRange; import com.intellij.psi.PsiClass; import com.intellij.psi.PsiField; @@ -16,6 +17,7 @@ import com.intellij.psi.PsiMethod; import com.redhat.devtools.intellij.lsp4mp4ij.psi.core.utils.IPsiUtils; import org.eclipse.lsp4j.Range; +import org.jetbrains.annotations.NotNull; /** * Position utilities. @@ -58,7 +60,6 @@ public static Range toNameRange(PsiClass type, IPsiUtils utils) { * @param method the java type. * @param utils the JDT utilities. * @return the LSP range for the given method name. - * @throws JavaModelException */ public static Range toNameRange(PsiMethod method, IPsiUtils utils) { PsiFile openable = method.getContainingFile(); @@ -66,4 +67,16 @@ public static Range toNameRange(PsiMethod method, IPsiUtils utils) { return utils.toRange(openable, sourceRange.getStartOffset(), sourceRange.getLength()); } + /** + * Returns the LSP Range for the class declaration of the given type + * + * @param type the java type. + * @param utils the JDT utilities. + * @return the LSP range the class declaration of the given type. + */ + public static Range toClassDeclarationRange(@NotNull PsiClass type, @NotNull IPsiUtils utils) { + PsiFile openable = type.getContainingFile(); + TextRange sourceRange = HighlightNamesUtil.getClassDeclarationTextRange(type); + return utils.toRange(openable, sourceRange.getStartOffset(), sourceRange.getLength()); + } } diff --git a/src/main/java/com/redhat/devtools/intellij/quarkus/QuarkusBundle.java b/src/main/java/com/redhat/devtools/intellij/quarkus/QuarkusBundle.java new file mode 100644 index 000000000..714b7cd3a --- /dev/null +++ b/src/main/java/com/redhat/devtools/intellij/quarkus/QuarkusBundle.java @@ -0,0 +1,45 @@ +/******************************************************************************* + * Copyright (c) 2023 Red Hat Inc. and others. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 + * which is available at https://www.apache.org/licenses/LICENSE-2.0. + * + * SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 + * + * Contributors: + * Red Hat Inc. - initial API and implementation + *******************************************************************************/ +package com.redhat.devtools.intellij.quarkus; + +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; + +/** + * Quarkus messages bundle. + */ +public final class QuarkusBundle extends DynamicBundle { + + @NonNls public static final String BUNDLE = "messages.QuarkusBundle"; + private static final QuarkusBundle INSTANCE = new QuarkusBundle(); + + private QuarkusBundle() { + super(BUNDLE); + } + + @NotNull + public static @Nls String message(@NotNull @PropertyKey(resourceBundle = BUNDLE) String key, Object @NotNull ... params) { + return INSTANCE.getMessage(key, params); + } + + @NotNull + public static Supplier<@Nls String> messagePointer(@NotNull @PropertyKey(resourceBundle = BUNDLE) String key, Object @NotNull ... params) { + return INSTANCE.getLazyMessage(key, params); + } +} \ No newline at end of file diff --git a/src/main/java/com/redhat/devtools/intellij/quarkus/psi/internal/builditems/QuarkusBuildItemErrorCode.java b/src/main/java/com/redhat/devtools/intellij/quarkus/psi/internal/builditems/QuarkusBuildItemErrorCode.java new file mode 100644 index 000000000..a30b2dde3 --- /dev/null +++ b/src/main/java/com/redhat/devtools/intellij/quarkus/psi/internal/builditems/QuarkusBuildItemErrorCode.java @@ -0,0 +1,27 @@ +/******************************************************************************* + * Copyright (c) 2023 Red Hat Inc. and others. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 + * which is available at https://www.apache.org/licenses/LICENSE-2.0. + * + * SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 + * + * Contributors: + * Red Hat Inc. - initial API and implementation + *******************************************************************************/ +package com.redhat.devtools.intellij.quarkus.psi.internal.builditems; + +import com.redhat.devtools.intellij.lsp4mp4ij.psi.core.java.diagnostics.IJavaErrorCode; + +public enum QuarkusBuildItemErrorCode implements IJavaErrorCode { + + InvalidModifierBuildItem; + + @Override + public String getCode() { + return name(); + } +} + diff --git a/src/main/java/com/redhat/devtools/intellij/quarkus/psi/internal/validators/QuarkusBuildItemDiagnosticsParticipant.java b/src/main/java/com/redhat/devtools/intellij/quarkus/psi/internal/validators/QuarkusBuildItemDiagnosticsParticipant.java new file mode 100644 index 000000000..9c306e9e7 --- /dev/null +++ b/src/main/java/com/redhat/devtools/intellij/quarkus/psi/internal/validators/QuarkusBuildItemDiagnosticsParticipant.java @@ -0,0 +1,93 @@ +/******************************************************************************* + * Copyright (c) 2023 Red Hat Inc. and others. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 + * which is available at https://www.apache.org/licenses/LICENSE-2.0. + * + * SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 + * + * Contributors: + * Red Hat Inc. - initial API and implementation + *******************************************************************************/ +package com.redhat.devtools.intellij.quarkus.psi.internal.validators; + +import com.intellij.openapi.module.Module; +import com.intellij.psi.PsiClass; +import com.intellij.psi.PsiElement; +import com.intellij.psi.PsiFile; +import com.intellij.psi.PsiModifier; +import com.intellij.psi.util.InheritanceUtil; +import com.redhat.devtools.intellij.lsp4mp4ij.psi.core.java.diagnostics.IJavaDiagnosticsParticipant; +import com.redhat.devtools.intellij.lsp4mp4ij.psi.core.java.diagnostics.JavaDiagnosticsContext; +import com.redhat.devtools.intellij.lsp4mp4ij.psi.core.utils.PositionUtils; +import com.redhat.devtools.intellij.lsp4mp4ij.psi.core.utils.PsiTypeUtils; +import com.redhat.devtools.intellij.quarkus.QuarkusBundle; +import com.redhat.devtools.intellij.quarkus.psi.internal.builditems.QuarkusBuildItemErrorCode; +import org.eclipse.lsp4j.Diagnostic; +import org.eclipse.lsp4j.DiagnosticSeverity; +import org.eclipse.lsp4j.Range; +import org.eclipse.lsp4mp.commons.DocumentFormat; + +import java.util.ArrayList; +import java.util.List; + +public class QuarkusBuildItemDiagnosticsParticipant implements IJavaDiagnosticsParticipant { + + private static final String BUILD_ITEM= "io.quarkus.builder.item.BuildItem"; + + @Override + public boolean isAdaptedForDiagnostics(JavaDiagnosticsContext context) { + // Collection of diagnostics for Quarkus Build Items is done only if + // io.quarkus.builder.item.BuildItem is on the classpath + Module javaProject = context.getJavaProject(); + return PsiTypeUtils.findType(javaProject, BUILD_ITEM) != null; + } + + @Override + public List collectDiagnostics(JavaDiagnosticsContext context) { + PsiFile typeRoot = context.getTypeRoot(); + PsiElement[] elements = typeRoot.getChildren(); + List diagnostics = new ArrayList<>(); + collectDiagnostics(elements, diagnostics, context); + return diagnostics; + } + + private static void collectDiagnostics(PsiElement[] elements, List diagnostics, + JavaDiagnosticsContext context) { + for (PsiElement element : elements) { + if (element instanceof PsiClass) { + PsiClass psiClass = (PsiClass) element; + if (isBuildItem(psiClass)) { + validateBuildItem(psiClass, diagnostics, context); + } + } + } + } + + private static boolean isBuildItem(PsiClass type) { + return InheritanceUtil.isInheritor(type, BUILD_ITEM); + } + + private static void validateBuildItem(PsiClass psiClass, List diagnostics, JavaDiagnosticsContext context) { + if (psiClass.hasModifierProperty(PsiModifier.FINAL) + || psiClass.hasModifierProperty(PsiModifier.ABSTRACT) + ) { + return; + } + Range range = PositionUtils.toClassDeclarationRange(psiClass, context.getUtils()); + Diagnostic d = context.createDiagnostic(context.getUri(), + createDiagnosticMessage(psiClass, context.getDocumentFormat()), + range, "quarkus", + QuarkusBuildItemErrorCode.InvalidModifierBuildItem, + DiagnosticSeverity.Error + ); + diagnostics.add(d); + } + + private static String createDiagnosticMessage(PsiClass classType, DocumentFormat documentFormat) { + String quote = DocumentFormat.Markdown.equals(documentFormat)?"`":"'"; + return QuarkusBundle.message("quarkus.validation.buildItem.invalid", classType.getQualifiedName(), quote); + } +} diff --git a/src/main/resources/META-INF/plugin.xml b/src/main/resources/META-INF/plugin.xml index aefda9165..cc3157114 100644 --- a/src/main/resources/META-INF/plugin.xml +++ b/src/main/resources/META-INF/plugin.xml @@ -417,6 +417,7 @@ + diff --git a/src/main/resources/messages/QuarkusBundle.properties b/src/main/resources/messages/QuarkusBundle.properties new file mode 100644 index 000000000..6d5ccb6ac --- /dev/null +++ b/src/main/resources/messages/QuarkusBundle.properties @@ -0,0 +1,14 @@ +############################################################################### +# Copyright (c) 2023 Red Hat Inc. and others. +# All rights reserved. This program and the accompanying materials +# are made available under the terms of the Eclipse Public License v2.0 +# which accompanies this distribution, and is available at +# http://www.eclipse.org/legal/epl-v20.html +# +# SPDX-License-Identifier: EPL-2.0 +# +# Contributors: +# Red Hat Inc. - initial API and implementation +############################################################################### + +quarkus.validation.buildItem.invalid=BuildItem class {1}{0}{1} must either be declared final or abstract