This is how MallocChecker looks with options used.
http://reviews.llvm.org/D3967
Files:
include/clang/StaticAnalyzer/Core/AnalyzerOptions.h
lib/StaticAnalyzer/Checkers/Checkers.td
lib/StaticAnalyzer/Checkers/MallocChecker.cpp
lib/StaticAnalyzer/Core/AnalyzerOptions.cpp
test/Analysis/free.c
test/Analysis/malloc-annotations.c
test/Analysis/outofbound.c
test/Analysis/undef-buffers.c
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<unsigned> 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<bool> &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<bool> &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<OwnershipAttr>())
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<OwnershipAttr>())
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<MallocChecker::CheckKind>();
}
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<MallocChecker::CheckKind> 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<MallocChecker::CheckKind> 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<MallocChecker::CheckKind> 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<MallocChecker::CheckKind> 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<MallocChecker>();
+ 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<MallocChecker>(); \
+ 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<bool>(V)
.Case("true", true)
.Case("false", false)
@@ -110,9 +134,9 @@
}
bool AnalyzerOptions::getBooleanOption(Optional<bool> &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 *);
_______________________________________________
cfe-commits mailing list
[email protected]
http://lists.cs.uiuc.edu/mailman/listinfo/cfe-commits