SuppressWarningsChecker.java

/*******************************************************************************
 * Copyright (c) 2026 Carsten Hammer.
 *
 * This program and the accompanying materials
 * are made available under the terms of the Eclipse Public License 2.0
 * which accompanies this distribution, and is available at
 * https://www.eclipse.org/legal/epl-2.0/
 *
 * SPDX-License-Identifier: EPL-2.0
 *
 * Contributors:
 *     Carsten Hammer - initial API and implementation
 *******************************************************************************/
package org.sandbox.jdt.triggerpattern.internal;

import java.util.List;

import org.eclipse.jdt.core.dom.ASTNode;
import org.eclipse.jdt.core.dom.Annotation;
import org.eclipse.jdt.core.dom.ArrayInitializer;
import org.eclipse.jdt.core.dom.BodyDeclaration;
import org.eclipse.jdt.core.dom.Expression;
import org.eclipse.jdt.core.dom.MemberValuePair;
import org.eclipse.jdt.core.dom.NormalAnnotation;
import org.eclipse.jdt.core.dom.SingleMemberAnnotation;
import org.eclipse.jdt.core.dom.StringLiteral;

/**
 * Utility that checks if an AST node is suppressed by a {@code @SuppressWarnings}
 * annotation on any enclosing method, field, or type declaration.
 *
 * <p>This walks up the AST from a given node and checks each enclosing
 * {@link BodyDeclaration} for a {@code @SuppressWarnings} annotation
 * whose value contains the specified key.</p>
 *
 * @since 1.4.0
 */
public final class SuppressWarningsChecker {

	private static final String SUPPRESS_WARNINGS = "SuppressWarnings"; //$NON-NLS-1$
	private static final String VALUE = "value"; //$NON-NLS-1$

	private SuppressWarningsChecker() {
		// utility class
	}

	/**
	 * Checks if the given AST node is suppressed by a {@code @SuppressWarnings}
	 * annotation containing the specified key.
	 *
	 * @param node the AST node to check
	 * @param key the suppress warnings key to look for
	 * @return {@code true} if the node is suppressed
	 */
	public static boolean isSuppressed(ASTNode node, String key) {
		if (node == null || key == null || key.isEmpty()) {
			return false;
		}
		ASTNode current = node;
		while (current != null) {
			if (current instanceof BodyDeclaration bodyDecl) {
				if (hasSuppressWarningsKey(bodyDecl, key)) {
					return true;
				}
			}
			current = current.getParent();
		}
		return false;
	}

	/**
	 * Checks if a body declaration has a {@code @SuppressWarnings} annotation
	 * containing the given key.
	 */
	@SuppressWarnings("unchecked")
	private static boolean hasSuppressWarningsKey(BodyDeclaration bodyDecl, String key) {
		for (Object modifier : bodyDecl.modifiers()) {
			if (modifier instanceof Annotation annotation) {
				String annotationName = annotation.getTypeName().getFullyQualifiedName();
				if (SUPPRESS_WARNINGS.equals(annotationName)
						|| "java.lang.SuppressWarnings".equals(annotationName)) { //$NON-NLS-1$
					return containsKey(annotation, key);
				}
			}
		}
		return false;
	}

	/**
	 * Checks if a {@code @SuppressWarnings} annotation contains the given key.
	 */
	private static boolean containsKey(Annotation annotation, String key) {
		if (annotation instanceof SingleMemberAnnotation sma) {
			return expressionContainsKey(sma.getValue(), key);
		}
		if (annotation instanceof NormalAnnotation na) {
			@SuppressWarnings("unchecked")
			List<MemberValuePair> values = na.values();
			for (MemberValuePair pair : values) {
				if (VALUE.equals(pair.getName().getIdentifier())) {
					return expressionContainsKey(pair.getValue(), key);
				}
			}
		}
		return false;
	}

	/**
	 * Checks if an expression (String literal or array of String literals)
	 * contains the given key.
	 */
	private static boolean expressionContainsKey(Expression expr, String key) {
		if (expr instanceof StringLiteral literal) {
			return key.equals(literal.getLiteralValue());
		}
		if (expr instanceof ArrayInitializer arrayInit) {
			@SuppressWarnings("unchecked")
			List<Expression> expressions = arrayInit.expressions();
			for (Expression element : expressions) {
				if (element instanceof StringLiteral literal) {
					if (key.equals(literal.getLiteralValue())) {
						return true;
					}
				}
			}
		}
		return false;
	}
}