Skip to content

Commit

Permalink
feat: validate BuildItem classes
Browse files Browse the repository at this point in the history
Signed-off-by: Fred Bricon <[email protected]>
  • Loading branch information
fbricon authored and angelozerr committed Sep 18, 2023
1 parent db1ab9b commit 9c95979
Show file tree
Hide file tree
Showing 11 changed files with 380 additions and 1 deletion.
43 changes: 43 additions & 0 deletions projects/quarkus/projects/maven/quarkus-builditems/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
#Maven
target/
pom.xml.tag
pom.xml.releaseBackup
pom.xml.versionsBackup
release.properties
.flattened-pom.xml

# Eclipse
.project
.classpath
.settings/
bin/

# IntelliJ
.idea
*.ipr
*.iml
*.iws

# NetBeans
nb-configuration.xml

# Visual Studio Code
.vscode
.factorypath

# OSX
.DS_Store

# Vim
*.swp
*.swo

# patch
*.orig
*.rej

# Local environment
.env

# Plugin directory
/.quarkus/cli/plugins/
116 changes: 116 additions & 0 deletions projects/quarkus/projects/maven/quarkus-builditems/pom.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,116 @@
<?xml version="1.0"?>
<project xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd" xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<modelVersion>4.0.0</modelVersion>
<groupId>org.acme</groupId>
<artifactId>quarkus-builditems</artifactId>
<version>1.0.0-SNAPSHOT</version>
<properties>
<compiler-plugin.version>3.11.0</compiler-plugin.version>
<maven.compiler.release>17</maven.compiler.release>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
<quarkus.platform.artifact-id>quarkus-bom</quarkus.platform.artifact-id>
<quarkus.platform.group-id>io.quarkus.platform</quarkus.platform.group-id>
<quarkus.platform.version>3.3.1</quarkus.platform.version>
<skipITs>true</skipITs>
<surefire-plugin.version>3.1.2</surefire-plugin.version>
</properties>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>${quarkus.platform.group-id}</groupId>
<artifactId>${quarkus.platform.artifact-id}</artifactId>
<version>${quarkus.platform.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
<dependencies>
<dependency>
<groupId>io.quarkus</groupId>
<artifactId>quarkus-core-deployment</artifactId>
</dependency>
<dependency>
<groupId>io.quarkus</groupId>
<artifactId>quarkus-arc</artifactId>
</dependency>
<dependency>
<groupId>io.quarkus</groupId>
<artifactId>quarkus-junit5</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>${quarkus.platform.group-id}</groupId>
<artifactId>quarkus-maven-plugin</artifactId>
<version>${quarkus.platform.version}</version>
<extensions>true</extensions>
<executions>
<execution>
<goals>
<goal>build</goal>
<goal>generate-code</goal>
<goal>generate-code-tests</goal>
</goals>
</execution>
</executions>
</plugin>
<plugin>
<artifactId>maven-compiler-plugin</artifactId>
<version>${compiler-plugin.version}</version>
<configuration>
<compilerArgs>
<arg>-parameters</arg>
</compilerArgs>
</configuration>
</plugin>
<plugin>
<artifactId>maven-surefire-plugin</artifactId>
<version>${surefire-plugin.version}</version>
<configuration>
<systemPropertyVariables>
<java.util.logging.manager>org.jboss.logmanager.LogManager</java.util.logging.manager>
<maven.home>${maven.home}</maven.home>
</systemPropertyVariables>
</configuration>
</plugin>
<plugin>
<artifactId>maven-failsafe-plugin</artifactId>
<version>${surefire-plugin.version}</version>
<executions>
<execution>
<goals>
<goal>integration-test</goal>
<goal>verify</goal>
</goals>
<configuration>
<systemPropertyVariables>
<native.image.path>${project.build.directory}/${project.build.finalName}-runner</native.image.path>
<java.util.logging.manager>org.jboss.logmanager.LogManager</java.util.logging.manager>
<maven.home>${maven.home}</maven.home>
</systemPropertyVariables>
</configuration>
</execution>
</executions>
</plugin>
</plugins>
</build>
<profiles>
<profile>
<id>native</id>
<activation>
<property>
<name>native</name>
</property>
</activation>
<properties>
<skipITs>false</skipITs>
<quarkus.package.type>native</quarkus.package.type>
</properties>
</profile>
</profiles>
</project>
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
package org.acme.builditems;

import io.quarkus.builder.item.MultiBuildItem;

public class BadBuildItem extends MultiBuildItem {
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
package org.acme.builditems;

import io.quarkus.builder.item.SimpleBuildItem;

public final class GoodBuildItem extends SimpleBuildItem {
}
Original file line number Diff line number Diff line change
Expand Up @@ -9,13 +9,15 @@
*******************************************************************************/
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;
import com.intellij.psi.PsiFile;
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.
Expand Down Expand Up @@ -58,12 +60,23 @@ 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();
TextRange sourceRange = method.getNameIdentifier().getTextRange();
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());
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,8 @@ public class QuarkusConstants {
public static final String CONFIG_GROUP_ANNOTATION = "io.quarkus.runtime.annotations.ConfigGroup";
public static final String CONFIG_ITEM_ANNOTATION = "io.quarkus.runtime.annotations.ConfigItem";
public static final String CONFIG_PROPERTIES_ANNOTATION = "io.quarkus.arc.config.ConfigProperties";
public static final String QUARKUS_DIAGNOSTIC_SOURCE = "quarkus";


/**
* As ConfigProperties is not part anymore of Quarkus since 3.1.2.Final,
Expand Down Expand Up @@ -116,5 +118,6 @@ public class QuarkusConstants {
public static final String QUARKUS_CODE_URL = System.getProperty(QUARKUS_CODE_URL_PROPERTY_NAME, QUARKUS_CODE_URL_PRODUCTION);

public static final String QUARKUS_RUNTIME_CLASS_NAME = "io.quarkus.runtime.LaunchMode";
public static final String QUARKUS_BUILD_ITEM_CLASS_NAME = "io.quarkus.builder.item.BuildItem";
public static final String QUARKUS_RUN_CONTEXT_KEY = QuarkusConstants.class.getName() + ".quarkusContext";
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
/*******************************************************************************
* 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;

/**
* Represents error codes for validation issues in classes inheriting <code>io.quarkus.builder.item.BuildItem</code>.
*/
public enum QuarkusBuildItemErrorCode implements IJavaErrorCode {

InvalidModifierBuildItem;

@Override
public String getCode() {
return name();
}
}

Original file line number Diff line number Diff line change
@@ -0,0 +1,99 @@
/*******************************************************************************
* 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.QuarkusConstants;
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;

/**
* Validates <code>io.quarkus.builder.item.BuildItem</code> subclasses.
* <ul>
* <li>checks if the BuildItem is final or abstract</li>
* </ul>
*/
public class QuarkusBuildItemDiagnosticsParticipant implements IJavaDiagnosticsParticipant {

private static final String INVALID_MODIFIER = "BuildItem class %2$s%1$s%2$s must either be declared final or abstract";

@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, QuarkusConstants.QUARKUS_BUILD_ITEM_CLASS_NAME) != null;
}

@Override
public List<Diagnostic> collectDiagnostics(JavaDiagnosticsContext context) {
PsiFile typeRoot = context.getTypeRoot();
PsiElement[] elements = typeRoot.getChildren();
List<Diagnostic> diagnostics = new ArrayList<>();
collectDiagnostics(elements, diagnostics, context);
return diagnostics;
}

private static void collectDiagnostics(PsiElement[] elements, List<Diagnostic> 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, QuarkusConstants.QUARKUS_BUILD_ITEM_CLASS_NAME);
}

