Index: include/clang/StaticAnalyzer/Core/AnalyzerOptions.h =================================================================== --- include/clang/StaticAnalyzer/Core/AnalyzerOptions.h +++ include/clang/StaticAnalyzer/Core/AnalyzerOptions.h @@ -27,6 +27,9 @@ class DiagnosticsEngine; class Preprocessor; class LangOptions; +namespace ento { +class CheckerBase; +} /// Analysis - Set of available source code analyses. enum Analyses { @@ -132,7 +135,7 @@ AnalysisPurgeMode AnalysisPurgeOpt; std::string AnalyzeSpecificFunction; - + /// \brief The maximum number of times the analyzer visits a block. unsigned maxBlockVisitOnPath; @@ -245,18 +248,62 @@ /// \sa getMaxNodesPerTopLevelFunction Optional MaxNodesPerTopLevelFunction; + /// A helper function that retrieves option for a given full-qualified + /// checker name. + /// Options for checkers can be specified via 'analyzer-config' command-line + /// option. + /// Example: + /// @code-analyzer-config unix.Malloc:OptionName=CheckerOptionValue @endcode + /// or @code-analyzer-config unix:OptionName=GroupOptionValue @endcode + /// for groups of checkers + /// @param [in] CheckerName Full-qualified checker name, like + /// alpha.unix.StreamChecker + /// @param [in] OptionName Name of the option to get + /// @param [in] Default Default value if no option is specified + /// @retval CheckerOptionValue An option for a checker if it was specified + /// @retval GroupOptionValue An option for group if it was specified and no + /// checker-specific options were found. The closer group to checker, + /// the more priority it has. For example, @c coregroup.subgroup has more + /// priority than @c coregroup for @c coregroup.subgroup.CheckerName checker + /// @retval Default If nor checker option, nor group option was found + StringRef getCheckerOption(StringRef CheckerName, StringRef OptionName, + StringRef Default) const; + public: /// Interprets an option's string value as a boolean. /// /// Accepts the strings "true" and "false". /// If an option value is not provided, returns the given \p DefaultVal. - bool getBooleanOption(StringRef Name, bool DefaultVal); + /// @param [in] Name Name for option to retrieve + /// @param [in] DefaultVal Default value returned if no such option was + /// specified + /// @param [in] C Optional parameter that may be used to retrieve + /// checker-related option for a given checker + bool getBooleanOption(StringRef Name, bool DefaultVal, + const ento::CheckerBase *C = nullptr); /// Variant that accepts a Optional value to cache the result. - bool getBooleanOption(Optional &V, StringRef Name, bool DefaultVal); + /// + /// @param [in,out] V Return value storage, returned if parameter contains + /// an existing valid option, else it is used to store a return value + /// @param [in] Name Name for option to retrieve + /// @param [in] DefaultVal Default value returned if no such option was + /// specified + /// @param [in] C Optional parameter that may be used to retrieve + /// checker-related option for a given checker + bool getBooleanOption(Optional &V, StringRef Name, bool DefaultVal, + const ento::CheckerBase *C = nullptr); /// Interprets an option's string value as an integer value. - int getOptionAsInteger(StringRef Name, int DefaultVal); + /// + /// If an option value is not provided, returns the given \p DefaultVal. + /// @param [in] Name Name for option to retrieve + /// @param [in] DefaultVal Default value returned if no such option was + /// specified + /// @param [in] C Optional parameter that may be used to retrieve + /// checker-related option for a given checker + int getOptionAsInteger(StringRef Name, int DefaultVal, + const ento::CheckerBase *C = nullptr); /// \brief Retrieves and sets the UserMode. This is a high-level option, /// which is used to set other low-level options. It is not accessible Index: lib/StaticAnalyzer/Checkers/Checkers.td =================================================================== --- lib/StaticAnalyzer/Checkers/Checkers.td +++ lib/StaticAnalyzer/Checkers/Checkers.td @@ -295,7 +299,7 @@ HelpText<"Check calls to various UNIX/Posix functions">, DescFile<"UnixAPIChecker.cpp">; -def MallocPessimistic : Checker<"Malloc">, +def MallocChecker: Checker<"Malloc">, HelpText<"Check for memory leaks, double free, and use-after-free problems. Traces memory managed by malloc()/free().">, DescFile<"MallocChecker.cpp">; @@ -315,10 +319,6 @@ HelpText<"Check improper use of chroot">, DescFile<"ChrootChecker.cpp">; -def MallocOptimistic : Checker<"MallocWithAnnotations">, - HelpText<"Check for memory leaks, double free, and use-after-free problems. Traces memory managed by malloc()/free(). Assumes that all user-defined functions which might free a pointer are annotated.">, - DescFile<"MallocChecker.cpp">; - def PthreadLockChecker : Checker<"PthreadLock">, HelpText<"Simple lock -> unlock checker">, DescFile<"PthreadLockChecker.cpp">; Index: lib/StaticAnalyzer/Checkers/MallocChecker.cpp =================================================================== --- lib/StaticAnalyzer/Checkers/MallocChecker.cpp +++ lib/StaticAnalyzer/Checkers/MallocChecker.cpp @@ -165,14 +165,15 @@ /// In pessimistic mode, the checker assumes that it does not know which /// functions might free the memory. enum CheckKind { - CK_MallocPessimistic, - CK_MallocOptimistic, + CK_MallocChecker, CK_NewDeleteChecker, CK_NewDeleteLeaksChecker, CK_MismatchedDeallocatorChecker, CK_NumCheckKinds }; + DefaultBool IsOptimistic; + DefaultBool ChecksEnabled[CK_NumCheckKinds]; CheckName CheckNames[CK_NumCheckKinds]; @@ -524,7 +525,7 @@ return true; } - if (ChecksEnabled[CK_MallocOptimistic] && FD->hasAttrs()) + if (IsOptimistic && FD->hasAttrs()) for (const auto *I : FD->specific_attrs()) if (I->getOwnKind() == OwnershipAttr::Returns) return true; @@ -543,7 +544,7 @@ return true; } - if (ChecksEnabled[CK_MallocOptimistic] && FD->hasAttrs()) + if (IsOptimistic && FD->hasAttrs()) for (const auto *I : FD->specific_attrs()) if (I->getOwnKind() == OwnershipAttr::Takes || I->getOwnKind() == OwnershipAttr::Holds) @@ -733,8 +734,7 @@ } } - if (ChecksEnabled[CK_MallocOptimistic] || - ChecksEnabled[CK_MismatchedDeallocatorChecker]) { + if (IsOptimistic || ChecksEnabled[CK_MismatchedDeallocatorChecker]) { // Check all the attributes, if there are any. // There can be multiple of these attributes. if (FD->hasAttrs()) @@ -1202,11 +1202,8 @@ MallocChecker::getCheckIfTracked(AllocationFamily Family) const { switch (Family) { case AF_Malloc: { - if (ChecksEnabled[CK_MallocOptimistic]) { - return CK_MallocOptimistic; - } else if (ChecksEnabled[CK_MallocPessimistic]) { - return CK_MallocPessimistic; - } + if (ChecksEnabled[CK_MallocChecker]) + return CK_MallocChecker; return Optional(); } case AF_CXXNew: @@ -1328,9 +1325,7 @@ SourceRange Range, const Expr *DeallocExpr) const { - if (!ChecksEnabled[CK_MallocOptimistic] && - !ChecksEnabled[CK_MallocPessimistic] && - !ChecksEnabled[CK_NewDeleteChecker]) + if (!ChecksEnabled[CK_MallocChecker] && !ChecksEnabled[CK_NewDeleteChecker]) return; Optional CheckKind = @@ -1434,9 +1429,7 @@ SourceRange Range, const Expr *DeallocExpr, const Expr *AllocExpr) const { - if (!ChecksEnabled[CK_MallocOptimistic] && - !ChecksEnabled[CK_MallocPessimistic] && - !ChecksEnabled[CK_NewDeleteChecker]) + if (!ChecksEnabled[CK_MallocChecker] && !ChecksEnabled[CK_NewDeleteChecker]) return; Optional CheckKind = @@ -1490,9 +1483,7 @@ void MallocChecker::ReportUseAfterFree(CheckerContext &C, SourceRange Range, SymbolRef Sym) const { - if (!ChecksEnabled[CK_MallocOptimistic] && - !ChecksEnabled[CK_MallocPessimistic] && - !ChecksEnabled[CK_NewDeleteChecker]) + if (!ChecksEnabled[CK_MallocChecker] && !ChecksEnabled[CK_NewDeleteChecker]) return; Optional CheckKind = getCheckIfTracked(C, Sym); @@ -1518,9 +1509,7 @@ bool Released, SymbolRef Sym, SymbolRef PrevSym) const { - if (!ChecksEnabled[CK_MallocOptimistic] && - !ChecksEnabled[CK_MallocPessimistic] && - !ChecksEnabled[CK_NewDeleteChecker]) + if (!ChecksEnabled[CK_MallocChecker] && !ChecksEnabled[CK_NewDeleteChecker]) return; Optional CheckKind = getCheckIfTracked(C, Sym); @@ -1731,8 +1720,7 @@ void MallocChecker::reportLeak(SymbolRef Sym, ExplodedNode *N, CheckerContext &C) const { - if (!ChecksEnabled[CK_MallocOptimistic] && - !ChecksEnabled[CK_MallocPessimistic] && + if (!ChecksEnabled[CK_MallocChecker] && !ChecksEnabled[CK_NewDeleteLeaksChecker]) return; @@ -1865,8 +1853,7 @@ if (!FD) return; - if ((ChecksEnabled[CK_MallocOptimistic] || - ChecksEnabled[CK_MallocPessimistic]) && + if (ChecksEnabled[CK_MallocChecker] && isFreeFunction(FD, C.getASTContext())) return; @@ -2371,6 +2358,8 @@ void ento::registerNewDeleteLeaksChecker(CheckerManager &mgr) { registerCStringCheckerBasic(mgr); MallocChecker *checker = mgr.registerChecker(); + checker->IsOptimistic = mgr.getAnalyzerOptions().getBooleanOption( + "Optimistic", false, checker); checker->ChecksEnabled[MallocChecker::CK_NewDeleteLeaksChecker] = true; checker->CheckNames[MallocChecker::CK_NewDeleteLeaksChecker] = mgr.getCurrentCheckName(); @@ -2384,11 +2373,12 @@ void ento::register##name(CheckerManager &mgr) { \ registerCStringCheckerBasic(mgr); \ MallocChecker *checker = mgr.registerChecker(); \ + checker->IsOptimistic = mgr.getAnalyzerOptions().getBooleanOption( \ + "Optimistic", false, checker); \ checker->ChecksEnabled[MallocChecker::CK_##name] = true; \ checker->CheckNames[MallocChecker::CK_##name] = mgr.getCurrentCheckName(); \ } -REGISTER_CHECKER(MallocPessimistic) -REGISTER_CHECKER(MallocOptimistic) +REGISTER_CHECKER(MallocChecker) REGISTER_CHECKER(NewDeleteChecker) REGISTER_CHECKER(MismatchedDeallocatorChecker) Index: lib/StaticAnalyzer/Core/AnalyzerOptions.cpp =================================================================== --- lib/StaticAnalyzer/Core/AnalyzerOptions.cpp +++ lib/StaticAnalyzer/Core/AnalyzerOptions.cpp @@ -13,12 +13,14 @@ //===----------------------------------------------------------------------===// #include "clang/StaticAnalyzer/Core/AnalyzerOptions.h" +#include "clang/StaticAnalyzer/Core/Checker.h" #include "llvm/ADT/SmallString.h" #include "llvm/ADT/StringSwitch.h" #include "llvm/Support/ErrorHandling.h" #include "llvm/Support/raw_ostream.h" using namespace clang; +using namespace ento; using namespace llvm; AnalyzerOptions::UserModeKind AnalyzerOptions::getUserMode() { @@ -98,11 +100,33 @@ static StringRef toString(bool b) { return b ? "true" : "false"; } -bool AnalyzerOptions::getBooleanOption(StringRef Name, bool DefaultVal) { +StringRef AnalyzerOptions::getCheckerOption(StringRef CheckerName, + StringRef OptionName, + StringRef Default) const { + std::string Name = CheckerName; + std::string Suffix = ":" + OptionName.str(); + // Search for a category option if option for checker is not specified + ConfigTable::const_iterator e = Config.end(); + while (!Name.empty()) { + ConfigTable::const_iterator i = Config.find(Name + Suffix); + if (i != e) + return StringRef(i->getValue()); + size_t pos = Name.rfind('.'); + if (pos == std::string::npos) + return Default; + Name = Name.substr(0, pos); + } + return Default; +} + +bool AnalyzerOptions::getBooleanOption(StringRef Name, bool DefaultVal, + const CheckerBase *C) { // FIXME: We should emit a warning here if the value is something other than // "true", "false", or the empty string (meaning the default value), // but the AnalyzerOptions doesn't have access to a diagnostic engine. - StringRef V(Config.GetOrCreateValue(Name, toString(DefaultVal)).getValue()); + StringRef Default = toString(DefaultVal); + StringRef V = C ? getCheckerOption(C->getTagDescription(), Name, Default) + : StringRef(Config.GetOrCreateValue(Name,Default).getValue()); return llvm::StringSwitch(V) .Case("true", true) .Case("false", false) @@ -110,9 +134,9 @@ } bool AnalyzerOptions::getBooleanOption(Optional &V, StringRef Name, - bool DefaultVal) { + bool DefaultVal, const CheckerBase *C) { if (!V.hasValue()) - V = getBooleanOption(Name, DefaultVal); + V = getBooleanOption(Name, DefaultVal, C); return V.getValue(); } @@ -196,12 +220,14 @@ /* Default = */ false); } -int AnalyzerOptions::getOptionAsInteger(StringRef Name, int DefaultVal) { +int AnalyzerOptions::getOptionAsInteger(StringRef Name, int DefaultVal, + const CheckerBase *C) { SmallString<10> StrBuf; llvm::raw_svector_ostream OS(StrBuf); OS << DefaultVal; - StringRef V(Config.GetOrCreateValue(Name, OS.str()).getValue()); + StringRef V = C ? getCheckerOption(C->getTagDescription(), Name, OS.str()) + : StringRef(Config.GetOrCreateValue(Name,OS.str()).getValue()); int Res = DefaultVal; bool b = V.getAsInteger(10, Res); assert(!b && "analyzer-config option should be numeric"); Index: test/Analysis/free.c =================================================================== --- test/Analysis/free.c +++ test/Analysis/free.c @@ -1,5 +1,5 @@ // RUN: %clang_cc1 -analyze -analyzer-store=region -analyzer-checker=core,unix.Malloc -fblocks -verify %s -// RUN: %clang_cc1 -analyze -analyzer-store=region -analyzer-checker=core,alpha.unix.MallocWithAnnotations -fblocks -verify %s +// RUN: %clang_cc1 -analyze -analyzer-store=region -analyzer-checker=core,unix.Malloc -fblocks -verify -analyzer-config unix.Malloc:Optimistic=true %s void free(void *); void t1 () { Index: test/Analysis/malloc-annotations.c =================================================================== --- test/Analysis/malloc-annotations.c +++ test/Analysis/malloc-annotations.c @@ -1,4 +1,5 @@ -// RUN: %clang_cc1 -analyze -analyzer-checker=core,alpha.deadcode.UnreachableCode,alpha.core.CastSize,alpha.unix.MallocWithAnnotations -analyzer-store=region -verify %s +// RUN: %clang_cc1 -analyze -analyzer-checker=core,alpha.deadcode.UnreachableCode,alpha.core.CastSize,unix.Malloc -analyzer-store=region -verify -analyzer-config unix:Optimistic=true %s +// RUN: %clang_cc1 -analyze -analyzer-checker=core,alpha.deadcode.UnreachableCode,alpha.core.CastSize,unix.Malloc -analyzer-store=region -verify -analyzer-config unix.Malloc:Optimistic=true %s typedef __typeof(sizeof(int)) size_t; void *malloc(size_t); void free(void *); Index: test/Analysis/outofbound.c =================================================================== --- test/Analysis/outofbound.c +++ test/Analysis/outofbound.c @@ -1,4 +1,4 @@ -// RUN: %clang_cc1 -Wno-array-bounds -analyze -analyzer-checker=core,alpha.unix,alpha.security.ArrayBound -analyzer-store=region -verify %s +// RUN: %clang_cc1 -Wno-array-bounds -analyze -analyzer-checker=core,unix,alpha.security.ArrayBound -analyzer-store=region -verify -analyzer-config unix:Optimistic=true %s typedef __typeof(sizeof(int)) size_t; void *malloc(size_t); Index: test/Analysis/undef-buffers.c =================================================================== --- test/Analysis/undef-buffers.c +++ test/Analysis/undef-buffers.c @@ -1,4 +1,4 @@ -// RUN: %clang_cc1 -analyze -analyzer-checker=core,alpha.unix,core.uninitialized -analyzer-store=region -verify %s +// RUN: %clang_cc1 -analyze -analyzer-checker=core,unix,core.uninitialized -analyzer-store=region -verify -analyzer-config unix:Optimistic=true %s typedef __typeof(sizeof(int)) size_t; void *malloc(size_t); void free(void *);