From 333e5a248f5a13195028fe7adb59870f6601fec8 Mon Sep 17 00:00:00 2001 From: Kristof Umann Date: Fri, 19 Apr 2019 12:32:10 +0000 Subject: [PATCH] [analyzer][NFC] Reimplement checker options TL;DR: * Add checker and package options to the TableGen files * Added a new class called CmdLineOption, and both Package and Checker recieved a list field. * Added every existing checker and package option to Checkers.td. * The CheckerRegistry class * Received some comments to most of it's inline classes * Received the CmdLineOption and PackageInfo inline classes, a list of CmdLineOption was added to CheckerInfo and PackageInfo * Added addCheckerOption and addPackageOption * Added a new field called Packages, used in addPackageOptions, filled up in addPackage Detailed description: In the last couple months, a lot of effort was put into tightening the analyzer's command line interface. The main issue is that it's spectacularly easy to mess up a lenghty enough invocation of the analyzer, and the user was given no warnings or errors at all in that case. We can divide the effort of resolving this into several chapters: * Non-checker analyzer configurations: Gather every analyzer configuration into a dedicated file. Emit errors for non-existent configurations or incorrect values. Be able to list these configurations. Tighten AnalyzerOptions interface to disallow making such a mistake in the future. * Fix the "Checker Naming Bug" by reimplementing checker dependencies: When cplusplus.InnerPointer was enabled, it implicitly registered unix.Malloc, which implicitly registered some sort of a modeling checker from the CStringChecker family. This resulted in all of these checker objects recieving the name "cplusplus.InnerPointer", making AnalyzerOptions asking for the wrong checker options from the command line: cplusplus.InnerPointer:Optimisic istead of unix.Malloc:Optimistic. This was resolved by making CheckerRegistry responsible for checker dependency handling, instead of checkers themselves. * Checker options: (this patch included!) Same as the first item, but for checkers. (+ minor fixes here and there, and everything else that is yet to come) There were several issues regarding checker options, that non-checker configurations didn't suffer from: checker plugins are loaded runtime, and they could add new checkers and new options, meaning that unlike for non-checker configurations, we can't collect every checker option purely by generating code. Also, as seen from the "Checker Naming Bug" issue raised above, they are very rarely used in practice, and all sorts of skeletons fell out of the closet while working on this project. They were extremely problematic for users as well, purely because of how long they were. Consider the following monster of a checker option: alpha.cplusplus.UninitializedObject:CheckPointeeInitialization=false While we were able to verify whether the checker itself (the part before the colon) existed, any errors past that point were unreported, easily resulting in 7+ hours of analyses going to waste. This patch, similarly to how dependencies were reimplemented, uses TableGen to register checker options into Checkers.td, so that Checkers.inc now contains entries for both checker and package options. Using the preprocessor, Checkers.inc is converted into code in CheckerRegistry, adding every builtin (checkers and packages that have an entry in the Checkers.td file) checker and package option to the registry. The new addPackageOption and addCheckerOption functions expose the same functionality to statically-linked non-builtin and plugin checkers and packages as well. Emitting errors for incorrect user input, being able to list these options, and some other functionalies will land in later patches. Differential Revision: https://reviews.llvm.org/D57855 git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@358752 91177308-0d34-0410-b5e6-96231b3b80d8 --- include/clang/Basic/DiagnosticCommonKinds.td | 2 +- .../StaticAnalyzer/Checkers/CheckerBase.td | 53 ++++- .../clang/StaticAnalyzer/Checkers/Checkers.td | 225 +++++++++++++++++- .../StaticAnalyzer/Frontend/CheckerRegistry.h | 83 ++++++- lib/Frontend/CompilerInvocation.cpp | 2 +- .../Frontend/CheckerRegistry.cpp | 98 ++++++-- test/Analysis/disable-all-checks.c | 2 +- test/Analysis/invalid-checker-option.c | 19 ++ utils/TableGen/ClangSACheckersEmitter.cpp | 107 ++++++++- 9 files changed, 555 insertions(+), 36 deletions(-) create mode 100644 test/Analysis/invalid-checker-option.c 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 val> { + bits<2> Type = val; +} +def Integer : CmdLineOptionTypeEnum<0>; +def String : CmdLineOptionTypeEnum<1>; +def Boolean : CmdLineOptionTypeEnum<2>; + +class Type { + bits<2> Type = val.Type; +} + +/// Describes an option for a checker or a package. +class CmdLineOption { + bits<2> Type = type.Type; + string CmdFlag = cmdFlag; + string Desc = desc; + string DefaultVal = defaultVal; +} + +/// Describes a list of package options. +class PackageOptions opts> { + list 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 PackageName = name; - Package ParentPackage; + string PackageName = name; + // This field is optional. + list PackageOptions; + Package ParentPackage; } /// Describes a 'super' package that holds another package inside it. This is @@ -52,11 +83,19 @@ class Documentation { /// def DereferenceChecker : Checker<"NullDereference">, /// HelpText<"Check for dereferences of null pointers">; class Checker { - string CheckerName = name; - string HelpText; - list Dependencies; - bits<2> Documentation; - Package ParentPackage; + string CheckerName = name; + string HelpText; + // This field is optional. + list CheckerOptions; + // This field is optional. + list Dependencies; + bits<2> Documentation; + Package ParentPackage; +} + +/// Describes a list of checker options. +class CheckerOptions opts> { + list 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; -def Nullability : Package<"nullability">; +def Nullability : Package<"nullability">, + PackageOptions<[ + CmdLineOption + ]>; def Cplusplus : Package<"cplusplus">; def CplusplusAlpha : Package<"cplusplus">, ParentPackage; @@ -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 + ]>, Dependencies<[CStringModeling]>, Documentation; @@ -447,7 +465,28 @@ def CXXSelfAssignmentChecker : Checker<"SelfAssignment">, Documentation; def MoveChecker: Checker<"Move">, - HelpText<"Find use-after-move bugs in C++">, + HelpText<"Find use-after-move bugs in C++">, + CheckerOptions<[ + CmdLineOption + ]>, Documentation; } // 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 + ]>, Documentation; } // end: "optin.cplusplus" @@ -492,7 +537,40 @@ def MismatchedIteratorChecker : Checker<"MismatchedIterator">, Documentation; def UninitializedObjectChecker: Checker<"UninitializedObject">, - HelpText<"Reports uninitialized fields after object construction">, + HelpText<"Reports uninitialized fields after object construction">, + CheckerOptions<[ + CmdLineOption, + CmdLineOption, + CmdLineOption, + CmdLineOption, + CmdLineOption + ]>, Documentation; } // end: "alpha.cplusplus" @@ -554,6 +632,13 @@ let ParentPackage = Performance in { def PaddingChecker : Checker<"Padding">, HelpText<"Check for excessively padded structs.">, + CheckerOptions<[ + CmdLineOption + ]>, Documentation; } // end: "padding" @@ -662,11 +747,18 @@ def MallocOverflowSecurityChecker : Checker<"MallocOverflow">, HelpText<"Check for overflows in the arguments to malloc()">, Documentation; -// 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, + CmdLineOption + ]>, Documentation; } // 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 + ]>, Documentation; 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, + CmdLineOption + ]>, Dependencies<[RetainCountBase]>, Documentation; @@ -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 + ]>, Documentation; 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, + CmdLineOption, + CmdLineOption, + CmdLineOption, + CmdLineOption, + CmdLineOption, + CmdLineOption, + CmdLineOption, + CmdLineOption, + CmdLineOption, + CmdLineOption, + CmdLineOption, + CmdLineOption, + CmdLineOption, + CmdLineOption, + CmdLineOption + ]>, Documentation; def DominatorsTreeDumper : Checker<"DumpDominators">, @@ -1034,6 +1228,25 @@ let ParentPackage = CloneDetectionAlpha in { def CloneChecker : Checker<"CloneChecker">, HelpText<"Reports similar pieces of code.">, + CheckerOptions<[ + CmdLineOption, + CmdLineOption, + CmdLineOption + ]>, Documentation; } // 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; + struct CheckerInfo; using CheckerInfoList = std::vector; @@ -98,6 +119,8 @@ public: using ConstCheckerInfoList = llvm::SmallVector; using CheckerInfoSet = llvm::SetVector; + /// 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; + private: template static void initializeManager(CheckerManager &mgr) { mgr.registerChecker(); @@ -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 PackageSizes; /// Contains all (Dependendent checker, Dependency) pairs. We need this, as /// we'll resolve dependencies after all checkers were added first. llvm::SmallVector, 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, 0> PackageOptions; + llvm::SmallVector, 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 struct FullNameLT { } }; +using PackageNameLT = FullNameLT; using CheckerNameLT = FullNameLT; } // 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 &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 +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 &CheckerOptEntry : + CheckerOptions) { + insertOptionToCollection(CheckerOptEntry.first, Checkers, + std::move(CheckerOptEntry.second)); + } + CheckerOptions.clear(); + + for (const std::pair &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 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 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 -- 2.40.0