Skip to content
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
/*******************************************************************************
* Copyright (c) 2025 IBM Corporation
*
* 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.
*
* SPDX-License-Identifier: EPL-2.0
*
* Contributors:
* IBM Corporation - initial API and implementation
*******************************************************************************/

package io.openliberty.tools.intellij.lsp4jakarta.lsp4ij;

import com.intellij.openapi.project.Project;
import com.intellij.psi.*;
import com.intellij.psi.search.GlobalSearchScope;

/**
* Utility class for common IntelliJ PSI-based diagnostic logic.
*/
public class DiagnosticsUtils {

/**
* inheritsFrom
* Check if specified superType is present or not in the type hierarchy
*
* @param clazz
* @param fqSuperType
* @return
*/
public static boolean inheritsFrom(PsiClass clazz, String fqSuperType) {
Project project = clazz.getProject();
PsiClass superClass = JavaPsiFacade.getInstance(project)
.findClass(fqSuperType, GlobalSearchScope.allScope(project));
return superClass != null &&
(clazz.isEquivalentTo(superClass) || clazz.isInheritor(superClass, true));
}
}
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*******************************************************************************
* Copyright (c) 2021, 2022 IBM Corporation and others.
* Copyright (c) 2021, 2025 IBM Corporation 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
Expand Down Expand Up @@ -44,4 +44,8 @@ public class AnnotationConstants {
public static final String DIAGNOSTIC_CODE_PREDESTROY_PARAMS = "PreDestroyParams";
public static final String DIAGNOSTIC_CODE_PREDESTROY_EXCEPTION = "PreDestroyException";
public static final String DIAGNOSTIC_CODE_PREDESTROY_STATIC = "PreDestroyStatic";

/* Exceptions */
public static final String EXCEPTION = "java.lang.Exception";
public static final String RUNTIME_EXCEPTION = "java.lang.RuntimeException";
}
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*******************************************************************************
* Copyright (c) 2021, 2024 IBM Corporation and others.
* Copyright (c) 2021, 2025 IBM Corporation 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
Expand All @@ -14,8 +14,8 @@
package io.openliberty.tools.intellij.lsp4jakarta.lsp4ij.annotations;

import com.intellij.psi.*;
import com.intellij.psi.util.PsiUtil;
import io.openliberty.tools.intellij.lsp4jakarta.lsp4ij.AbstractDiagnosticsCollector;
import io.openliberty.tools.intellij.lsp4jakarta.lsp4ij.DiagnosticsUtils;
import io.openliberty.tools.intellij.lsp4jakarta.lsp4ij.Messages;
import org.eclipse.lsp4j.Diagnostic;
import org.eclipse.lsp4j.DiagnosticSeverity;
Expand All @@ -25,6 +25,9 @@
import java.util.List;
import java.util.regex.Pattern;

import static io.openliberty.tools.intellij.lsp4jakarta.lsp4ij.annotations.AnnotationConstants.EXCEPTION;
import static io.openliberty.tools.intellij.lsp4jakarta.lsp4ij.annotations.AnnotationConstants.RUNTIME_EXCEPTION;

/**
*
* jararta.annotation Diagnostics
Expand Down Expand Up @@ -148,6 +151,13 @@ public void collectDiagnostics(PsiJavaFile unit, List<Diagnostic> diagnostics) {
if (isMatchedAnnotation(annotation, AnnotationConstants.POST_CONSTRUCT_FQ_NAME)) {
if (element instanceof PsiMethod) {
PsiMethod method = (PsiMethod) element;
if (isCheckedExceptionPresent(method)) {
String diagnosticMessage = Messages.getMessage("MethodMustNotThrow",
"@PostConstruct");
diagnostics.add(createDiagnostic(method, unit, diagnosticMessage,
AnnotationConstants.DIAGNOSTIC_CODE_POSTCONSTRUCT_EXCEPTION, null,
DiagnosticSeverity.Error));
}
if (method.getParameters().length != 0) {
String diagnosticMessage = Messages.getMessage("MethodMustNotHaveParameters",
"@PostConstruct");
Expand All @@ -163,18 +173,17 @@ public void collectDiagnostics(PsiJavaFile unit, List<Diagnostic> diagnostics) {
AnnotationConstants.DIAGNOSTIC_CODE_POSTCONSTRUCT_RETURN_TYPE, null,
DiagnosticSeverity.Error));
}

if (method.getThrowsTypes().length != 0) {
String diagnosticMessage = Messages.getMessage("MethodMustNotThrow",
"@PostConstruct");
diagnostics.add(createDiagnostic(method, unit, diagnosticMessage,
AnnotationConstants.DIAGNOSTIC_CODE_POSTCONSTRUCT_EXCEPTION, null,
DiagnosticSeverity.Warning));
}
}
} else if (isMatchedAnnotation(annotation, AnnotationConstants.PRE_DESTROY_FQ_NAME)) {
if (element instanceof PsiMethod) {
PsiMethod method = (PsiMethod) element;
if (isCheckedExceptionPresent(method)) {
String diagnosticMessage = Messages.getMessage("MethodMustNotThrow",
"@PreDestroy");
diagnostics.add(createDiagnostic(method, unit, diagnosticMessage,
AnnotationConstants.DIAGNOSTIC_CODE_PREDESTROY_EXCEPTION, null,
DiagnosticSeverity.Error));
}
if (method.getParameters().length != 0) {
String diagnosticMessage = Messages.getMessage("MethodMustNotHaveParameters",
"@PreDestroy");
Expand All @@ -190,14 +199,6 @@ public void collectDiagnostics(PsiJavaFile unit, List<Diagnostic> diagnostics) {
AnnotationConstants.DIAGNOSTIC_CODE_PREDESTROY_STATIC, method.getName(),
DiagnosticSeverity.Error));
}

if (method.getThrowsTypes().length != 0) {
String diagnosticMessage = Messages.getMessage("MethodMustNotThrow",
"@PreDestroy");
diagnostics.add(createDiagnostic(method, unit, diagnosticMessage,
AnnotationConstants.DIAGNOSTIC_CODE_PREDESTROY_EXCEPTION, null,
DiagnosticSeverity.Warning));
}
}
}
}
Expand All @@ -224,4 +225,44 @@ private static boolean isValidAnnotation(String annotationName, String[] validAn
}
return false;
}

/**
* isCheckedExceptionPresent
* This method scans the exception signatures to identify if any checked exceptions are declared.
*
* @param method
* @return
*/
private boolean isCheckedExceptionPresent(PsiMethod method) {
for (PsiClassType type : method.getThrowsList().getReferencedTypes()) {
PsiClass exceptionClass = type.resolve();
if (exceptionClass != null && extendsException(exceptionClass) && notExtendsRuntimeException(exceptionClass)) {
return true;
}
}
return false;
}

/**
* extendsException
*
* @param exceptionClass The root type of which the super-types are checked.
* @return true if Exception is the superType of the given exception type.
*/
private static boolean extendsException(PsiClass exceptionClass) {
return DiagnosticsUtils.inheritsFrom(exceptionClass, EXCEPTION);
}

/**
* notExtendsRuntimeException
*
* @param exceptionClass The root type of which the super-types are checked.
* @return true if RuntimeException is not the superType of the given exception type.
*/
private static boolean notExtendsRuntimeException(PsiClass exceptionClass) {
return !DiagnosticsUtils.inheritsFrom(exceptionClass, RUNTIME_EXCEPTION);
}



}
Loading
Loading