blob: dfba90c44e32c6656605f0fdd978c13d8edcb0b4 [file] [log] [blame]
package org.checkerframework.checker.calledmethods;
import java.util.LinkedHashSet;
import org.checkerframework.checker.nullness.qual.MonotonicNonNull;
import org.checkerframework.common.accumulation.AccumulationChecker;
import org.checkerframework.common.basetype.BaseTypeChecker;
import org.checkerframework.common.returnsreceiver.ReturnsReceiverChecker;
import org.checkerframework.common.value.ValueChecker;
import org.checkerframework.framework.qual.StubFiles;
import org.checkerframework.framework.source.SupportedOptions;
import org.checkerframework.framework.source.SuppressWarningsPrefix;
/**
* The Called Methods Checker tracks the methods that have definitely been called on an object. One
* common use case for the Called Methods Checker is to specify safe combinations of options to
* builder or builder-like interfaces, preventing objects from being instantiated incompletely.
*/
@SuppressWarningsPrefix({
// Preferred checkername.
"calledmethods",
// Deprecated checkernames, supported for backward compatibility.
"builder",
"object.construction",
"objectconstruction"
})
@SupportedOptions({
CalledMethodsChecker.USE_VALUE_CHECKER,
CalledMethodsChecker.COUNT_FRAMEWORK_BUILD_CALLS,
CalledMethodsChecker.DISABLE_BUILDER_FRAMEWORK_SUPPORTS,
CalledMethodsChecker.DISABLE_RETURNS_RECEIVER
})
@StubFiles({"DescribeImages.astub", "GenerateDataKey.astub"})
public class CalledMethodsChecker extends AccumulationChecker {
/**
* If this option is supplied, count the number of analyzed calls to build() in supported builder
* frameworks and print it when analysis is complete. Useful for collecting metrics.
*/
public static final String COUNT_FRAMEWORK_BUILD_CALLS = "countFrameworkBuildCalls";
/**
* This option disables the support for (and therefore the automated checking of) code that uses
* the given builder frameworks. Useful when a user **only** wants to enforce specifications on
* custom builder objects (such as the AWS SDK examples).
*/
public static final String DISABLE_BUILDER_FRAMEWORK_SUPPORTS = "disableBuilderFrameworkSupports";
/**
* If this option is supplied, use the Value Checker to reduce false positives when analyzing
* calls to the AWS SDK.
*/
public static final String USE_VALUE_CHECKER = "useValueChecker";
/**
* Some use cases for the Called Methods Checker do not involve checking fluent APIs, and in those
* cases disabling the Returns Receiver Checker using this flag will make the Called Methods
* Checker run much faster.
*/
public static final String DISABLE_RETURNS_RECEIVER = "disableReturnsReceiver";
/**
* The number of calls to build frameworks supported by this invocation. Incremented only if the
* {@link #COUNT_FRAMEWORK_BUILD_CALLS} option was supplied.
*/
int numBuildCalls = 0;
/** Never access this boolean directly. Call {@link #isReturnsReceiverDisabled()} instead. */
private @MonotonicNonNull Boolean returnsReceiverDisabled = null;
/**
* Was the Returns Receiver Checker disabled on the command line?
*
* @return whether the -AdisableReturnsReceiver option was specified on the command line
*/
private boolean isReturnsReceiverDisabled() {
if (returnsReceiverDisabled == null) {
returnsReceiverDisabled = hasOptionNoSubcheckers(DISABLE_RETURNS_RECEIVER);
}
return returnsReceiverDisabled;
}
@Override
protected LinkedHashSet<Class<? extends BaseTypeChecker>> getImmediateSubcheckerClasses() {
LinkedHashSet<Class<? extends BaseTypeChecker>> checkers =
super.getImmediateSubcheckerClasses();
if (!isReturnsReceiverDisabled()) {
checkers.add(ReturnsReceiverChecker.class);
}
// BaseTypeChecker#hasOption calls this method (so that all subcheckers' options are
// considered), so the processingEnvironment must be checked for options directly.
if (this.processingEnv.getOptions().containsKey(USE_VALUE_CHECKER)
|| this.processingEnv
.getOptions()
.containsKey(this.getClass().getSimpleName() + "_" + USE_VALUE_CHECKER)) {
checkers.add(ValueChecker.class);
}
return checkers;
}
/**
* Check whether the given alias analysis is enabled by this particular accumulation checker.
*
* @param aliasAnalysis the analysis to check
* @return true iff the analysis is enabled
*/
@Override
public boolean isEnabled(AliasAnalysis aliasAnalysis) {
if (aliasAnalysis == AliasAnalysis.RETURNS_RECEIVER) {
return !isReturnsReceiverDisabled();
}
return super.isEnabled(aliasAnalysis);
}
@Override
public void typeProcessingOver() {
if (getBooleanOption(COUNT_FRAMEWORK_BUILD_CALLS)) {
System.out.printf("Found %d build() method calls.%n", numBuildCalls);
}
super.typeProcessingOver();
}
}