private static void validateBuildItem(PsiClass psiClass, List<Diagnostic> 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, QuarkusConstants.QUARKUS_DIAGNOSTIC_SOURCE,
QuarkusBuildItemErrorCode.InvalidModifierBuildItem,
DiagnosticSeverity.Error
);
diagnostics.add(d);
}

private static String createDiagnosticMessage(PsiClass classType, DocumentFormat documentFormat) {
String quote = DocumentFormat.Markdown.equals(documentFormat)?"`":"'";
return String.format(INVALID_MODIFIER, classType.getQualifiedName(), quote);
}
}
1 change: 1 addition & 0 deletions src/main/resources/META-INF/plugin.xml
Original file line number Diff line number Diff line change
Expand Up @@ -417,6 +417,7 @@
<javaDiagnosticsParticipant implementation="com.redhat.devtools.intellij.lsp4mp4ij.psi.internal.health.java.MicroProfileHealthDiagnosticsParticipant"/>
<javaDiagnosticsParticipant implementation="com.redhat.devtools.intellij.lsp4mp4ij.psi.internal.restclient.java.MicroProfileRestClientDiagnosticsParticipant"/>
<javaDiagnosticsParticipant implementation="com.redhat.devtools.intellij.lsp4mp4ij.psi.internal.metrics.java.MicroProfileMetricsDiagnosticsParticipant"/>
<javaDiagnosticsParticipant implementation="com.redhat.devtools.intellij.quarkus.psi.internal.validators.QuarkusBuildItemDiagnosticsParticipant"/>

<projectLabelProvider implementation="com.redhat.devtools.intellij.lsp4mp4ij.psi.internal.core.providers.MicroProfileProjectLabelProvider"/>

Expand Down
Loading

0 comments on commit 9c95979

Please sign in to comment.