summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--include/clang/Basic/DiagnosticCommonKinds.td2
-rw-r--r--include/clang/StaticAnalyzer/Checkers/CheckerBase.td53
-rw-r--r--include/clang/StaticAnalyzer/Checkers/Checkers.td225
-rw-r--r--include/clang/StaticAnalyzer/Frontend/CheckerRegistry.h83
-rw-r--r--lib/Frontend/CompilerInvocation.cpp2
-rw-r--r--lib/StaticAnalyzer/Frontend/CheckerRegistry.cpp98
-rw-r--r--test/Analysis/disable-all-checks.c2
-rw-r--r--test/Analysis/invalid-checker-option.c19
-rw-r--r--utils/TableGen/ClangSACheckersEmitter.cpp107
9 files changed, 555 insertions, 36 deletions
diff --git a/include/clang/Basic/DiagnosticCommonKinds.td b/include/clang/Basic/DiagnosticCommonKinds.td
index de2cb85671..7cd3b21109 100644
--- a/include/clang/Basic/DiagnosticCommonKinds.td
+++ b/include/clang/Basic/DiagnosticCommonKinds.td
@@ -292,7 +292,7 @@ def err_omp_more_one_clause : Error<
// Static Analyzer Core
def err_unknown_analyzer_checker : Error<
- "no analyzer checkers are associated with '%0'">;
+ "no analyzer checkers or packages are associated with '%0'">;
def note_suggest_disabling_all_checkers : Note<
"use -analyzer-disable-all-checks to disable all static analyzer checkers">;
}
diff --git a/include/clang/StaticAnalyzer/Checkers/CheckerBase.td b/include/clang/StaticAnalyzer/Checkers/CheckerBase.td
index aaa1b5560f..0b0f92ea59 100644
--- a/include/clang/StaticAnalyzer/Checkers/CheckerBase.td
+++ b/include/clang/StaticAnalyzer/Checkers/CheckerBase.td
@@ -10,14 +10,45 @@
//
//===----------------------------------------------------------------------===//
+/// Describes a checker or package option type. This is important for validating
+/// user supplied inputs.
+/// New option types can be added by modifying this enum. Note that this
+/// requires changes in the TableGen emitter file ClangSACheckersEmitter.cpp.
+class CmdLineOptionTypeEnum<bits<2> val> {
+ bits<2> Type = val;
+}
+def Integer : CmdLineOptionTypeEnum<0>;
+def String : CmdLineOptionTypeEnum<1>;
+def Boolean : CmdLineOptionTypeEnum<2>;
+
+class Type<CmdLineOptionTypeEnum val> {
+ bits<2> Type = val.Type;
+}
+
+/// Describes an option for a checker or a package.
+class CmdLineOption<CmdLineOptionTypeEnum type, string cmdFlag, string desc,
+ string defaultVal> {
+ bits<2> Type = type.Type;
+ string CmdFlag = cmdFlag;
+ string Desc = desc;
+ string DefaultVal = defaultVal;
+}
+
+/// Describes a list of package options.
+class PackageOptions<list<CmdLineOption> opts> {
+ list<CmdLineOption> PackageOptions = opts;
+}
+
/// Describes a package. Every checker is a part of a package, for example,
/// 'NullDereference' is part of the 'core' package, hence it's full name is
/// 'core.NullDereference'.
/// Example:
/// def Core : Package<"core">;
class Package<string name> {
- string PackageName = name;
- Package ParentPackage;
+ string PackageName = name;
+ // This field is optional.
+ list<CmdLineOption> PackageOptions;
+ Package ParentPackage;
}
/// Describes a 'super' package that holds another package inside it. This is
@@ -52,11 +83,19 @@ class Documentation<DocumentationEnum val> {
/// def DereferenceChecker : Checker<"NullDereference">,
/// HelpText<"Check for dereferences of null pointers">;
class Checker<string name = ""> {
- string CheckerName = name;
- string HelpText;
- list<Checker> Dependencies;
- bits<2> Documentation;
- Package ParentPackage;
+ string CheckerName = name;
+ string HelpText;
+ // This field is optional.
+ list<CmdLineOption> CheckerOptions;
+ // This field is optional.
+ list<Checker> Dependencies;
+ bits<2> Documentation;
+ Package ParentPackage;
+}
+
+/// Describes a list of checker options.
+class CheckerOptions<list<CmdLineOption> opts> {
+ list<CmdLineOption> CheckerOptions = opts;
}
/// Describes dependencies in between checkers. For example, InnerPointerChecker
diff --git a/include/clang/StaticAnalyzer/Checkers/Checkers.td b/include/clang/StaticAnalyzer/Checkers/Checkers.td
index af5edae1ca..3b2fd11af0 100644
--- a/include/clang/StaticAnalyzer/Checkers/Checkers.td
+++ b/include/clang/StaticAnalyzer/Checkers/Checkers.td
@@ -42,7 +42,17 @@ def OptIn : Package<"optin">;
// development, but unwanted for developers who target only a single platform.
def PortabilityOptIn : Package<"portability">, ParentPackage<OptIn>;
-def Nullability : Package<"nullability">;
+def Nullability : Package<"nullability">,
+ PackageOptions<[
+ CmdLineOption<Boolean,
+ "NoDiagnoseCallsToSystemHeaders",
+ "Suppresses warnings for violating nullability annotations "
+ "of system header functions. This is useful if you are "
+ "concerned with your custom nullability annotations more "
+ "than with following nullability specifications of system "
+ "header functions.",
+ "false">
+ ]>;
def Cplusplus : Package<"cplusplus">;
def CplusplusAlpha : Package<"cplusplus">, ParentPackage<Alpha>;
@@ -371,6 +381,14 @@ def DynamicMemoryModeling: Checker<"DynamicMemoryModeling">,
HelpText<"The base of several malloc() related checkers. On it's own it "
"emits no reports, but adds valuable information to the analysis "
"when enabled.">,
+ CheckerOptions<[
+ CmdLineOption<Boolean,
+ "Optimistic",
+ "If set to true, the checker assumes that all the "
+ "allocating and deallocating functions are annotated with "
+ "ownership_holds, ownership_takes and ownership_returns.",
+ "false">
+ ]>,
Dependencies<[CStringModeling]>,
Documentation<NotDocumented>;
@@ -447,7 +465,28 @@ def CXXSelfAssignmentChecker : Checker<"SelfAssignment">,
Documentation<NotDocumented>;
def MoveChecker: Checker<"Move">,
- HelpText<"Find use-after-move bugs in C++">,
+ HelpText<"Find use-after-move bugs in C++">,
+ CheckerOptions<[
+ CmdLineOption<String,
+ "WarnOn",
+ "In non-aggressive mode, only warn on use-after-move of "
+ "local variables (or local rvalue references) and of STL "
+ "objects. The former is possible because local variables (or "
+ "local rvalue references) are not tempting their user to "
+ "re-use the storage. The latter is possible because STL "
+ "objects are known to end up in a valid but unspecified "
+ "state after the move and their state-reset methods are also "
+ "known, which allows us to predict precisely when "
+ "use-after-move is invalid. Some STL objects are known to "
+ "conform to additional contracts after move, so they are not "
+ "tracked. However, smart pointers specifically are tracked "
+ "because we can perform extra checking over them. In "
+ "aggressive mode, warn on any use-after-move because the "
+ "user has intentionally asked us to completely eliminate "
+ "use-after-move in his code. Values: \"KnownsOnly\", "
+ "\"KnownsAndLocals\", \"All\".",
+ "KnownsAndLocals">
+ ]>,
Documentation<HasDocumentation>;
} // end: "cplusplus"
@@ -456,6 +495,12 @@ let ParentPackage = CplusplusOptIn in {
def VirtualCallChecker : Checker<"VirtualCall">,
HelpText<"Check virtual function calls during construction or destruction">,
+ CheckerOptions<[
+ CmdLineOption<Boolean,
+ "PureOnly",
+ "Whether to only report calls to pure virtual methods.",
+ "false">
+ ]>,
Documentation<HasDocumentation>;
} // end: "optin.cplusplus"
@@ -492,7 +537,40 @@ def MismatchedIteratorChecker : Checker<"MismatchedIterator">,
Documentation<HasAlphaDocumentation>;
def UninitializedObjectChecker: Checker<"UninitializedObject">,
- HelpText<"Reports uninitialized fields after object construction">,
+ HelpText<"Reports uninitialized fields after object construction">,
+ CheckerOptions<[
+ CmdLineOption<Boolean,
+ "Pedantic",
+ "If set to false, the checker won't emit warnings "
+ "for objects that don't have at least one initialized "
+ "field.",
+ "false">,
+ CmdLineOption<Boolean,
+ "NotesAsWarnings",
+ "If set to true, the checker will emit a warning "
+ "for each uninitalized field, as opposed to emitting one "
+ "warning per constructor call, and listing the uninitialized "
+ "fields that belongs to it in notes.",
+ "false">,
+ CmdLineOption<Boolean,
+ "CheckPointeeInitialization",
+ "If set to false, the checker will not analyze "
+ "the pointee of pointer/reference fields, and will only "
+ "check whether the object itself is initialized.",
+ "false">,
+ CmdLineOption<String,
+ "IgnoreRecordsWithField",
+ "If supplied, the checker will not analyze "
+ "structures that have a field with a name or type name that "
+ "matches the given pattern.",
+ "\"\"">,
+ CmdLineOption<Boolean,
+ "IgnoreGuardedFields",
+ "If set to true, the checker will analyze _syntactically_ "
+ "whether the found uninitialized object is used without a "
+ "preceding assert call. Defaults to false.",
+ "false">
+ ]>,
Documentation<HasAlphaDocumentation>;
} // end: "alpha.cplusplus"
@@ -554,6 +632,13 @@ let ParentPackage = Performance in {
def PaddingChecker : Checker<"Padding">,
HelpText<"Check for excessively padded structs.">,
+ CheckerOptions<[
+ CmdLineOption<Integer,
+ "AllowedPad",
+ "Reports are only generated if the excessive padding exceeds "
+ "'AllowedPad' in bytes.",
+ "24">
+ ]>,
Documentation<NotDocumented>;
} // end: "padding"
@@ -662,11 +747,18 @@ def MallocOverflowSecurityChecker : Checker<"MallocOverflow">,
HelpText<"Check for overflows in the arguments to malloc()">,
Documentation<HasAlphaDocumentation>;
-// Operating systems specific PROT_READ/PROT_WRITE values is not implemented,
-// the defaults are correct for several common operating systems though,
-// but may need to be overridden via the related analyzer-config flags.
def MmapWriteExecChecker : Checker<"MmapWriteExec">,
HelpText<"Warn on mmap() calls that are both writable and executable">,
+ CheckerOptions<[
+ CmdLineOption<Integer,
+ "MmapProtExec",
+ "Specifies the value of PROT_EXEC",
+ "0x04">,
+ CmdLineOption<Integer,
+ "MmapProtRead",
+ "Specifies the value of PROT_READ",
+ "0x01">
+ ]>,
Documentation<HasAlphaDocumentation>;
} // end "alpha.security"
@@ -704,6 +796,14 @@ def NSOrCFErrorDerefChecker : Checker<"NSOrCFErrorDerefChecker">,
def NumberObjectConversionChecker : Checker<"NumberObjectConversion">,
HelpText<"Check for erroneous conversions of objects representing numbers "
"into numbers">,
+ CheckerOptions<[
+ CmdLineOption<Boolean,
+ "Pedantic",
+ "Enables detection of more conversion patterns (which are "
+ "most likely more harmless, and therefore are more likely to "
+ "produce false positives).",
+ "false">
+ ]>,
Documentation<NotDocumented>;
def MacOSXAPIChecker : Checker<"API">,
@@ -795,6 +895,23 @@ def NSErrorChecker : Checker<"NSError">,
def RetainCountChecker : Checker<"RetainCount">,
HelpText<"Check for leaks and improper reference count management">,
+ CheckerOptions<[
+ CmdLineOption<Boolean,
+ "CheckOSObject",
+ "Find violations of retain-release rules applied to XNU "
+ "OSObject instances. By default, the checker only checks "
+ "retain-release rules for Objective-C NSObject instances "
+ "and CoreFoundation objects.",
+ "true">,
+ CmdLineOption<Boolean,
+ "TrackNSCFStartParam",
+ "Check not only that the code follows retain-release rules "
+ "with respect to objects it allocates or borrows from "
+ "elsewhere, but also that it fulfills its own retain count "
+ "specification with respect to objects that it receives as "
+ "arguments.",
+ "false">
+ ]>,
Dependencies<[RetainCountBase]>,
Documentation<HasDocumentation>;
@@ -903,6 +1020,17 @@ let ParentPackage = LocalizabilityOptIn in {
def NonLocalizedStringChecker : Checker<"NonLocalizedStringChecker">,
HelpText<"Warns about uses of non-localized NSStrings passed to UI methods "
"expecting localized NSStrings">,
+ CheckerOptions<[
+ CmdLineOption<Boolean,
+ "AggressiveReport",
+ "Marks a string being returned by any call as localized if "
+ "it is in LocStringFunctions (LSF) or the function is "
+ "annotated. Otherwise, we mark it as NonLocalized "
+ "(Aggressive) or NonLocalized only if it is not backed by a "
+ "SymRegion (Non-Aggressive), basically leaving only string "
+ "literals as NonLocalized.",
+ "false">
+ ]>,
Documentation<HasDocumentation>;
def EmptyLocalizationContextChecker :
@@ -961,6 +1089,72 @@ let ParentPackage = Debug in {
def AnalysisOrderChecker : Checker<"AnalysisOrder">,
HelpText<"Print callbacks that are called during analysis in order">,
+ CheckerOptions<[
+ CmdLineOption<Boolean,
+ "PreStmtCastExpr",
+ "",
+ "false">,
+ CmdLineOption<Boolean,
+ "PostStmtCastExpr",
+ "",
+ "false">,
+ CmdLineOption<Boolean,
+ "PreStmtArraySubscriptExpr",
+ "",
+ "false">,
+ CmdLineOption<Boolean,
+ "PostStmtArraySubscriptExpr",
+ "",
+ "false">,
+ CmdLineOption<Boolean,
+ "PreStmtCXXNewExpr",
+ "",
+ "false">,
+ CmdLineOption<Boolean,
+ "PostStmtCXXNewExpr",
+ "",
+ "false">,
+ CmdLineOption<Boolean,
+ "PreStmtOffsetOfExpr",
+ "",
+ "false">,
+ CmdLineOption<Boolean,
+ "PostStmtOffsetOfExpr",
+ "",
+ "false">,
+ CmdLineOption<Boolean,
+ "PreCall",
+ "",
+ "false">,
+ CmdLineOption<Boolean,
+ "PostCall",
+ "",
+ "false">,
+ CmdLineOption<Boolean,
+ "EndFunction",
+ "",
+ "false">,
+ CmdLineOption<Boolean,
+ "NewAllocator",
+ "",
+ "false">,
+ CmdLineOption<Boolean,
+ "Bind",
+ "",
+ "false">,
+ CmdLineOption<Boolean,
+ "LiveSymbols",
+ "",
+ "false">,
+ CmdLineOption<Boolean,
+ "RegionChanges",
+ "",
+ "false">,
+ CmdLineOption<Boolean,
+ "*",
+ "Enables all callbacks.",
+ "false">
+ ]>,
Documentation<NotDocumented>;
def DominatorsTreeDumper : Checker<"DumpDominators">,
@@ -1034,6 +1228,25 @@ let ParentPackage = CloneDetectionAlpha in {
def CloneChecker : Checker<"CloneChecker">,
HelpText<"Reports similar pieces of code.">,
+ CheckerOptions<[
+ CmdLineOption<Integer,
+ "MinimumCloneComplexity",
+ "Ensures that every clone has at least the given complexity. "
+ "Complexity is here defined as the total amount of children "
+ "of a statement. This constraint assumes the first statement "
+ "in the group is representative for all other statements in "
+ "the group in terms of complexity.",
+ "50">,
+ CmdLineOption<Boolean,
+ "ReportNormalClones",
+ "Report all clones, even less suspicious ones.",
+ "true">,
+ CmdLineOption<String,
+ "IgnoredFilesPattern",
+ "If supplied, the checker wont analyze files with a filename "
+ "that matches the given pattern.",
+ "\"\"">
+ ]>,
Documentation<HasAlphaDocumentation>;
} // end "clone"
diff --git a/include/clang/StaticAnalyzer/Frontend/CheckerRegistry.h b/include/clang/StaticAnalyzer/Frontend/CheckerRegistry.h
index 052ef66e60..8550baa43e 100644
--- a/include/clang/StaticAnalyzer/Frontend/CheckerRegistry.h
+++ b/include/clang/StaticAnalyzer/Frontend/CheckerRegistry.h
@@ -91,6 +91,27 @@ public:
using InitializationFunction = void (*)(CheckerManager &);
using ShouldRegisterFunction = bool (*)(const LangOptions &);
+ /// Specifies a command line option. It may either belong to a checker or a
+ /// package.
+ struct CmdLineOption {
+ StringRef OptionType;
+ StringRef OptionName;
+ StringRef DefaultValStr;
+ StringRef Description;
+
+ CmdLineOption(StringRef OptionType, StringRef OptionName,
+ StringRef DefaultValStr, StringRef Description)
+ : OptionType(OptionType), OptionName(OptionName),
+ DefaultValStr(DefaultValStr), Description(Description) {
+
+ assert((OptionType == "bool" || OptionType == "string" ||
+ OptionType == "int") &&
+ "Unknown command line option type!");
+ }
+ };
+
+ using CmdLineOptionList = llvm::SmallVector<CmdLineOption, 0>;
+
struct CheckerInfo;
using CheckerInfoList = std::vector<CheckerInfo>;
@@ -98,6 +119,8 @@ public:
using ConstCheckerInfoList = llvm::SmallVector<const CheckerInfo *, 0>;
using CheckerInfoSet = llvm::SetVector<const CheckerInfo *>;
+ /// Specifies a checker. Note that this isn't what we call a checker object,
+ /// it merely contains everything required to create one.
struct CheckerInfo {
enum class StateFromCmdLine {
// This checker wasn't explicitly enabled or disabled.
@@ -113,6 +136,7 @@ public:
StringRef FullName;
StringRef Desc;
StringRef DocumentationUri;
+ CmdLineOptionList CmdLineOptions;
StateFromCmdLine State = StateFromCmdLine::State_Unspecified;
ConstCheckerInfoList Dependencies;
@@ -136,6 +160,23 @@ public:
using StateFromCmdLine = CheckerInfo::StateFromCmdLine;
+ /// Specifies a package. Each package option is implicitly an option for all
+ /// checkers within the package.
+ struct PackageInfo {
+ StringRef FullName;
+ CmdLineOptionList CmdLineOptions;
+
+ // Since each package must have a different full name, we can identify
+ // CheckerInfo objects by them.
+ bool operator==(const PackageInfo &Rhs) const {
+ return FullName == Rhs.FullName;
+ }
+
+ explicit PackageInfo(StringRef FullName) : FullName(FullName) {}
+ };
+
+ using PackageInfoList = llvm::SmallVector<PackageInfo, 0>;
+
private:
template <typename T> static void initializeManager(CheckerManager &mgr) {
mgr.registerChecker<T>();
@@ -165,6 +206,35 @@ public:
/// called \p dependency.
void addDependency(StringRef FullName, StringRef Dependency);
+ /// Registers an option to a given checker. A checker option will always have
+ /// the following format:
+ /// CheckerFullName:OptionName=Value
+ /// And can be specified from the command line like this:
+ /// -analyzer-config CheckerFullName:OptionName=Value
+ ///
+ /// Options for unknown checkers, or unknown options for a given checker, or
+ /// invalid value types for that given option are reported as an error in
+ /// non-compatibility mode.
+ void addCheckerOption(StringRef OptionType, StringRef CheckerFullName,
+ StringRef OptionName, StringRef DefaultValStr,
+ StringRef Description);
+
+ /// Adds a package to the registry.
+ void addPackage(StringRef FullName);
+
+ /// Registers an option to a given package. A package option will always have
+ /// the following format:
+ /// PackageFullName:OptionName=Value
+ /// And can be specified from the command line like this:
+ /// -analyzer-config PackageFullName:OptionName=Value
+ ///
+ /// Options for unknown packages, or unknown options for a given package, or
+ /// invalid value types for that given option are reported as an error in
+ /// non-compatibility mode.
+ void addPackageOption(StringRef OptionType, StringRef PackageFullName,
+ StringRef OptionName, StringRef DefaultValStr,
+ StringRef Description);
+
// FIXME: This *really* should be added to the frontend flag descriptions.
/// Initializes a CheckerManager by calling the initialization functions for
/// all checkers specified by the given CheckerOptInfo list. The order of this
@@ -193,21 +263,30 @@ private:
CheckerInfoListRange getMutableCheckersForCmdLineArg(StringRef CmdLineArg);
CheckerInfoList Checkers;
+ PackageInfoList Packages;
+ /// Used for couting how many checkers belong to a certain package in the
+ /// \c Checkers field. For convenience purposes.
llvm::StringMap<size_t> PackageSizes;
/// Contains all (Dependendent checker, Dependency) pairs. We need this, as
/// we'll resolve dependencies after all checkers were added first.
llvm::SmallVector<std::pair<StringRef, StringRef>, 0> Dependencies;
-
void resolveDependencies();
+ /// Contains all (FullName, CmdLineOption) pairs. Similarly to dependencies,
+ /// we only modify the actual CheckerInfo and PackageInfo objects once all
+ /// of them have been added.
+ llvm::SmallVector<std::pair<StringRef, CmdLineOption>, 0> PackageOptions;
+ llvm::SmallVector<std::pair<StringRef, CmdLineOption>, 0> CheckerOptions;
+
+ void resolveCheckerAndPackageOptions();
+
DiagnosticsEngine &Diags;
AnalyzerOptions &AnOpts;
const LangOptions &LangOpts;
};
} // namespace ento
-
} // namespace clang
#endif // LLVM_CLANG_STATICANALYZER_CORE_CHECKERREGISTRY_H
diff --git a/lib/Frontend/CompilerInvocation.cpp b/lib/Frontend/CompilerInvocation.cpp
index 5caf8b4e10..cc7d5b62a8 100644
--- a/lib/Frontend/CompilerInvocation.cpp
+++ b/lib/Frontend/CompilerInvocation.cpp
@@ -422,7 +422,7 @@ static void initOption(AnalyzerOptions::ConfigTable &Config,
OptionField = DefaultVal;
bool HasFailed = getStringOption(Config, Name, std::to_string(DefaultVal))
- .getAsInteger(10, OptionField);
+ .getAsInteger(0, OptionField);
if (Diags && HasFailed)
Diags->Report(diag::err_analyzer_config_invalid_input)
<< Name << "an unsigned";
diff --git a/lib/StaticAnalyzer/Frontend/CheckerRegistry.cpp b/lib/StaticAnalyzer/Frontend/CheckerRegistry.cpp
index 8f1c87a92e..f1ee454f63 100644
--- a/lib/StaticAnalyzer/Frontend/CheckerRegistry.cpp
+++ b/lib/StaticAnalyzer/Frontend/CheckerRegistry.cpp
@@ -45,6 +45,7 @@ template <class T> struct FullNameLT {
}
};
+using PackageNameLT = FullNameLT<CheckerRegistry::PackageInfo>;
using CheckerNameLT = FullNameLT<CheckerRegistry::CheckerInfo>;
} // end of anonymous namespace
@@ -118,6 +119,9 @@ CheckerRegistry::CheckerRegistry(
addChecker(register##CLASS, shouldRegister##CLASS, FULLNAME, HELPTEXT, \
DOC_URI);
+#define GET_PACKAGES
+#define PACKAGE(FULLNAME) addPackage(FULLNAME);
+
#include "clang/StaticAnalyzer/Checkers/Checkers.inc"
#undef CHECKER
#undef GET_CHECKERS
@@ -166,6 +170,7 @@ CheckerRegistry::CheckerRegistry(
// FIXME: Alphabetical sort puts 'experimental' in the middle.
// Would it be better to name it '~experimental' or something else
// that's ASCIIbetically last?
+ llvm::sort(Packages, PackageNameLT{});
llvm::sort(Checkers, CheckerNameLT{});
#define GET_CHECKER_DEPENDENCIES
@@ -173,11 +178,24 @@ CheckerRegistry::CheckerRegistry(
#define CHECKER_DEPENDENCY(FULLNAME, DEPENDENCY) \
addDependency(FULLNAME, DEPENDENCY);
+#define GET_CHECKER_OPTIONS
+#define CHECKER_OPTION(TYPE, FULLNAME, CMDFLAG, DESC, DEFAULT_VAL) \
+ addCheckerOption(TYPE, FULLNAME, CMDFLAG, DEFAULT_VAL, DESC);
+
+#define GET_PACKAGE_OPTIONS
+#define PACKAGE_OPTION(TYPE, FULLNAME, CMDFLAG, DESC, DEFAULT_VAL) \
+ addPackageOption(TYPE, FULLNAME, CMDFLAG, DEFAULT_VAL, DESC);
+
#include "clang/StaticAnalyzer/Checkers/Checkers.inc"
#undef CHECKER_DEPENDENCY
#undef GET_CHECKER_DEPENDENCIES
+#undef CHECKER_OPTION
+#undef GET_CHECKER_OPTIONS
+#undef PACKAGE_OPTION
+#undef GET_PACKAGE_OPTIONS
resolveDependencies();
+ resolveCheckerAndPackageOptions();
// Parse '-analyzer-checker' and '-analyzer-disable-checker' options from the
// command line.
@@ -266,20 +284,6 @@ CheckerRegistry::CheckerInfoSet CheckerRegistry::getEnabledCheckers() const {
return EnabledCheckers;
}
-void CheckerRegistry::addChecker(InitializationFunction Rfn,
- ShouldRegisterFunction Sfn, StringRef Name,
- StringRef Desc, StringRef DocsUri) {
- Checkers.emplace_back(Rfn, Sfn, Name, Desc, DocsUri);
-
- // Record the presence of the checker in its packages.
- StringRef PackageName, LeafName;
- std::tie(PackageName, LeafName) = Name.rsplit(PackageSeparator);
- while (!LeafName.empty()) {
- PackageSizes[PackageName] += 1;
- std::tie(PackageName, LeafName) = PackageName.rsplit(PackageSeparator);
- }
-}
-
void CheckerRegistry::resolveDependencies() {
for (const std::pair<StringRef, StringRef> &Entry : Dependencies) {
auto CheckerIt = binaryFind(Checkers, Entry.first);
@@ -302,6 +306,72 @@ void CheckerRegistry::addDependency(StringRef FullName, StringRef Dependency) {
Dependencies.emplace_back(FullName, Dependency);
}
+template <class T>
+static void
+insertOptionToCollection(StringRef FullName, T &Collection,
+ const CheckerRegistry::CmdLineOption &&Option) {
+ auto It = binaryFind(Collection, FullName);
+ assert(It != Collection.end() &&
+ "Failed to find the checker while attempting to add a command line "
+ "option to it!");
+
+ It->CmdLineOptions.emplace_back(std::move(Option));
+}
+
+void CheckerRegistry::resolveCheckerAndPackageOptions() {
+ for (const std::pair<StringRef, CmdLineOption> &CheckerOptEntry :
+ CheckerOptions) {
+ insertOptionToCollection(CheckerOptEntry.first, Checkers,
+ std::move(CheckerOptEntry.second));
+ }
+ CheckerOptions.clear();
+
+ for (const std::pair<StringRef, CmdLineOption> &PackageOptEntry :
+ PackageOptions) {
+ insertOptionToCollection(PackageOptEntry.first, Checkers,
+ std::move(PackageOptEntry.second));
+ }
+ PackageOptions.clear();
+}
+
+void CheckerRegistry::addPackage(StringRef FullName) {
+ Packages.emplace_back(PackageInfo(FullName));
+}
+
+void CheckerRegistry::addPackageOption(StringRef OptionType,
+ StringRef PackageFullName,
+ StringRef OptionName,
+ StringRef DefaultValStr,
+ StringRef Description) {
+ PackageOptions.emplace_back(
+ PackageFullName,
+ CmdLineOption{OptionType, OptionName, DefaultValStr, Description});
+}
+
+void CheckerRegistry::addChecker(InitializationFunction Rfn,
+ ShouldRegisterFunction Sfn, StringRef Name,
+ StringRef Desc, StringRef DocsUri) {
+ Checkers.emplace_back(Rfn, Sfn, Name, Desc, DocsUri);
+
+ // Record the presence of the checker in its packages.
+ StringRef PackageName, LeafName;
+ std::tie(PackageName, LeafName) = Name.rsplit(PackageSeparator);
+ while (!LeafName.empty()) {
+ PackageSizes[PackageName] += 1;
+ std::tie(PackageName, LeafName) = PackageName.rsplit(PackageSeparator);
+ }
+}
+
+void CheckerRegistry::addCheckerOption(StringRef OptionType,
+ StringRef CheckerFullName,
+ StringRef OptionName,
+ StringRef DefaultValStr,
+ StringRef Description) {
+ CheckerOptions.emplace_back(
+ CheckerFullName,
+ CmdLineOption{OptionType, OptionName, DefaultValStr, Description});
+}
+
void CheckerRegistry::initializeManager(CheckerManager &CheckerMgr) const {
// Collect checkers enabled by the options.
CheckerInfoSet enabledCheckers = getEnabledCheckers();
diff --git a/test/Analysis/disable-all-checks.c b/test/Analysis/disable-all-checks.c
index fba53429ab..4d1c625ef1 100644
--- a/test/Analysis/disable-all-checks.c
+++ b/test/Analysis/disable-all-checks.c
@@ -12,7 +12,7 @@
//
// expected-no-diagnostics
-// CHECK: no analyzer checkers are associated with 'non.existant.Checker'
+// CHECK: no analyzer checkers or packages are associated with 'non.existant.Checker'
// CHECK: use -analyzer-disable-all-checks to disable all static analyzer checkers
int buggy() {
int x = 0;
diff --git a/test/Analysis/invalid-checker-option.c b/test/Analysis/invalid-checker-option.c
new file mode 100644
index 0000000000..4ce783539f
--- /dev/null
+++ b/test/Analysis/invalid-checker-option.c
@@ -0,0 +1,19 @@
+// RUN: not %clang_analyze_cc1 -verify %s \
+// RUN: -analyzer-checker=core \
+// RUN: -analyzer-config RetainOneTwoThree:CheckOSObject=false \
+// RUN: 2>&1 | FileCheck %s -check-prefix=CHECK-NON-EXISTENT-CHECKER
+
+// Note that non-existent packages and checkers were always reported.
+
+// RUN: not %clang_analyze_cc1 -verify %s \
+// RUN: -analyzer-checker=core \
+// RUN: -analyzer-config-compatibility-mode=true \
+// RUN: -analyzer-config RetainOneTwoThree:CheckOSObject=false \
+// RUN: 2>&1 | FileCheck %s -check-prefix=CHECK-NON-EXISTENT-CHECKER
+
+// CHECK-NON-EXISTENT-CHECKER: (frontend): no analyzer checkers or packages
+// CHECK-NON-EXISTENT-CHECKER-SAME: are associated with 'RetainOneTwoThree'
+
+// expected-no-diagnostics
+
+int main() {}
diff --git a/utils/TableGen/ClangSACheckersEmitter.cpp b/utils/TableGen/ClangSACheckersEmitter.cpp
index 259df5b138..03c3b3b9fb 100644
--- a/utils/TableGen/ClangSACheckersEmitter.cpp
+++ b/utils/TableGen/ClangSACheckersEmitter.cpp
@@ -90,6 +90,26 @@ static std::string getCheckerDocs(const Record &R) {
.str();
}
+/// Retrieves the type from a CmdOptionTypeEnum typed Record object. Note that
+/// the class itself has to be modified for adding a new option type in
+/// CheckerBase.td.
+static std::string getCheckerOptionType(const Record &R) {
+ if (BitsInit *BI = R.getValueAsBitsInit("Type")) {
+ switch(getValueFromBitsInit(BI, R)) {
+ case 0:
+ return "int";
+ case 1:
+ return "string";
+ case 2:
+ return "bool";
+ }
+ }
+ PrintFatalError(R.getLoc(),
+ "unable to parse command line option type for "
+ + getCheckerFullName(&R));
+ return "";
+}
+
static void printChecker(llvm::raw_ostream &OS, const Record &R) {
OS << "CHECKER(" << "\"";
OS.write_escaped(getCheckerFullName(&R)) << "\", ";
@@ -134,6 +154,45 @@ void EmitClangSACheckers(RecordKeeper &Records, raw_ostream &OS) {
OS << "#endif // GET_PACKAGES\n"
"\n";
+ // Emit a package option.
+ //
+ // PACKAGE_OPTION(OPTIONTYPE, PACKAGENAME, OPTIONNAME, DESCRIPTION, DEFAULT)
+ // - OPTIONTYPE: Type of the option, whether it's integer or boolean etc.
+ // This is important for validating user input. Note that
+ // it's a string, rather than an actual type: since we can
+ // load checkers runtime, we can't use template hackery for
+ // sorting this out compile-time.
+ // - PACKAGENAME: Name of the package.
+ // - OPTIONNAME: Name of the option.
+ // - DESCRIPTION
+ // - DEFAULT: The default value for this option.
+ //
+ // The full option can be specified in the command like like this:
+ // -analyzer-config PACKAGENAME:OPTIONNAME=VALUE
+ OS << "\n"
+ "#ifdef GET_PACKAGE_OPTIONS\n";
+ for (const Record *Package : packages) {
+
+ if (Package->isValueUnset("PackageOptions"))
+ continue;
+
+ std::vector<Record *> PackageOptions = Package
+ ->getValueAsListOfDefs("PackageOptions");
+ for (Record *PackageOpt : PackageOptions) {
+ OS << "PACKAGE_OPTION(\"";
+ OS.write_escaped(getCheckerOptionType(*PackageOpt)) << "\", \"";
+ OS.write_escaped(getPackageFullName(Package)) << "\", ";
+ OS << '\"' << getStringValue(*PackageOpt, "CmdFlag") << "\", ";
+ OS << '\"';
+ OS.write_escaped(getStringValue(*PackageOpt, "Desc")) << "\", ";
+ OS << '\"';
+ OS.write_escaped(getStringValue(*PackageOpt, "DefaultVal")) << "\"";
+ OS << ")\n";
+ }
+ }
+ OS << "#endif // GET_PACKAGE_OPTIONS\n"
+ "\n";
+
// Emit checkers.
//
// CHECKER(FULLNAME, CLASS, HELPTEXT)
@@ -160,15 +219,15 @@ void EmitClangSACheckers(RecordKeeper &Records, raw_ostream &OS) {
// - DEPENDENCY: The full name of the checker FULLNAME depends on.
OS << "\n"
"#ifdef GET_CHECKER_DEPENDENCIES\n";
- for (const Record *checker : checkers) {
- if (checker->isValueUnset("Dependencies"))
+ for (const Record *Checker : checkers) {
+ if (Checker->isValueUnset("Dependencies"))
continue;
for (const Record *Dependency :
- checker->getValueAsListOfDefs("Dependencies")) {
+ Checker->getValueAsListOfDefs("Dependencies")) {
OS << "CHECKER_DEPENDENCY(";
OS << '\"';
- OS.write_escaped(getCheckerFullName(checker)) << "\", ";
+ OS.write_escaped(getCheckerFullName(Checker)) << "\", ";
OS << '\"';
OS.write_escaped(getCheckerFullName(Dependency)) << '\"';
OS << ")\n";
@@ -176,5 +235,45 @@ void EmitClangSACheckers(RecordKeeper &Records, raw_ostream &OS) {
}
OS << "\n"
"#endif // GET_CHECKER_DEPENDENCIES\n";
+
+ // Emit a package option.
+ //
+ // CHECKER_OPTION(OPTIONTYPE, CHECKERNAME, OPTIONNAME, DESCRIPTION, DEFAULT)
+ // - OPTIONTYPE: Type of the option, whether it's integer or boolean etc.
+ // This is important for validating user input. Note that
+ // it's a string, rather than an actual type: since we can
+ // load checkers runtime, we can't use template hackery for
+ // sorting this out compile-time.
+ // - CHECKERNAME: Name of the package.
+ // - OPTIONNAME: Name of the option.
+ // - DESCRIPTION
+ // - DEFAULT: The default value for this option.
+ //
+ // The full option can be specified in the command like like this:
+ // -analyzer-config CHECKERNAME:OPTIONNAME=VALUE
+ OS << "\n"
+ "#ifdef GET_CHECKER_OPTIONS\n";
+ for (const Record *Checker : checkers) {
+
+ if (Checker->isValueUnset("CheckerOptions"))
+ continue;
+
+ std::vector<Record *> CheckerOptions = Checker
+ ->getValueAsListOfDefs("CheckerOptions");
+ for (Record *CheckerOpt : CheckerOptions) {
+ OS << "CHECKER_OPTION(\"";
+ OS << getCheckerOptionType(*CheckerOpt) << "\", \"";
+ OS.write_escaped(getCheckerFullName(Checker)) << "\", ";
+ OS << '\"' << getStringValue(*CheckerOpt, "CmdFlag") << "\", ";
+ OS << '\"';
+ OS.write_escaped(getStringValue(*CheckerOpt, "Desc")) << "\", ";
+ OS << '\"';
+ OS.write_escaped(getStringValue(*CheckerOpt, "DefaultVal")) << "\"";
+ OS << ")";
+ OS << '\n';
+ }
+ }
+ OS << "#endif // GET_CHECKER_OPTIONS\n"
+ "\n";
}
} // end namespace clang