Szelethus updated this revision to Diff 190977. Szelethus edited the summary of this revision. Szelethus added a comment.
- Moved every non-related change to smaller patches, this should ease **a lot** on reviewers. - Now processing options once all checkers are added to the registry. This is important, because I use binary searches to find the checkers and packages that need to be modified -- if a plugin however called `CheckerRegistry::add*Option`, it would've cause an assertion failure. In D57855#1392560 <https://reviews.llvm.org/D57855#1392560>, @xazax.hun wrote: > We have `examples/analyzer-plugin`. I would prefer to add an example option > to the example plugin so people do see how to do this when they are > registering a checker from a plugin. Coming in a separate patch later! CHANGES SINCE LAST ACTION https://reviews.llvm.org/D57855/new/ https://reviews.llvm.org/D57855 Files: include/clang/Basic/DiagnosticCommonKinds.td include/clang/StaticAnalyzer/Checkers/CheckerBase.td include/clang/StaticAnalyzer/Checkers/Checkers.td include/clang/StaticAnalyzer/Frontend/CheckerRegistry.h lib/Frontend/CompilerInvocation.cpp lib/StaticAnalyzer/Frontend/CheckerRegistry.cpp test/Analysis/disable-all-checks.c test/Analysis/invalid-checker-option.c utils/TableGen/ClangSACheckersEmitter.cpp
Index: utils/TableGen/ClangSACheckersEmitter.cpp =================================================================== --- utils/TableGen/ClangSACheckersEmitter.cpp +++ utils/TableGen/ClangSACheckersEmitter.cpp @@ -90,6 +90,26 @@ .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 @@ 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 @@ // - 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 @@ } 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 Index: test/Analysis/invalid-checker-option.c =================================================================== --- /dev/null +++ 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() {} Index: test/Analysis/disable-all-checks.c =================================================================== --- test/Analysis/disable-all-checks.c +++ 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; Index: lib/StaticAnalyzer/Frontend/CheckerRegistry.cpp =================================================================== --- lib/StaticAnalyzer/Frontend/CheckerRegistry.cpp +++ lib/StaticAnalyzer/Frontend/CheckerRegistry.cpp @@ -47,6 +47,7 @@ } }; +using PackageNameLT = FullNameLT<CheckerRegistry::PackageInfo>; using CheckerNameLT = FullNameLT<CheckerRegistry::CheckerInfo>; } // end of anonymous namespace @@ -123,6 +124,9 @@ 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 @@ -171,6 +175,7 @@ // 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 @@ -178,11 +183,24 @@ #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. @@ -271,20 +289,6 @@ 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); @@ -306,6 +310,72 @@ 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(); Index: lib/Frontend/CompilerInvocation.cpp =================================================================== --- lib/Frontend/CompilerInvocation.cpp +++ lib/Frontend/CompilerInvocation.cpp @@ -422,7 +422,7 @@ 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"; Index: include/clang/StaticAnalyzer/Frontend/CheckerRegistry.h =================================================================== --- include/clang/StaticAnalyzer/Frontend/CheckerRegistry.h +++ include/clang/StaticAnalyzer/Frontend/CheckerRegistry.h @@ -91,6 +91,27 @@ 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 @@ 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 @@ StringRef FullName; StringRef Desc; StringRef DocumentationUri; + CmdLineOptionList CmdLineOptions; StateFromCmdLine State = StateFromCmdLine::State_Unspecified; ConstCheckerInfoList Dependencies; @@ -136,6 +160,23 @@ 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 @@ /// 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 @@ 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 Index: include/clang/StaticAnalyzer/Checkers/Checkers.td =================================================================== --- include/clang/StaticAnalyzer/Checkers/Checkers.td +++ include/clang/StaticAnalyzer/Checkers/Checkers.td @@ -42,7 +42,13 @@ // 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", + "", + "false"> + ]>; def Cplusplus : Package<"cplusplus">; def CplusplusAlpha : Package<"cplusplus">, ParentPackage<Alpha>; @@ -371,6 +377,14 @@ 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 +461,28 @@ 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 +491,12 @@ 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 +533,40 @@ 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 +628,13 @@ 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 +743,18 @@ 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 +792,14 @@ 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 +891,20 @@ def RetainCountChecker : Checker<"RetainCount">, HelpText<"Check for leaks and improper reference count management">, + CheckerOptions<[ + CmdLineOption<Boolean, + "leak-diagnostics-reference-allocation", + "", + "false">, + CmdLineOption<Boolean, + "CheckOSObject", + "", + "true">, + CmdLineOption<Boolean, + "TrackNSCFStartParam", + "", + "false"> + ]>, Dependencies<[RetainCountBase]>, Documentation<HasDocumentation>; @@ -903,6 +1013,17 @@ 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 +1082,72 @@ 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">, @@ -1030,6 +1217,25 @@ 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" Index: include/clang/StaticAnalyzer/Checkers/CheckerBase.td =================================================================== --- include/clang/StaticAnalyzer/Checkers/CheckerBase.td +++ 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 @@ /// 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 Index: include/clang/Basic/DiagnosticCommonKinds.td =================================================================== --- include/clang/Basic/DiagnosticCommonKinds.td +++ include/clang/Basic/DiagnosticCommonKinds.td @@ -292,7 +292,7 @@ // 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">; }
_______________________________________________ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits