All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.checkerframework.framework.source.AggregateChecker Maven / Gradle / Ivy

package org.checkerframework.framework.source;

import com.sun.source.util.TreePath;
import com.sun.tools.javac.processing.JavacProcessingEnvironment;
import com.sun.tools.javac.util.Context;
import com.sun.tools.javac.util.Log;

import org.checkerframework.checker.nullness.qual.MonotonicNonNull;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;

import javax.annotation.processing.ProcessingEnvironment;
import javax.lang.model.element.TypeElement;
import javax.tools.Diagnostic;

/**
 * An aggregate checker that packages multiple checkers together. The resulting checker invokes the
 * component checkers in turn on the processed files.
 *
 * 

There is no communication, interaction, or cooperation between the component checkers, even to * the extent of being able to read one another's qualifiers. An aggregate checker is merely * shorthand to invoke a sequence of checkers. * *

This class delegates {@code AbstractTypeProcessor} responsibilities to each component checker. * *

Checker writers need to subclass this class and only override {@link #getSupportedCheckers()} * to indicate the classes of the checkers to be bundled. */ public abstract class AggregateChecker extends SourceChecker { protected final List checkers; /** * Returns the list of supported checkers to be run together. Subclasses need to override this * method. * * @return the list of checkers to be run */ protected abstract Collection> getSupportedCheckers(); /** Supported options for this checker. */ private @MonotonicNonNull Set supportedOptions = null; /** Options passed to this checker. */ private @MonotonicNonNull Map options = null; /** Create a new AggregateChecker. */ protected AggregateChecker() { Collection> checkerClasses = getSupportedCheckers(); checkers = new ArrayList<>(checkerClasses.size()); for (Class checkerClass : checkerClasses) { try { SourceChecker instance = checkerClass.getDeclaredConstructor().newInstance(); instance.setParentChecker(this); checkers.add(instance); } catch (Exception e) { message( Diagnostic.Kind.ERROR, "Couldn't instantiate an instance of " + checkerClass); } } } /** * {@code processingEnv} needs to be set on each checker since we are not calling init on the * checker, which leaves it null. If one of checkers is an AggregateChecker, its visitors will * try use checker's processing env which should not be null. */ @Override protected void setProcessingEnvironment(ProcessingEnvironment env) { super.setProcessingEnvironment(env); for (SourceChecker checker : checkers) { checker.setProcessingEnvironment(env); } } @Override public void initChecker() { // No need to call super, it might result in reflective instantiations // of visitor/factory classes. // super.initChecker(); // To prevent the warning that initChecker wasn't called. messager = processingEnv.getMessager(); // first initialize all checkers for (SourceChecker checker : checkers) { checker.initChecker(); } // then share options as necessary for (SourceChecker checker : checkers) { // We need to add all options that are activated for the aggregate to // the individual checkers. checker.addOptions(super.getOptions()); // Each checker should "support" all possible lint options - otherwise // subchecker A would complain about a lint option for subchecker B. checker.setSupportedLintOptions(this.getSupportedLintOptions()); } allCheckersInited = true; } // Whether all checkers were successfully initialized. private boolean allCheckersInited = false; // AbstractTypeProcessor delegation @Override public final void typeProcess(TypeElement element, TreePath tree) { Context context = ((JavacProcessingEnvironment) processingEnv).getContext(); Log log = Log.instance(context); if (log.nerrors > this.errsOnLastExit) { // If there is a Java error, do not perform any of the component type checks, but come // back for the next compilation unit. this.errsOnLastExit = log.nerrors; return; } if (!allCheckersInited) { // If there was an initialization problem, an // error was already output. Just quit. return; } for (SourceChecker checker : checkers) { checker.errsOnLastExit = this.errsOnLastExit; checker.typeProcess(element, tree); if (checker.javacErrored) { this.javacErrored = true; return; } this.errsOnLastExit = checker.errsOnLastExit; } } @Override public void typeProcessingOver() { for (SourceChecker checker : checkers) { checker.typeProcessingOver(); } super.typeProcessingOver(); } @Override public final Set getSupportedOptions() { if (this.supportedOptions == null) { Set options = new HashSet<>(); for (SourceChecker checker : checkers) { options.addAll(checker.getSupportedOptions()); } options.addAll( expandCFOptions( Arrays.asList(this.getClass()), options.toArray(new String[options.size()]))); this.supportedOptions = options; } return this.supportedOptions; } @Override public final Map getOptions() { if (this.options == null) { Map options = new HashMap<>(super.getOptions()); for (SourceChecker checker : checkers) { options.putAll(checker.getOptions()); } this.options = Collections.unmodifiableMap(options); } return this.options; } @Override public final Set getSupportedLintOptions() { Set lints = new HashSet<>(); for (SourceChecker checker : checkers) { lints.addAll(checker.getSupportedLintOptions()); } return lints; } @Override protected SourceVisitor createSourceVisitor() { return new SourceVisitor(this) { // Aggregate checkers do not visit source, // the checkers in the aggregate checker do. }; } // TODO some methods in a component checker should behave differently if they // are part of an aggregate, e.g. getSuppressWarningKeys should additionally // return the name of the aggregate checker. // We could add a query method in SourceChecker that refers to the aggregate, if present. // At the moment, all the component checkers manually need to add the name of the aggregate. }





© 2015 - 2025 Weber Informatics LLC | Privacy Policy