From e40c71c10a5ca1c95c43b4cb7380306a6584d0db Mon Sep 17 00:00:00 2001 From: Gabor Horvath Date: Wed, 4 Mar 2015 17:59:34 +0000 Subject: [PATCH] [analyzer] Individual configuration options can be specified for checkers. Reviewed by: Anna Zaks Original patch by: Aleksei Sidorin Differential Revision: http://reviews.llvm.org/D7905 llvm-svn: 231266 --- .../StaticAnalyzer/Core/AnalyzerOptions.h | 98 ++++++++++++++++++- clang/lib/StaticAnalyzer/Checkers/Checkers.td | 6 +- .../StaticAnalyzer/Checkers/MallocChecker.cpp | 44 ++++----- .../StaticAnalyzer/Core/AnalyzerOptions.cpp | 59 +++++++++-- clang/test/Analysis/free.c | 2 +- clang/test/Analysis/malloc-annotations.c | 2 +- clang/test/Analysis/outofbound.c | 2 +- clang/test/Analysis/undef-buffers.c | 2 +- clang/unittests/CMakeLists.txt | 1 + clang/unittests/Makefile | 2 +- .../StaticAnalyzer/AnalyzerOptionsTest.cpp | 74 ++++++++++++++ clang/unittests/StaticAnalyzer/CMakeLists.txt | 13 +++ clang/unittests/StaticAnalyzer/Makefile | 15 +++ 13 files changed, 272 insertions(+), 48 deletions(-) create mode 100644 clang/unittests/StaticAnalyzer/AnalyzerOptionsTest.cpp create mode 100644 clang/unittests/StaticAnalyzer/CMakeLists.txt create mode 100644 clang/unittests/StaticAnalyzer/Makefile diff --git a/clang/include/clang/StaticAnalyzer/Core/AnalyzerOptions.h b/clang/include/clang/StaticAnalyzer/Core/AnalyzerOptions.h index fc9fc5ee7931..f02e48a4418b 100644 --- a/clang/include/clang/StaticAnalyzer/Core/AnalyzerOptions.h +++ b/clang/include/clang/StaticAnalyzer/Core/AnalyzerOptions.h @@ -28,6 +28,10 @@ class DiagnosticsEngine; class Preprocessor; class LangOptions; +namespace ento { +class CheckerBase; +} + /// Analysis - Set of available source code analyses. enum Analyses { #define ANALYSIS(NAME, CMDFLAG, DESC, SCOPE) NAME, @@ -252,18 +256,102 @@ private: /// \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. + /// @param [in] SearchInParents If set to true and the searched option was not + /// specified for the given checker the options for the parent packages will + /// be searched as well. The inner packages take precedence over the outer + /// ones. + /// @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, + bool SearchInParents = false); + public: - /// Interprets an option's string value as a boolean. + /// Interprets an option's string value as a boolean. The "true" string is + /// interpreted as true and the "false" string is interpreted as false. /// - /// 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 The optional checker parameter that can be used to restrict + /// the search to the options of this particular checker (and its parents + /// dependening on search mode). + /// @param [in] SearchInParents If set to true and the searched option was not + /// specified for the given checker the options for the parent packages will + /// be searched as well. The inner packages take precedence over the outer + /// ones. + bool getBooleanOption(StringRef Name, bool DefaultVal, + const ento::CheckerBase *C = nullptr, + bool SearchInParents = false); /// 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 The optional checker parameter that can be used to restrict + /// the search to the options of this particular checker (and its parents + /// dependening on search mode). + /// @param [in] SearchInParents If set to true and the searched option was not + /// specified for the given checker the options for the parent packages will + /// be searched as well. The inner packages take precedence over the outer + /// ones. + bool getBooleanOption(Optional &V, StringRef Name, bool DefaultVal, + const ento::CheckerBase *C = nullptr, + bool SearchInParents = false); /// 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 The optional checker parameter that can be used to restrict + /// the search to the options of this particular checker (and its parents + /// dependening on search mode). + /// @param [in] SearchInParents If set to true and the searched option was not + /// specified for the given checker the options for the parent packages will + /// be searched as well. The inner packages take precedence over the outer + /// ones. + int getOptionAsInteger(StringRef Name, int DefaultVal, + const ento::CheckerBase *C = nullptr, + bool SearchInParents = false); + + /// Query an option's string value. + /// + /// 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 The optional checker parameter that can be used to restrict + /// the search to the options of this particular checker (and its parents + /// dependening on search mode). + /// @param [in] SearchInParents If set to true and the searched option was not + /// specified for the given checker the options for the parent packages will + /// be searched as well. The inner packages take precedence over the outer + /// ones. + StringRef getOptionAsString(StringRef Name, StringRef DefaultVal, + const ento::CheckerBase *C = nullptr, + bool SearchInParents = false); /// \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 diff --git a/clang/lib/StaticAnalyzer/Checkers/Checkers.td b/clang/lib/StaticAnalyzer/Checkers/Checkers.td index ba5b4fa3c66c..d1d6ac277ffe 100644 --- a/clang/lib/StaticAnalyzer/Checkers/Checkers.td +++ b/clang/lib/StaticAnalyzer/Checkers/Checkers.td @@ -295,7 +295,7 @@ def UnixAPIChecker : Checker<"API">, 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 +315,6 @@ def ChrootChecker : Checker<"Chroot">, 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">; diff --git a/clang/lib/StaticAnalyzer/Checkers/MallocChecker.cpp b/clang/lib/StaticAnalyzer/Checkers/MallocChecker.cpp index c2ace7b35db6..d77af6a4fb2b 100644 --- a/clang/lib/StaticAnalyzer/Checkers/MallocChecker.cpp +++ b/clang/lib/StaticAnalyzer/Checkers/MallocChecker.cpp @@ -170,8 +170,7 @@ public: /// 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, @@ -184,6 +183,8 @@ public: MOK_Any }; + DefaultBool IsOptimistic; + DefaultBool ChecksEnabled[CK_NumCheckKinds]; CheckName CheckNames[CK_NumCheckKinds]; typedef llvm::SmallVector CKVecTy; @@ -584,7 +585,7 @@ bool MallocChecker::isCMemFunction(const FunctionDecl *FD, if (Family != AF_Malloc) return false; - if (ChecksEnabled[CK_MallocOptimistic] && FD->hasAttrs()) { + if (IsOptimistic && FD->hasAttrs()) { for (const auto *I : FD->specific_attrs()) { OwnershipAttr::OwnershipKind OwnKind = I->getOwnKind(); if(OwnKind == OwnershipAttr::Takes || OwnKind == OwnershipAttr::Holds) { @@ -791,8 +792,7 @@ void MallocChecker::checkPostStmt(const CallExpr *CE, CheckerContext &C) const { } } - 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()) @@ -1362,8 +1362,7 @@ MallocChecker::getCheckIfTracked(MallocChecker::CheckKind CK, case AF_IfNameIndex: case AF_Alloca: { // C checkers. - if (CK == CK_MallocOptimistic || - CK == CK_MallocPessimistic) { + if (CK == CK_MallocChecker) { return CK; } return Optional(); @@ -1516,8 +1515,7 @@ void MallocChecker::ReportBadFree(CheckerContext &C, SVal ArgVal, SourceRange Range, const Expr *DeallocExpr) const { - auto CheckKind = getCheckIfTracked(MakeVecFromCK(CK_MallocOptimistic, - CK_MallocPessimistic, + auto CheckKind = getCheckIfTracked(MakeVecFromCK(CK_MallocChecker, CK_NewDeleteChecker), C, DeallocExpr); if (!CheckKind.hasValue()) @@ -1559,8 +1557,7 @@ void MallocChecker::ReportBadFree(CheckerContext &C, SVal ArgVal, void MallocChecker::ReportFreeAlloca(CheckerContext &C, SVal ArgVal, SourceRange Range) const { - auto CheckKind = getCheckIfTracked(MakeVecFromCK(CK_MallocOptimistic, - CK_MallocPessimistic, + auto CheckKind = getCheckIfTracked(MakeVecFromCK(CK_MallocChecker, CK_MismatchedDeallocatorChecker), AF_Alloca); if (!CheckKind.hasValue()) @@ -1639,8 +1636,7 @@ void MallocChecker::ReportOffsetFree(CheckerContext &C, SVal ArgVal, const Expr *AllocExpr) const { - auto CheckKind = getCheckIfTracked(MakeVecFromCK(CK_MallocOptimistic, - CK_MallocPessimistic, + auto CheckKind = getCheckIfTracked(MakeVecFromCK(CK_MallocChecker, CK_NewDeleteChecker), C, AllocExpr); if (!CheckKind.hasValue()) @@ -1692,8 +1688,7 @@ void MallocChecker::ReportOffsetFree(CheckerContext &C, SVal ArgVal, void MallocChecker::ReportUseAfterFree(CheckerContext &C, SourceRange Range, SymbolRef Sym) const { - auto CheckKind = getCheckIfTracked(MakeVecFromCK(CK_MallocOptimistic, - CK_MallocPessimistic, + auto CheckKind = getCheckIfTracked(MakeVecFromCK(CK_MallocChecker, CK_NewDeleteChecker), C, Sym); if (!CheckKind.hasValue()) @@ -1718,8 +1713,7 @@ void MallocChecker::ReportDoubleFree(CheckerContext &C, SourceRange Range, bool Released, SymbolRef Sym, SymbolRef PrevSym) const { - auto CheckKind = getCheckIfTracked(MakeVecFromCK(CK_MallocOptimistic, - CK_MallocPessimistic, + auto CheckKind = getCheckIfTracked(MakeVecFromCK(CK_MallocChecker, CK_NewDeleteChecker), C, Sym); if (!CheckKind.hasValue()) @@ -1934,8 +1928,7 @@ MallocChecker::getAllocationSite(const ExplodedNode *N, SymbolRef Sym, void MallocChecker::reportLeak(SymbolRef Sym, ExplodedNode *N, CheckerContext &C) const { - auto CheckKind = getCheckIfTracked(MakeVecFromCK(CK_MallocOptimistic, - CK_MallocPessimistic, + auto CheckKind = getCheckIfTracked(MakeVecFromCK(CK_MallocChecker, CK_NewDeleteLeaksChecker), C, Sym); if (!CheckKind.hasValue()) @@ -2058,8 +2051,7 @@ void MallocChecker::checkPreCall(const CallEvent &Call, return; ASTContext &Ctx = C.getASTContext(); - if ((ChecksEnabled[CK_MallocOptimistic] || - ChecksEnabled[CK_MallocPessimistic]) && + if (ChecksEnabled[CK_MallocChecker] && (isCMemFunction(FD, Ctx, AF_Malloc, MemoryOperationKind::MOK_Free) || isCMemFunction(FD, Ctx, AF_IfNameIndex, MemoryOperationKind::MOK_Free))) @@ -2551,8 +2543,7 @@ void MallocChecker::printState(raw_ostream &Out, ProgramStateRef State, for (RegionStateTy::iterator I = RS.begin(), E = RS.end(); I != E; ++I) { const RefState *RefS = State->get(I.getKey()); AllocationFamily Family = RefS->getAllocationFamily(); - auto CheckKind = getCheckIfTracked(MakeVecFromCK(CK_MallocOptimistic, - CK_MallocPessimistic, + auto CheckKind = getCheckIfTracked(MakeVecFromCK(CK_MallocChecker, CK_NewDeleteChecker), Family); I.getKey()->dumpToStream(Out); @@ -2568,6 +2559,8 @@ void MallocChecker::printState(raw_ostream &Out, ProgramStateRef State, 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(); @@ -2581,11 +2574,12 @@ void ento::registerNewDeleteLeaksChecker(CheckerManager &mgr) { 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) diff --git a/clang/lib/StaticAnalyzer/Core/AnalyzerOptions.cpp b/clang/lib/StaticAnalyzer/Core/AnalyzerOptions.cpp index d717e3fe86d2..1696bcfac9c1 100644 --- a/clang/lib/StaticAnalyzer/Core/AnalyzerOptions.cpp +++ b/clang/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() { @@ -100,12 +102,37 @@ AnalyzerOptions::mayInlineCXXMemberFunction(CXXInlineableMemberKind K) { 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, + bool SearchInParents) { + // Search for a package option if the option for the checker is not specified + // and search in parents is enabled. + ConfigTable::const_iterator E = Config.end(); + do { + ConfigTable::const_iterator I = + Config.find((Twine(CheckerName) + ":" + OptionName).str()); + if (I != E) + return StringRef(I->getValue()); + size_t Pos = CheckerName.rfind('.'); + if (Pos == StringRef::npos) + return Default; + CheckerName = CheckerName.substr(0, Pos); + } while (!CheckerName.empty() && SearchInParents); + return Default; +} + +bool AnalyzerOptions::getBooleanOption(StringRef Name, bool DefaultVal, + const CheckerBase *C, + bool SearchInParents) { // 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 Default = toString(DefaultVal); StringRef V = - Config.insert(std::make_pair(Name, toString(DefaultVal))).first->second; + C ? getCheckerOption(C->getTagDescription(), Name, Default, + SearchInParents) + : StringRef(Config.insert(std::make_pair(Name, Default)).first->second); return llvm::StringSwitch(V) .Case("true", true) .Case("false", false) @@ -113,9 +140,10 @@ bool AnalyzerOptions::getBooleanOption(StringRef Name, bool DefaultVal) { } bool AnalyzerOptions::getBooleanOption(Optional &V, StringRef Name, - bool DefaultVal) { + bool DefaultVal, const CheckerBase *C, + bool SearchInParents) { if (!V.hasValue()) - V = getBooleanOption(Name, DefaultVal); + V = getBooleanOption(Name, DefaultVal, C, SearchInParents); return V.getValue(); } @@ -199,19 +227,35 @@ bool AnalyzerOptions::shouldWriteStableReportFilename() { /* Default = */ false); } -int AnalyzerOptions::getOptionAsInteger(StringRef Name, int DefaultVal) { +int AnalyzerOptions::getOptionAsInteger(StringRef Name, int DefaultVal, + const CheckerBase *C, + bool SearchInParents) { SmallString<10> StrBuf; llvm::raw_svector_ostream OS(StrBuf); OS << DefaultVal; - StringRef V = Config.insert(std::make_pair(Name, OS.str())).first->second; + StringRef V = C ? getCheckerOption(C->getTagDescription(), Name, OS.str(), + SearchInParents) + : StringRef(Config.insert(std::make_pair(Name, OS.str())) + .first->second); + int Res = DefaultVal; bool b = V.getAsInteger(10, Res); assert(!b && "analyzer-config option should be numeric"); - (void) b; + (void)b; return Res; } +StringRef AnalyzerOptions::getOptionAsString(StringRef Name, + StringRef DefaultVal, + const CheckerBase *C, + bool SearchInParents) { + return C ? getCheckerOption(C->getTagDescription(), Name, DefaultVal, + SearchInParents) + : StringRef( + Config.insert(std::make_pair(Name, DefaultVal)).first->second); +} + unsigned AnalyzerOptions::getAlwaysInlineSize() { if (!AlwaysInlineSize.hasValue()) AlwaysInlineSize = getOptionAsInteger("ipa-always-inline-size", 3); @@ -281,4 +325,3 @@ bool AnalyzerOptions::shouldPrunePaths() { bool AnalyzerOptions::shouldConditionalizeStaticInitializers() { return getBooleanOption("cfg-conditional-static-initializers", true); } - diff --git a/clang/test/Analysis/free.c b/clang/test/Analysis/free.c index 6f33732e0062..3746bf11c04e 100644 --- a/clang/test/Analysis/free.c +++ b/clang/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 typedef __typeof(sizeof(int)) size_t; void free(void *); void *alloca(size_t); diff --git a/clang/test/Analysis/malloc-annotations.c b/clang/test/Analysis/malloc-annotations.c index c197df4386e8..3119cb7e9744 100644 --- a/clang/test/Analysis/malloc-annotations.c +++ b/clang/test/Analysis/malloc-annotations.c @@ -1,4 +1,4 @@ -// 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.Malloc:Optimistic=true %s typedef __typeof(sizeof(int)) size_t; void *malloc(size_t); void free(void *); diff --git a/clang/test/Analysis/outofbound.c b/clang/test/Analysis/outofbound.c index 45786ec4537e..81ed7ac00b39 100644 --- a/clang/test/Analysis/outofbound.c +++ b/clang/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); diff --git a/clang/test/Analysis/undef-buffers.c b/clang/test/Analysis/undef-buffers.c index f18d6e59a4eb..1581b2b8cbb2 100644 --- a/clang/test/Analysis/undef-buffers.c +++ b/clang/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 *); diff --git a/clang/unittests/CMakeLists.txt b/clang/unittests/CMakeLists.txt index d0e2860e07f0..bc55cdf4bf92 100644 --- a/clang/unittests/CMakeLists.txt +++ b/clang/unittests/CMakeLists.txt @@ -13,6 +13,7 @@ add_subdirectory(Basic) add_subdirectory(Lex) add_subdirectory(Driver) if(CLANG_ENABLE_STATIC_ANALYZER) + add_subdirectory(StaticAnalyzer) add_subdirectory(Frontend) endif() add_subdirectory(ASTMatchers) diff --git a/clang/unittests/Makefile b/clang/unittests/Makefile index 1e6a50835b05..07c0d0e08715 100644 --- a/clang/unittests/Makefile +++ b/clang/unittests/Makefile @@ -20,7 +20,7 @@ PARALLEL_DIRS = CodeGen Basic Lex Driver Format ASTMatchers AST Tooling \ include $(CLANG_LEVEL)/../..//Makefile.config ifeq ($(ENABLE_CLANG_ARCMT),1) -PARALLEL_DIRS += Frontend libclang +PARALLEL_DIRS += Frontend libclang StaticAnalyzer endif endif # CLANG_LEVEL diff --git a/clang/unittests/StaticAnalyzer/AnalyzerOptionsTest.cpp b/clang/unittests/StaticAnalyzer/AnalyzerOptionsTest.cpp new file mode 100644 index 000000000000..33f1740bea8a --- /dev/null +++ b/clang/unittests/StaticAnalyzer/AnalyzerOptionsTest.cpp @@ -0,0 +1,74 @@ +//===- unittest/Analysis/AnalyzerOptionsTest.cpp - SA Options test --------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "clang/StaticAnalyzer/Core/AnalyzerOptions.h" +#include "clang/StaticAnalyzer/Core/Checker.h" +#include "gtest/gtest.h" + +namespace clang { +namespace ento { + +TEST(StaticAnalyzerOptions, SearchInParentPackageTests) { + AnalyzerOptions Opts; + Opts.Config["Outer.Inner.CheckerOne:Option"] = "true"; + Opts.Config["Outer.Inner:Option"] = "false"; + Opts.Config["Outer.Inner:Option2"] = "true"; + Opts.Config["Outer:Option2"] = "false"; + + struct CheckerOneMock : CheckerBase { + StringRef getTagDescription() const override { + return "Outer.Inner.CheckerOne"; + } + }; + struct CheckerTwoMock : CheckerBase { + StringRef getTagDescription() const override { + return "Outer.Inner.CheckerTwo"; + } + }; + + // Checker one has Option specified as true. It should read true regardless of + // search mode. + CheckerOneMock CheckerOne; + EXPECT_TRUE(Opts.getBooleanOption("Option", false, &CheckerOne)); + // The package option is overriden with a checker option. + EXPECT_TRUE(Opts.getBooleanOption("Option", false, &CheckerOne, true)); + // The Outer package option is overriden by the Inner package option. No + // package option is specified. + EXPECT_TRUE(Opts.getBooleanOption("Option2", false, &CheckerOne, true)); + // No package option is specified and search in packages is turned off. The + // default value should be returned. + EXPECT_FALSE(Opts.getBooleanOption("Option2", false, &CheckerOne)); + EXPECT_TRUE(Opts.getBooleanOption("Option2", true, &CheckerOne)); + + // Checker true has no option specified. It should get the default value when + // search in parents turned off and false when search in parents turned on. + CheckerTwoMock CheckerTwo; + EXPECT_FALSE(Opts.getBooleanOption("Option", false, &CheckerTwo)); + EXPECT_TRUE(Opts.getBooleanOption("Option", true, &CheckerTwo)); + EXPECT_FALSE(Opts.getBooleanOption("Option", true, &CheckerTwo, true)); +} + +TEST(StaticAnalyzerOptions, StringOptions) { + AnalyzerOptions Opts; + Opts.Config["Outer.Inner.CheckerOne:Option"] = "StringValue"; + + struct CheckerOneMock : CheckerBase { + StringRef getTagDescription() const override { + return "Outer.Inner.CheckerOne"; + } + }; + + CheckerOneMock CheckerOne; + EXPECT_TRUE("StringValue" == + Opts.getOptionAsString("Option", "DefaultValue", &CheckerOne)); + EXPECT_TRUE("DefaultValue" == + Opts.getOptionAsString("Option2", "DefaultValue", &CheckerOne)); +} +} // end namespace ento +} // end namespace clang diff --git a/clang/unittests/StaticAnalyzer/CMakeLists.txt b/clang/unittests/StaticAnalyzer/CMakeLists.txt new file mode 100644 index 000000000000..4aa5efba77a2 --- /dev/null +++ b/clang/unittests/StaticAnalyzer/CMakeLists.txt @@ -0,0 +1,13 @@ +set(LLVM_LINK_COMPONENTS + Support + ) + +add_clang_unittest(StaticAnalysisTests + AnalyzerOptionsTest.cpp + ) + +target_link_libraries(StaticAnalysisTests + clangBasic + clangAnalysis + clangStaticAnalyzerCore + ) diff --git a/clang/unittests/StaticAnalyzer/Makefile b/clang/unittests/StaticAnalyzer/Makefile new file mode 100644 index 000000000000..af85b711e5b3 --- /dev/null +++ b/clang/unittests/StaticAnalyzer/Makefile @@ -0,0 +1,15 @@ +##===- unittests/Basic/Makefile ----------------------------*- Makefile -*-===## +# +# The LLVM Compiler Infrastructure +# +# This file is distributed under the University of Illinois Open Source +# License. See LICENSE.TXT for details. +# +##===----------------------------------------------------------------------===## + +CLANG_LEVEL = ../.. +TESTNAME = StaticAnalysis +LINK_COMPONENTS := support mc +USEDLIBS = clangBasic.a clangAnalysis.a clangStaticAnalyzerCore.a + +include $(CLANG_LEVEL)/unittests/Makefile