Index: cfe/trunk/examples/analyzer-plugin/MainCallChecker.cpp =================================================================== --- cfe/trunk/examples/analyzer-plugin/MainCallChecker.cpp +++ cfe/trunk/examples/analyzer-plugin/MainCallChecker.cpp @@ -35,7 +35,7 @@ return; if (!BT) - BT.reset(new BugType("call to main", "example analyzer plugin")); + BT.reset(new BugType(this, "call to main", "example analyzer plugin")); BugReport *report = new BugReport(*BT, BT->getName(), N); report->addRange(Callee->getSourceRange()); Index: cfe/trunk/include/clang/StaticAnalyzer/Core/BugReporter/BugReporter.h =================================================================== --- cfe/trunk/include/clang/StaticAnalyzer/Core/BugReporter/BugReporter.h +++ cfe/trunk/include/clang/StaticAnalyzer/Core/BugReporter/BugReporter.h @@ -19,6 +19,7 @@ #include "clang/StaticAnalyzer/Core/AnalyzerOptions.h" #include "clang/StaticAnalyzer/Core/BugReporter/BugReporterVisitor.h" #include "clang/StaticAnalyzer/Core/BugReporter/PathDiagnostic.h" +#include "clang/StaticAnalyzer/Core/CheckerManager.h" #include "clang/StaticAnalyzer/Core/PathSensitive/ProgramState.h" #include "llvm/ADT/DenseSet.h" #include "llvm/ADT/FoldingSet.h" @@ -463,7 +464,12 @@ /// reports. void emitReport(BugReport *R); - void EmitBasicReport(const Decl *DeclWithIssue, + void EmitBasicReport(const Decl *DeclWithIssue, const CheckerBase *Checker, + StringRef BugName, StringRef BugCategory, + StringRef BugStr, PathDiagnosticLocation Loc, + ArrayRef Ranges = None); + + void EmitBasicReport(const Decl *DeclWithIssue, CheckName CheckName, StringRef BugName, StringRef BugCategory, StringRef BugStr, PathDiagnosticLocation Loc, ArrayRef Ranges = None); @@ -473,7 +479,8 @@ /// \brief Returns a BugType that is associated with the given name and /// category. - BugType *getBugTypeForName(StringRef name, StringRef category); + BugType *getBugTypeForName(CheckName CheckName, StringRef name, + StringRef category); }; // FIXME: Get rid of GRBugReporter. It's the wrong abstraction. Index: cfe/trunk/include/clang/StaticAnalyzer/Core/BugReporter/BugType.h =================================================================== --- cfe/trunk/include/clang/StaticAnalyzer/Core/BugReporter/BugType.h +++ cfe/trunk/include/clang/StaticAnalyzer/Core/BugReporter/BugType.h @@ -16,6 +16,7 @@ #include "clang/Basic/LLVM.h" #include "clang/StaticAnalyzer/Core/BugReporter/CommonBugCategories.h" +#include "clang/StaticAnalyzer/Core/Checker.h" #include "llvm/ADT/FoldingSet.h" #include @@ -29,20 +30,25 @@ class BugType { private: + const CheckName CheckName; const std::string Name; const std::string Category; bool SuppressonSink; virtual void anchor(); public: - BugType(StringRef name, StringRef cat) - : Name(name), Category(cat), SuppressonSink(false) {} + BugType(class CheckName Check, StringRef name, StringRef cat) + : CheckName(Check), Name(name), Category(cat), SuppressonSink(false) {} + BugType(const CheckerBase *checker, StringRef name, StringRef cat) + : CheckName(checker->getCheckName()), Name(name), Category(cat), + SuppressonSink(false) {} virtual ~BugType() {} // FIXME: Should these be made strings as well? StringRef getName() const { return Name; } StringRef getCategory() const { return Category; } - + StringRef getCheckName() const { return CheckName.getName(); } + /// isSuppressOnSink - Returns true if bug reports associated with this bug /// type should be suppressed if the end node of the report is post-dominated /// by a sink node. @@ -56,12 +62,17 @@ const std::string desc; virtual void anchor(); public: - BuiltinBug(const char *name, const char *description) - : BugType(name, categories::LogicError), desc(description) {} - - BuiltinBug(const char *name) - : BugType(name, categories::LogicError), desc(name) {} - + BuiltinBug(class CheckName CheckName, const char *name, + const char *description) + : BugType(CheckName, name, categories::LogicError), desc(description) {} + + BuiltinBug(const CheckerBase *checker, const char *name, + const char *description) + : BugType(checker, name, categories::LogicError), desc(description) {} + + BuiltinBug(const CheckerBase *checker, const char *name) + : BugType(checker, name, categories::LogicError), desc(name) {} + StringRef getDescription() const { return desc; } }; Index: cfe/trunk/include/clang/StaticAnalyzer/Core/BugReporter/PathDiagnostic.h =================================================================== --- cfe/trunk/include/clang/StaticAnalyzer/Core/BugReporter/PathDiagnostic.h +++ cfe/trunk/include/clang/StaticAnalyzer/Core/BugReporter/PathDiagnostic.h @@ -710,6 +710,7 @@ /// diagnostic. It represents an ordered-collection of PathDiagnosticPieces, /// each which represent the pieces of the path. class PathDiagnostic : public llvm::FoldingSetNode { + std::string CheckName; const Decl *DeclWithIssue; std::string BugType; std::string VerboseDesc; @@ -730,8 +731,8 @@ PathDiagnostic() LLVM_DELETED_FUNCTION; public: - PathDiagnostic(const Decl *DeclWithIssue, StringRef bugtype, - StringRef verboseDesc, StringRef shortDesc, + PathDiagnostic(StringRef CheckName, const Decl *DeclWithIssue, + StringRef bugtype, StringRef verboseDesc, StringRef shortDesc, StringRef category, PathDiagnosticLocation LocationToUnique, const Decl *DeclToUnique); @@ -788,6 +789,7 @@ StringRef getShortDescription() const { return ShortDesc.empty() ? VerboseDesc : ShortDesc; } + StringRef getCheckName() const { return CheckName; } StringRef getBugType() const { return BugType; } StringRef getCategory() const { return Category; } Index: cfe/trunk/include/clang/StaticAnalyzer/Core/Checker.h =================================================================== --- cfe/trunk/include/clang/StaticAnalyzer/Core/Checker.h +++ cfe/trunk/include/clang/StaticAnalyzer/Core/Checker.h @@ -453,14 +453,18 @@ } // end eval namespace class CheckerBase : public ProgramPointTag { + CheckName Name; + friend class ::clang::ento::CheckerManager; + public: StringRef getTagDescription() const; + CheckName getCheckName() const; /// See CheckerManager::runCheckersForPrintState. virtual void printState(raw_ostream &Out, ProgramStateRef State, const char *NL, const char *Sep) const { } }; - + template (ref); // already registered. CHECKER *checker = new CHECKER(); + checker->Name = CurrentCheckName; CheckerDtors.push_back(CheckerDtor(checker, destruct)); CHECKER::_register(checker, *this); ref = checker; @@ -182,6 +204,7 @@ return static_cast(ref); // already registered. CHECKER *checker = new CHECKER(AOpts); + checker->Name = CurrentCheckName; CheckerDtors.push_back(CheckerDtor(checker, destruct)); CHECKER::_register(checker, *this); ref = checker; Index: cfe/trunk/lib/StaticAnalyzer/Checkers/AnalyzerStatsChecker.cpp =================================================================== --- cfe/trunk/lib/StaticAnalyzer/Checkers/AnalyzerStatsChecker.cpp +++ cfe/trunk/lib/StaticAnalyzer/Checkers/AnalyzerStatsChecker.cpp @@ -112,7 +112,7 @@ << " | Empty WorkList: " << (Eng.hasEmptyWorkList() ? "yes" : "no"); - B.EmitBasicReport(D, "Analyzer Statistics", "Internal Statistics", + B.EmitBasicReport(D, this, "Analyzer Statistics", "Internal Statistics", output.str(), PathDiagnosticLocation(D, SM)); // Emit warning for each block we bailed out on. @@ -129,7 +129,7 @@ outputI << "(" << NameOfRootFunction << ")" << ": The analyzer generated a sink at this point"; B.EmitBasicReport( - D, "Sink Point", "Internal Statistics", outputI.str(), + D, this, "Sink Point", "Internal Statistics", outputI.str(), PathDiagnosticLocation::createBegin(CS->getStmt(), SM, LC)); } } Index: cfe/trunk/lib/StaticAnalyzer/Checkers/ArrayBoundChecker.cpp =================================================================== --- cfe/trunk/lib/StaticAnalyzer/Checkers/ArrayBoundChecker.cpp +++ cfe/trunk/lib/StaticAnalyzer/Checkers/ArrayBoundChecker.cpp @@ -66,8 +66,9 @@ return; if (!BT) - BT.reset(new BuiltinBug("Out-of-bound array access", - "Access out-of-bound array element (buffer overflow)")); + BT.reset(new BuiltinBug( + this, "Out-of-bound array access", + "Access out-of-bound array element (buffer overflow)")); // FIXME: It would be nice to eventually make this diagnostic more clear, // e.g., by referencing the original declaration or by saying *why* this Index: cfe/trunk/lib/StaticAnalyzer/Checkers/ArrayBoundCheckerV2.cpp =================================================================== --- cfe/trunk/lib/StaticAnalyzer/Checkers/ArrayBoundCheckerV2.cpp +++ cfe/trunk/lib/StaticAnalyzer/Checkers/ArrayBoundCheckerV2.cpp @@ -187,7 +187,7 @@ return; if (!BT) - BT.reset(new BuiltinBug("Out-of-bound access")); + BT.reset(new BuiltinBug(this, "Out-of-bound access")); // FIXME: This diagnostics are preliminary. We should get far better // diagnostics for explaining buffer overruns. @@ -311,7 +311,6 @@ return RegionRawOffsetV2(); } - void ento::registerArrayBoundCheckerV2(CheckerManager &mgr) { mgr.registerChecker(); } Index: cfe/trunk/lib/StaticAnalyzer/Checkers/BasicObjCFoundationChecks.cpp =================================================================== --- cfe/trunk/lib/StaticAnalyzer/Checkers/BasicObjCFoundationChecks.cpp +++ cfe/trunk/lib/StaticAnalyzer/Checkers/BasicObjCFoundationChecks.cpp @@ -39,7 +39,8 @@ namespace { class APIMisuse : public BugType { public: - APIMisuse(const char* name) : BugType(name, "API Misuse (Apple)") {} + APIMisuse(const CheckerBase *checker, const char *name) + : BugType(checker, name, "API Misuse (Apple)") {} }; } // end anonymous namespace @@ -191,7 +192,7 @@ const Expr *E, CheckerContext &C) const { if (!BT) - BT.reset(new APIMisuse("nil argument")); + BT.reset(new APIMisuse(this, "nil argument")); BugReport *R = new BugReport(*BT, Msg, N); R->addRange(Range); @@ -483,8 +484,8 @@ << " bits of the input integer will be lost."; if (!BT) - BT.reset(new APIMisuse("Bad use of CFNumberCreate")); - + BT.reset(new APIMisuse(this, "Bad use of CFNumberCreate")); + BugReport *report = new BugReport(*BT, os.str(), N); report->addRange(CE->getArg(2)->getSourceRange()); C.emitReport(report); @@ -522,8 +523,8 @@ Retain = &Ctx.Idents.get("CFRetain"); Release = &Ctx.Idents.get("CFRelease"); MakeCollectable = &Ctx.Idents.get("CFMakeCollectable"); - BT.reset( - new APIMisuse("null passed to CFRetain/CFRelease/CFMakeCollectable")); + BT.reset(new APIMisuse( + this, "null passed to CFRetain/CFRelease/CFMakeCollectable")); } // Check if we called CFRetain/CFRelease/CFMakeCollectable. @@ -600,9 +601,9 @@ CheckerContext &C) const { if (!BT) { - BT.reset(new APIMisuse("message incorrectly sent to class instead of class " - "instance")); - + BT.reset(new APIMisuse( + this, "message incorrectly sent to class instead of class instance")); + ASTContext &Ctx = C.getASTContext(); releaseS = GetNullarySelector("release", Ctx); retainS = GetNullarySelector("retain", Ctx); @@ -708,7 +709,8 @@ void VariadicMethodTypeChecker::checkPreObjCMessage(const ObjCMethodCall &msg, CheckerContext &C) const { if (!BT) { - BT.reset(new APIMisuse("Arguments passed to variadic method aren't all " + BT.reset(new APIMisuse(this, + "Arguments passed to variadic method aren't all " "Objective-C pointer types")); ASTContext &Ctx = C.getASTContext(); @@ -1263,6 +1265,7 @@ mgr.registerChecker(); } -void ento::registerObjCNonNilReturnValueChecker(CheckerManager &mgr) { +void +ento::registerObjCNonNilReturnValueChecker(CheckerManager &mgr) { mgr.registerChecker(); } Index: cfe/trunk/lib/StaticAnalyzer/Checkers/BoolAssignmentChecker.cpp =================================================================== --- cfe/trunk/lib/StaticAnalyzer/Checkers/BoolAssignmentChecker.cpp +++ cfe/trunk/lib/StaticAnalyzer/Checkers/BoolAssignmentChecker.cpp @@ -34,7 +34,7 @@ CheckerContext &C) const { if (ExplodedNode *N = C.addTransition(state)) { if (!BT) - BT.reset(new BuiltinBug("Assignment of a non-Boolean value")); + BT.reset(new BuiltinBug(this, "Assignment of a non-Boolean value")); C.emitReport(new BugReport(*BT, BT->getDescription(), N)); } } Index: cfe/trunk/lib/StaticAnalyzer/Checkers/CStringChecker.cpp =================================================================== --- cfe/trunk/lib/StaticAnalyzer/Checkers/CStringChecker.cpp +++ cfe/trunk/lib/StaticAnalyzer/Checkers/CStringChecker.cpp @@ -51,6 +51,11 @@ DefaultBool CheckCStringOutOfBounds; DefaultBool CheckCStringBufferOverlap; DefaultBool CheckCStringNotNullTerm; + + CheckName CheckNameCStringNullArg; + CheckName CheckNameCStringOutOfBounds; + CheckName CheckNameCStringBufferOverlap; + CheckName CheckNameCStringNotNullTerm; }; CStringChecksFilter Filter; @@ -232,8 +237,9 @@ return NULL; if (!BT_Null) - BT_Null.reset(new BuiltinBug(categories::UnixAPI, - "Null pointer argument in call to byte string function")); + BT_Null.reset(new BuiltinBug( + Filter.CheckNameCStringNullArg, categories::UnixAPI, + "Null pointer argument in call to byte string function")); SmallString<80> buf; llvm::raw_svector_ostream os(buf); @@ -294,8 +300,9 @@ return NULL; if (!BT_Bounds) { - BT_Bounds.reset(new BuiltinBug("Out-of-bound array access", - "Byte string function accesses out-of-bound array element")); + BT_Bounds.reset(new BuiltinBug( + Filter.CheckNameCStringOutOfBounds, "Out-of-bound array access", + "Byte string function accesses out-of-bound array element")); } BuiltinBug *BT = static_cast(BT_Bounds.get()); @@ -526,7 +533,8 @@ return; if (!BT_Overlap) - BT_Overlap.reset(new BugType(categories::UnixAPI, "Improper arguments")); + BT_Overlap.reset(new BugType(Filter.CheckNameCStringBufferOverlap, + categories::UnixAPI, "Improper arguments")); // Generate a report for this bug. BugReport *report = @@ -586,8 +594,9 @@ return NULL; if (!BT_AdditionOverflow) - BT_AdditionOverflow.reset(new BuiltinBug("API", - "Sum of expressions causes overflow")); + BT_AdditionOverflow.reset( + new BuiltinBug(Filter.CheckNameCStringOutOfBounds, "API", + "Sum of expressions causes overflow")); // This isn't a great error message, but this should never occur in real // code anyway -- you'd have to create a buffer longer than a size_t can @@ -703,8 +712,9 @@ if (ExplodedNode *N = C.addTransition(state)) { if (!BT_NotCString) - BT_NotCString.reset(new BuiltinBug(categories::UnixAPI, - "Argument is not a null-terminated string.")); + BT_NotCString.reset(new BuiltinBug( + Filter.CheckNameCStringNotNullTerm, categories::UnixAPI, + "Argument is not a null-terminated string.")); SmallString<120> buf; llvm::raw_svector_ostream os(buf); @@ -714,8 +724,7 @@ << "', which is not a null-terminated string"; // Generate a report for this bug. - BugReport *report = new BugReport(*BT_NotCString, - os.str(), N); + BugReport *report = new BugReport(*BT_NotCString, os.str(), N); report->addRange(Ex->getSourceRange()); C.emitReport(report); @@ -763,8 +772,9 @@ if (ExplodedNode *N = C.addTransition(state)) { if (!BT_NotCString) - BT_NotCString.reset(new BuiltinBug(categories::UnixAPI, - "Argument is not a null-terminated string.")); + BT_NotCString.reset(new BuiltinBug( + Filter.CheckNameCStringNotNullTerm, categories::UnixAPI, + "Argument is not a null-terminated string.")); SmallString<120> buf; llvm::raw_svector_ostream os(buf); @@ -2057,10 +2067,12 @@ C.addTransition(state); } -#define REGISTER_CHECKER(name) \ -void ento::register##name(CheckerManager &mgr) {\ - mgr.registerChecker()->Filter.Check##name = true; \ -} +#define REGISTER_CHECKER(name) \ + void ento::register##name(CheckerManager &mgr) { \ + CStringChecker *checker = mgr.registerChecker(); \ + checker->Filter.Check##name = true; \ + checker->Filter.CheckName##name = mgr.getCurrentCheckName(); \ + } REGISTER_CHECKER(CStringNullArg) REGISTER_CHECKER(CStringOutOfBounds) Index: cfe/trunk/lib/StaticAnalyzer/Checkers/CStringSyntaxChecker.cpp =================================================================== --- cfe/trunk/lib/StaticAnalyzer/Checkers/CStringSyntaxChecker.cpp +++ cfe/trunk/lib/StaticAnalyzer/Checkers/CStringSyntaxChecker.cpp @@ -31,6 +31,7 @@ namespace { class WalkAST: public StmtVisitor { + const CheckerBase *Checker; BugReporter &BR; AnalysisDeclContext* AC; @@ -81,9 +82,8 @@ bool containsBadStrncatPattern(const CallExpr *CE); public: - WalkAST(BugReporter &br, AnalysisDeclContext* ac) : - BR(br), AC(ac) { - } + WalkAST(const CheckerBase *checker, BugReporter &br, AnalysisDeclContext *ac) + : Checker(checker), BR(br), AC(ac) {} // Statement visitor methods. void VisitChildren(Stmt *S); @@ -157,8 +157,9 @@ os << "U"; os << "se a safer 'strlcat' API"; - BR.EmitBasicReport(FD, "Anti-pattern in the argument", "C String API", - os.str(), Loc, LenArg->getSourceRange()); + BR.EmitBasicReport(FD, Checker, "Anti-pattern in the argument", + "C String API", os.str(), Loc, + LenArg->getSourceRange()); } } @@ -179,7 +180,7 @@ void checkASTCodeBody(const Decl *D, AnalysisManager& Mgr, BugReporter &BR) const { - WalkAST walker(BR, Mgr.getAnalysisDeclContext(D)); + WalkAST walker(this, BR, Mgr.getAnalysisDeclContext(D)); walker.Visit(D->getBody()); } }; Index: cfe/trunk/lib/StaticAnalyzer/Checkers/CallAndMessageChecker.cpp =================================================================== --- cfe/trunk/lib/StaticAnalyzer/Checkers/CallAndMessageChecker.cpp +++ cfe/trunk/lib/StaticAnalyzer/Checkers/CallAndMessageChecker.cpp @@ -52,10 +52,10 @@ void checkPreCall(const CallEvent &Call, CheckerContext &C) const; private: - static bool PreVisitProcessArg(CheckerContext &C, SVal V, - SourceRange argRange, const Expr *argEx, - bool IsFirstArgument, bool checkUninitFields, - const CallEvent &Call, OwningPtr &BT); + bool PreVisitProcessArg(CheckerContext &C, SVal V, SourceRange argRange, + const Expr *argEx, bool IsFirstArgument, + bool checkUninitFields, const CallEvent &Call, + OwningPtr &BT) const; static void emitBadCall(BugType *BT, CheckerContext &C, const Expr *BadE); void emitNilReceiverBug(CheckerContext &C, const ObjCMethodCall &msg, @@ -65,9 +65,9 @@ ProgramStateRef state, const ObjCMethodCall &msg) const; - static void LazyInit_BT(const char *desc, OwningPtr &BT) { + void LazyInit_BT(const char *desc, OwningPtr &BT) const { if (!BT) - BT.reset(new BuiltinBug(desc)); + BT.reset(new BuiltinBug(this, desc)); } }; } // end anonymous namespace @@ -119,7 +119,7 @@ bool IsFirstArgument, bool checkUninitFields, const CallEvent &Call, - OwningPtr &BT) { + OwningPtr &BT) const { if (V.isUndef()) { if (ExplodedNode *N = C.generateSink()) { LazyInit_BT("Uninitialized argument value", BT); @@ -234,8 +234,8 @@ if (L.isUndef()) { if (!BT_call_undef) - BT_call_undef.reset(new BuiltinBug("Called function pointer is an " - "uninitalized pointer value")); + BT_call_undef.reset(new BuiltinBug( + this, "Called function pointer is an uninitalized pointer value")); emitBadCall(BT_call_undef.get(), C, Callee); return; } @@ -246,8 +246,8 @@ if (StNull && !StNonNull) { if (!BT_call_null) - BT_call_null.reset( - new BuiltinBug("Called function pointer is null (null dereference)")); + BT_call_null.reset(new BuiltinBug( + this, "Called function pointer is null (null dereference)")); emitBadCall(BT_call_null.get(), C, Callee); return; } @@ -265,7 +265,8 @@ if (!N) return; if (!BT_cxx_delete_undef) - BT_cxx_delete_undef.reset(new BuiltinBug("Uninitialized argument value")); + BT_cxx_delete_undef.reset( + new BuiltinBug(this, "Uninitialized argument value")); if (DE->isArrayFormAsWritten()) Desc = "Argument to 'delete[]' is uninitialized"; else @@ -289,8 +290,8 @@ SVal V = CC->getCXXThisVal(); if (V.isUndef()) { if (!BT_cxx_call_undef) - BT_cxx_call_undef.reset(new BuiltinBug("Called C++ object pointer is " - "uninitialized")); + BT_cxx_call_undef.reset( + new BuiltinBug(this, "Called C++ object pointer is uninitialized")); emitBadCall(BT_cxx_call_undef.get(), C, CC->getCXXThisExpr()); return; } @@ -301,8 +302,8 @@ if (StNull && !StNonNull) { if (!BT_cxx_call_null) - BT_cxx_call_null.reset(new BuiltinBug("Called C++ object pointer " - "is null")); + BT_cxx_call_null.reset( + new BuiltinBug(this, "Called C++ object pointer is null")); emitBadCall(BT_cxx_call_null.get(), C, CC->getCXXThisExpr()); return; } @@ -365,22 +366,21 @@ switch (msg.getMessageKind()) { case OCM_Message: if (!BT_msg_undef) - BT_msg_undef.reset(new BuiltinBug("Receiver in message expression " + BT_msg_undef.reset(new BuiltinBug(this, + "Receiver in message expression " "is an uninitialized value")); BT = BT_msg_undef.get(); break; case OCM_PropertyAccess: if (!BT_objc_prop_undef) - BT_objc_prop_undef.reset(new BuiltinBug("Property access on an " - "uninitialized object " - "pointer")); + BT_objc_prop_undef.reset(new BuiltinBug( + this, "Property access on an uninitialized object pointer")); BT = BT_objc_prop_undef.get(); break; case OCM_Subscript: if (!BT_objc_subscript_undef) - BT_objc_subscript_undef.reset(new BuiltinBug("Subscript access on an " - "uninitialized object " - "pointer")); + BT_objc_subscript_undef.reset(new BuiltinBug( + this, "Subscript access on an uninitialized object pointer")); BT = BT_objc_subscript_undef.get(); break; } @@ -418,7 +418,7 @@ if (!BT_msg_ret) BT_msg_ret.reset( - new BuiltinBug("Receiver in message expression is 'nil'")); + new BuiltinBug(this, "Receiver in message expression is 'nil'")); const ObjCMessageExpr *ME = msg.getOriginExpr(); Index: cfe/trunk/lib/StaticAnalyzer/Checkers/CastSizeChecker.cpp =================================================================== --- cfe/trunk/lib/StaticAnalyzer/Checkers/CastSizeChecker.cpp +++ cfe/trunk/lib/StaticAnalyzer/Checkers/CastSizeChecker.cpp @@ -69,9 +69,10 @@ if (regionSize % typeSize != 0) { if (ExplodedNode *errorNode = C.generateSink()) { if (!BT) - BT.reset(new BuiltinBug("Cast region with wrong size.", - "Cast a region whose size is not a multiple of the" - " destination type size.")); + BT.reset( + new BuiltinBug(this, "Cast region with wrong size.", + "Cast a region whose size is not a multiple of the" + " destination type size.")); BugReport *R = new BugReport(*BT, BT->getDescription(), errorNode); R->addRange(CE->getSourceRange()); @@ -80,7 +81,6 @@ } } - void ento::registerCastSizeChecker(CheckerManager &mgr) { - mgr.registerChecker(); + mgr.registerChecker(); } Index: cfe/trunk/lib/StaticAnalyzer/Checkers/CastToStructChecker.cpp =================================================================== --- cfe/trunk/lib/StaticAnalyzer/Checkers/CastToStructChecker.cpp +++ cfe/trunk/lib/StaticAnalyzer/Checkers/CastToStructChecker.cpp @@ -58,10 +58,11 @@ if (!OrigPointeeTy->isRecordType()) { if (ExplodedNode *N = C.addTransition()) { if (!BT) - BT.reset(new BuiltinBug("Cast from non-struct type to struct type", - "Casting a non-structure type to a structure type " - "and accessing a field can lead to memory access " - "errors or data corruption.")); + BT.reset( + new BuiltinBug(this, "Cast from non-struct type to struct type", + "Casting a non-structure type to a structure type " + "and accessing a field can lead to memory access " + "errors or data corruption.")); BugReport *R = new BugReport(*BT,BT->getDescription(), N); R->addRange(CE->getSourceRange()); C.emitReport(R); Index: cfe/trunk/lib/StaticAnalyzer/Checkers/CheckObjCDealloc.cpp =================================================================== --- cfe/trunk/lib/StaticAnalyzer/Checkers/CheckObjCDealloc.cpp +++ cfe/trunk/lib/StaticAnalyzer/Checkers/CheckObjCDealloc.cpp @@ -97,8 +97,9 @@ return false; } -static void checkObjCDealloc(const ObjCImplementationDecl *D, - const LangOptions& LOpts, BugReporter& BR) { +static void checkObjCDealloc(const CheckerBase *Checker, + const ObjCImplementationDecl *D, + const LangOptions &LOpts, BugReporter &BR) { assert (LOpts.getGC() != LangOptions::GCOnly); @@ -180,7 +181,7 @@ llvm::raw_string_ostream os(buf); os << "Objective-C class '" << *D << "' lacks a 'dealloc' instance method"; - BR.EmitBasicReport(D, name, categories::CoreFoundationObjectiveC, + BR.EmitBasicReport(D, Checker, name, categories::CoreFoundationObjectiveC, os.str(), DLoc); return; } @@ -198,7 +199,7 @@ << "' does not send a 'dealloc' message to its super class" " (missing [super dealloc])"; - BR.EmitBasicReport(MD, name, categories::CoreFoundationObjectiveC, + BR.EmitBasicReport(MD, Checker, name, categories::CoreFoundationObjectiveC, os.str(), DLoc); return; } @@ -264,8 +265,8 @@ PathDiagnosticLocation SDLoc = PathDiagnosticLocation::createBegin(*I, BR.getSourceManager()); - BR.EmitBasicReport(MD, name, categories::CoreFoundationObjectiveC, - os.str(), SDLoc); + BR.EmitBasicReport(MD, Checker, name, + categories::CoreFoundationObjectiveC, os.str(), SDLoc); } } } @@ -282,7 +283,8 @@ BugReporter &BR) const { if (mgr.getLangOpts().getGC() == LangOptions::GCOnly) return; - checkObjCDealloc(cast(D), mgr.getLangOpts(), BR); + checkObjCDealloc(this, cast(D), mgr.getLangOpts(), + BR); } }; } Index: cfe/trunk/lib/StaticAnalyzer/Checkers/CheckObjCInstMethSignature.cpp =================================================================== --- cfe/trunk/lib/StaticAnalyzer/Checkers/CheckObjCInstMethSignature.cpp +++ cfe/trunk/lib/StaticAnalyzer/Checkers/CheckObjCInstMethSignature.cpp @@ -40,7 +40,8 @@ static void CompareReturnTypes(const ObjCMethodDecl *MethDerived, const ObjCMethodDecl *MethAncestor, BugReporter &BR, ASTContext &Ctx, - const ObjCImplementationDecl *ID) { + const ObjCImplementationDecl *ID, + const CheckerBase *Checker) { QualType ResDerived = MethDerived->getReturnType(); QualType ResAncestor = MethAncestor->getReturnType(); @@ -69,15 +70,15 @@ PathDiagnosticLocation::createBegin(MethDerived, BR.getSourceManager()); - BR.EmitBasicReport(MethDerived, - "Incompatible instance method return type", - categories::CoreFoundationObjectiveC, - os.str(), MethDLoc); + BR.EmitBasicReport( + MethDerived, Checker, "Incompatible instance method return type", + categories::CoreFoundationObjectiveC, os.str(), MethDLoc); } } static void CheckObjCInstMethSignature(const ObjCImplementationDecl *ID, - BugReporter& BR) { + BugReporter &BR, + const CheckerBase *Checker) { const ObjCInterfaceDecl *D = ID->getClassInterface(); const ObjCInterfaceDecl *C = D->getSuperClass(); @@ -118,7 +119,7 @@ ObjCMethodDecl *MethDerived = MI->second; MI->second = 0; - CompareReturnTypes(MethDerived, M, BR, Ctx, ID); + CompareReturnTypes(MethDerived, M, BR, Ctx, ID, Checker); } C = C->getSuperClass(); @@ -135,7 +136,7 @@ public: void checkASTDecl(const ObjCImplementationDecl *D, AnalysisManager& mgr, BugReporter &BR) const { - CheckObjCInstMethSignature(D, BR); + CheckObjCInstMethSignature(D, BR, this); } }; } Index: cfe/trunk/lib/StaticAnalyzer/Checkers/CheckSecuritySyntaxOnly.cpp =================================================================== --- cfe/trunk/lib/StaticAnalyzer/Checkers/CheckSecuritySyntaxOnly.cpp +++ cfe/trunk/lib/StaticAnalyzer/Checkers/CheckSecuritySyntaxOnly.cpp @@ -46,8 +46,18 @@ DefaultBool check_vfork; DefaultBool check_FloatLoopCounter; DefaultBool check_UncheckedReturn; + + CheckName checkName_gets; + CheckName checkName_getpw; + CheckName checkName_mktemp; + CheckName checkName_mkstemp; + CheckName checkName_strcpy; + CheckName checkName_rand; + CheckName checkName_vfork; + CheckName checkName_FloatLoopCounter; + CheckName checkName_UncheckedReturn; }; - + class WalkAST : public StmtVisitor { BugReporter &BR; AnalysisDeclContext* AC; @@ -281,7 +291,7 @@ PathDiagnosticLocation FSLoc = PathDiagnosticLocation::createBegin(FS, BR.getSourceManager(), AC); - BR.EmitBasicReport(AC->getDecl(), + BR.EmitBasicReport(AC->getDecl(), filter.checkName_FloatLoopCounter, bugType, "Security", os.str(), FSLoc, ranges); } @@ -316,7 +326,7 @@ // Issue a warning. PathDiagnosticLocation CELoc = PathDiagnosticLocation::createBegin(CE, BR.getSourceManager(), AC); - BR.EmitBasicReport(AC->getDecl(), + BR.EmitBasicReport(AC->getDecl(), filter.checkName_gets, "Potential buffer overflow in call to 'gets'", "Security", "Call to function 'gets' is extremely insecure as it can " @@ -356,7 +366,7 @@ // Issue a warning. PathDiagnosticLocation CELoc = PathDiagnosticLocation::createBegin(CE, BR.getSourceManager(), AC); - BR.EmitBasicReport(AC->getDecl(), + BR.EmitBasicReport(AC->getDecl(), filter.checkName_getpw, "Potential buffer overflow in call to 'getpw'", "Security", "The getpw() function is dangerous as it may overflow the " @@ -397,7 +407,7 @@ // Issue a waring. PathDiagnosticLocation CELoc = PathDiagnosticLocation::createBegin(CE, BR.getSourceManager(), AC); - BR.EmitBasicReport(AC->getDecl(), + BR.EmitBasicReport(AC->getDecl(), filter.checkName_mktemp, "Potential insecure temporary file in call 'mktemp'", "Security", "Call to function 'mktemp' is insecure as it always " @@ -483,7 +493,7 @@ out << " used as a suffix"; } out << ')'; - BR.EmitBasicReport(AC->getDecl(), + BR.EmitBasicReport(AC->getDecl(), filter.checkName_mkstemp, "Insecure temporary file creation", "Security", out.str(), CELoc, strArg->getSourceRange()); } @@ -504,7 +514,7 @@ // Issue a warning. PathDiagnosticLocation CELoc = PathDiagnosticLocation::createBegin(CE, BR.getSourceManager(), AC); - BR.EmitBasicReport(AC->getDecl(), + BR.EmitBasicReport(AC->getDecl(), filter.checkName_strcpy, "Potential insecure memory buffer bounds restriction in " "call 'strcpy'", "Security", @@ -531,7 +541,7 @@ // Issue a warning. PathDiagnosticLocation CELoc = PathDiagnosticLocation::createBegin(CE, BR.getSourceManager(), AC); - BR.EmitBasicReport(AC->getDecl(), + BR.EmitBasicReport(AC->getDecl(), filter.checkName_strcpy, "Potential insecure memory buffer bounds restriction in " "call 'strcat'", "Security", @@ -609,8 +619,9 @@ PathDiagnosticLocation CELoc = PathDiagnosticLocation::createBegin(CE, BR.getSourceManager(), AC); - BR.EmitBasicReport(AC->getDecl(), os1.str(), "Security", os2.str(), - CELoc, CE->getCallee()->getSourceRange()); + BR.EmitBasicReport(AC->getDecl(), filter.checkName_rand, os1.str(), + "Security", os2.str(), CELoc, + CE->getCallee()->getSourceRange()); } //===----------------------------------------------------------------------===// @@ -633,7 +644,7 @@ // Issue a warning. PathDiagnosticLocation CELoc = PathDiagnosticLocation::createBegin(CE, BR.getSourceManager(), AC); - BR.EmitBasicReport(AC->getDecl(), + BR.EmitBasicReport(AC->getDecl(), filter.checkName_rand, "'random' is not a secure random number generator", "Security", "The 'random' function produces a sequence of values that " @@ -653,7 +664,7 @@ // All calls to vfork() are insecure, issue a warning. PathDiagnosticLocation CELoc = PathDiagnosticLocation::createBegin(CE, BR.getSourceManager(), AC); - BR.EmitBasicReport(AC->getDecl(), + BR.EmitBasicReport(AC->getDecl(), filter.checkName_vfork, "Potential insecure implementation-specific behavior in " "call 'vfork'", "Security", @@ -724,8 +735,9 @@ PathDiagnosticLocation CELoc = PathDiagnosticLocation::createBegin(CE, BR.getSourceManager(), AC); - BR.EmitBasicReport(AC->getDecl(), os1.str(), "Security", os2.str(), - CELoc, CE->getCallee()->getSourceRange()); + BR.EmitBasicReport(AC->getDecl(), filter.checkName_UncheckedReturn, os1.str(), + "Security", os2.str(), CELoc, + CE->getCallee()->getSourceRange()); } //===----------------------------------------------------------------------===// @@ -745,10 +757,13 @@ }; } -#define REGISTER_CHECKER(name) \ -void ento::register##name(CheckerManager &mgr) {\ - mgr.registerChecker()->filter.check_##name = true;\ -} +#define REGISTER_CHECKER(name) \ + void ento::register##name(CheckerManager &mgr) { \ + SecuritySyntaxChecker *checker = \ + mgr.registerChecker(); \ + checker->filter.check_##name = true; \ + checker->filter.checkName_##name = mgr.getCurrentCheckName(); \ + } REGISTER_CHECKER(gets) REGISTER_CHECKER(getpw) Index: cfe/trunk/lib/StaticAnalyzer/Checkers/CheckSizeofPointer.cpp =================================================================== --- cfe/trunk/lib/StaticAnalyzer/Checkers/CheckSizeofPointer.cpp +++ cfe/trunk/lib/StaticAnalyzer/Checkers/CheckSizeofPointer.cpp @@ -24,10 +24,12 @@ namespace { class WalkAST : public StmtVisitor { BugReporter &BR; + const CheckerBase *Checker; AnalysisDeclContext* AC; public: - WalkAST(BugReporter &br, AnalysisDeclContext* ac) : BR(br), AC(ac) {} + WalkAST(BugReporter &br, const CheckerBase *checker, AnalysisDeclContext *ac) + : BR(br), Checker(checker), AC(ac) {} void VisitUnaryExprOrTypeTraitExpr(UnaryExprOrTypeTraitExpr *E); void VisitStmt(Stmt *S) { VisitChildren(S); } void VisitChildren(Stmt *S); @@ -62,7 +64,7 @@ PathDiagnosticLocation ELoc = PathDiagnosticLocation::createBegin(E, BR.getSourceManager(), AC); - BR.EmitBasicReport(AC->getDecl(), + BR.EmitBasicReport(AC->getDecl(), Checker, "Potential unintended use of sizeof() on pointer type", categories::LogicError, "The code calls sizeof() on a pointer type. " @@ -80,7 +82,7 @@ public: void checkASTCodeBody(const Decl *D, AnalysisManager& mgr, BugReporter &BR) const { - WalkAST walker(BR, mgr.getAnalysisDeclContext(D)); + WalkAST walker(BR, this, mgr.getAnalysisDeclContext(D)); walker.Visit(D->getBody()); } }; Index: cfe/trunk/lib/StaticAnalyzer/Checkers/ChrootChecker.cpp =================================================================== --- cfe/trunk/lib/StaticAnalyzer/Checkers/ChrootChecker.cpp +++ cfe/trunk/lib/StaticAnalyzer/Checkers/ChrootChecker.cpp @@ -142,9 +142,9 @@ if (isRootChanged((intptr_t) *k)) if (ExplodedNode *N = C.addTransition()) { if (!BT_BreakJail) - BT_BreakJail.reset(new BuiltinBug("Break out of jail", - "No call of chdir(\"/\") immediately " - "after chroot")); + BT_BreakJail.reset(new BuiltinBug( + this, "Break out of jail", "No call of chdir(\"/\") immediately " + "after chroot")); BugReport *R = new BugReport(*BT_BreakJail, BT_BreakJail->getDescription(), N); C.emitReport(R); Index: cfe/trunk/lib/StaticAnalyzer/Checkers/DeadStoresChecker.cpp =================================================================== --- cfe/trunk/lib/StaticAnalyzer/Checkers/DeadStoresChecker.cpp +++ cfe/trunk/lib/StaticAnalyzer/Checkers/DeadStoresChecker.cpp @@ -124,6 +124,7 @@ const CFG &cfg; ASTContext &Ctx; BugReporter& BR; + const CheckerBase *Checker; AnalysisDeclContext* AC; ParentMap& Parents; llvm::SmallPtrSet Escaped; @@ -134,11 +135,12 @@ enum DeadStoreKind { Standard, Enclosing, DeadIncrement, DeadInit }; public: - DeadStoreObs(const CFG &cfg, ASTContext &ctx, - BugReporter& br, AnalysisDeclContext* ac, ParentMap& parents, - llvm::SmallPtrSet &escaped) - : cfg(cfg), Ctx(ctx), BR(br), AC(ac), Parents(parents), - Escaped(escaped), currentBlock(0) {} + DeadStoreObs(const CFG &cfg, ASTContext &ctx, BugReporter &br, + const CheckerBase *checker, AnalysisDeclContext *ac, + ParentMap &parents, + llvm::SmallPtrSet &escaped) + : cfg(cfg), Ctx(ctx), BR(br), Checker(checker), AC(ac), Parents(parents), + Escaped(escaped), currentBlock(0) {} virtual ~DeadStoreObs() {} @@ -199,7 +201,8 @@ return; } - BR.EmitBasicReport(AC->getDecl(), BugType, "Dead store", os.str(), L, R); + BR.EmitBasicReport(AC->getDecl(), Checker, BugType, "Dead store", os.str(), + L, R); } void CheckVarDecl(const VarDecl *VD, const Expr *Ex, const Expr *Val, @@ -439,7 +442,7 @@ ParentMap &pmap = mgr.getParentMap(D); FindEscaped FS; cfg.VisitBlockStmts(FS); - DeadStoreObs A(cfg, BR.getContext(), BR, AC, pmap, FS.Escaped); + DeadStoreObs A(cfg, BR.getContext(), BR, this, AC, pmap, FS.Escaped); L->runOnAllBlocks(A); } } Index: cfe/trunk/lib/StaticAnalyzer/Checkers/DereferenceChecker.cpp =================================================================== --- cfe/trunk/lib/StaticAnalyzer/Checkers/DereferenceChecker.cpp +++ cfe/trunk/lib/StaticAnalyzer/Checkers/DereferenceChecker.cpp @@ -97,7 +97,7 @@ // We know that 'location' cannot be non-null. This is what // we call an "explicit" null dereference. if (!BT_null) - BT_null.reset(new BuiltinBug("Dereference of null pointer")); + BT_null.reset(new BuiltinBug(this, "Dereference of null pointer")); SmallString<100> buf; llvm::raw_svector_ostream os(buf); @@ -180,7 +180,8 @@ if (l.isUndef()) { if (ExplodedNode *N = C.generateSink()) { if (!BT_undef) - BT_undef.reset(new BuiltinBug("Dereference of undefined pointer value")); + BT_undef.reset( + new BuiltinBug(this, "Dereference of undefined pointer value")); BugReport *report = new BugReport(*BT_undef, BT_undef->getDescription(), N); Index: cfe/trunk/lib/StaticAnalyzer/Checkers/DirectIvarAssignment.cpp =================================================================== --- cfe/trunk/lib/StaticAnalyzer/Checkers/DirectIvarAssignment.cpp +++ cfe/trunk/lib/StaticAnalyzer/Checkers/DirectIvarAssignment.cpp @@ -63,13 +63,15 @@ const ObjCMethodDecl *MD; const ObjCInterfaceDecl *InterfD; BugReporter &BR; + const CheckerBase *Checker; LocationOrAnalysisDeclContext DCtx; public: MethodCrawler(const IvarToPropertyMapTy &InMap, const ObjCMethodDecl *InMD, - const ObjCInterfaceDecl *InID, - BugReporter &InBR, AnalysisDeclContext *InDCtx) - : IvarToPropMap(InMap), MD(InMD), InterfD(InID), BR(InBR), DCtx(InDCtx) {} + const ObjCInterfaceDecl *InID, BugReporter &InBR, + const CheckerBase *Checker, AnalysisDeclContext *InDCtx) + : IvarToPropMap(InMap), MD(InMD), InterfD(InID), BR(InBR), + Checker(Checker), DCtx(InDCtx) {} void VisitStmt(const Stmt *S) { VisitChildren(S); } @@ -152,7 +154,8 @@ const Stmt *Body = M->getBody(); assert(Body); - MethodCrawler MC(IvarToPropMap, M->getCanonicalDecl(), InterD, BR, DCtx); + MethodCrawler MC(IvarToPropMap, M->getCanonicalDecl(), InterD, BR, this, + DCtx); MC.VisitStmt(Body); } } @@ -204,13 +207,11 @@ if (GetterMethod && GetterMethod->getCanonicalDecl() == MD) return; - BR.EmitBasicReport(MD, - "Property access", - categories::CoreFoundationObjectiveC, + BR.EmitBasicReport( + MD, Checker, "Property access", categories::CoreFoundationObjectiveC, "Direct assignment to an instance variable backing a property; " - "use the setter instead", PathDiagnosticLocation(IvarRef, - BR.getSourceManager(), - DCtx)); + "use the setter instead", + PathDiagnosticLocation(IvarRef, BR.getSourceManager(), DCtx)); } } } Index: cfe/trunk/lib/StaticAnalyzer/Checkers/DivZeroChecker.cpp =================================================================== --- cfe/trunk/lib/StaticAnalyzer/Checkers/DivZeroChecker.cpp +++ cfe/trunk/lib/StaticAnalyzer/Checkers/DivZeroChecker.cpp @@ -37,7 +37,7 @@ CheckerContext &C) const { if (ExplodedNode *N = C.generateSink(StateZero)) { if (!BT) - BT.reset(new BuiltinBug("Division by zero")); + BT.reset(new BuiltinBug(this, "Division by zero")); BugReport *R = new BugReport(*BT, Msg, N); bugreporter::trackNullOrUndefValue(N, bugreporter::GetDenomExpr(N), *R); Index: cfe/trunk/lib/StaticAnalyzer/Checkers/ExprInspectionChecker.cpp =================================================================== --- cfe/trunk/lib/StaticAnalyzer/Checkers/ExprInspectionChecker.cpp +++ cfe/trunk/lib/StaticAnalyzer/Checkers/ExprInspectionChecker.cpp @@ -95,7 +95,7 @@ return; if (!BT) - BT.reset(new BugType("Checking analyzer assumptions", "debug")); + BT.reset(new BugType(this, "Checking analyzer assumptions", "debug")); BugReport *R = new BugReport(*BT, getArgumentValueString(CE, C), N); C.emitReport(R); @@ -106,7 +106,7 @@ ExplodedNode *N = C.getPredecessor(); if (!BT) - BT.reset(new BugType("Checking analyzer assumptions", "debug")); + BT.reset(new BugType(this, "Checking analyzer assumptions", "debug")); BugReport *R = new BugReport(*BT, "REACHABLE", N); C.emitReport(R); @@ -126,7 +126,7 @@ return; if (!BT) - BT.reset(new BugType("Checking analyzer assumptions", "debug")); + BT.reset(new BugType(this, "Checking analyzer assumptions", "debug")); BugReport *R = new BugReport(*BT, getArgumentValueString(CE, C), N); C.emitReport(R); Index: cfe/trunk/lib/StaticAnalyzer/Checkers/FixedAddressChecker.cpp =================================================================== --- cfe/trunk/lib/StaticAnalyzer/Checkers/FixedAddressChecker.cpp +++ cfe/trunk/lib/StaticAnalyzer/Checkers/FixedAddressChecker.cpp @@ -52,10 +52,11 @@ if (ExplodedNode *N = C.addTransition()) { if (!BT) - BT.reset(new BuiltinBug("Use fixed address", - "Using a fixed address is not portable because that " - "address will probably not be valid in all " - "environments or platforms.")); + BT.reset( + new BuiltinBug(this, "Use fixed address", + "Using a fixed address is not portable because that " + "address will probably not be valid in all " + "environments or platforms.")); BugReport *R = new BugReport(*BT, BT->getDescription(), N); R->addRange(B->getRHS()->getSourceRange()); C.emitReport(R); Index: cfe/trunk/lib/StaticAnalyzer/Checkers/GenericTaintChecker.cpp =================================================================== --- cfe/trunk/lib/StaticAnalyzer/Checkers/GenericTaintChecker.cpp +++ cfe/trunk/lib/StaticAnalyzer/Checkers/GenericTaintChecker.cpp @@ -46,7 +46,7 @@ mutable OwningPtr BT; inline void initBugType() const { if (!BT) - BT.reset(new BugType("Use of Untrusted Data", "Untrusted Data")); + BT.reset(new BugType(this, "Use of Untrusted Data", "Untrusted Data")); } /// \brief Catch taint related bugs. Check if tainted data is passed to a Index: cfe/trunk/lib/StaticAnalyzer/Checkers/IdenticalExprChecker.cpp =================================================================== --- cfe/trunk/lib/StaticAnalyzer/Checkers/IdenticalExprChecker.cpp +++ cfe/trunk/lib/StaticAnalyzer/Checkers/IdenticalExprChecker.cpp @@ -36,8 +36,10 @@ class FindIdenticalExprVisitor : public RecursiveASTVisitor { public: - explicit FindIdenticalExprVisitor(BugReporter &B, AnalysisDeclContext *A) - : BR(B), AC(A) {} + explicit FindIdenticalExprVisitor(BugReporter &B, + const CheckerBase *Checker, + AnalysisDeclContext *A) + : BR(B), Checker(Checker), AC(A) {} // FindIdenticalExprVisitor only visits nodes // that are binary operators or conditional operators. bool VisitBinaryOperator(const BinaryOperator *B); @@ -45,6 +47,7 @@ private: BugReporter &BR; + const CheckerBase *Checker; AnalysisDeclContext *AC; }; } // end anonymous namespace @@ -112,7 +115,8 @@ Message = "comparison of identical expressions always evaluates to true"; else Message = "comparison of identical expressions always evaluates to false"; - BR.EmitBasicReport(AC->getDecl(), "Compare of identical expressions", + BR.EmitBasicReport(AC->getDecl(), Checker, + "Compare of identical expressions", categories::LogicError, Message, ELoc); } // We want to visit ALL nodes (subexpressions of binary comparison @@ -137,7 +141,8 @@ Sr[0] = C->getTrueExpr()->getSourceRange(); Sr[1] = C->getFalseExpr()->getSourceRange(); BR.EmitBasicReport( - AC->getDecl(), "Identical expressions in conditional expression", + AC->getDecl(), Checker, + "Identical expressions in conditional expression", categories::LogicError, "identical expressions on both sides of ':' in conditional expression", ELoc, Sr); @@ -245,7 +250,7 @@ public: void checkASTCodeBody(const Decl *D, AnalysisManager &Mgr, BugReporter &BR) const { - FindIdenticalExprVisitor Visitor(BR, Mgr.getAnalysisDeclContext(D)); + FindIdenticalExprVisitor Visitor(BR, this, Mgr.getAnalysisDeclContext(D)); Visitor.TraverseDecl(const_cast(D)); } }; Index: cfe/trunk/lib/StaticAnalyzer/Checkers/IvarInvalidationChecker.cpp =================================================================== --- cfe/trunk/lib/StaticAnalyzer/Checkers/IvarInvalidationChecker.cpp +++ cfe/trunk/lib/StaticAnalyzer/Checkers/IvarInvalidationChecker.cpp @@ -49,6 +49,9 @@ DefaultBool check_MissingInvalidationMethod; /// Check that all ivars are invalidated. DefaultBool check_InstanceVariableInvalidation; + + CheckName checkName_MissingInvalidationMethod; + CheckName checkName_InstanceVariableInvalidation; }; class IvarInvalidationCheckerImpl { @@ -200,7 +203,8 @@ const ObjCIvarDecl *IvarDecl, const IvarToPropMapTy &IvarToPopertyMap); - void reportNoInvalidationMethod(const ObjCIvarDecl *FirstIvarDecl, + void reportNoInvalidationMethod(CheckName CheckName, + const ObjCIvarDecl *FirstIvarDecl, const IvarToPropMapTy &IvarToPopertyMap, const ObjCInterfaceDecl *InterfaceD, bool MissingDeclaration) const; @@ -476,7 +480,8 @@ // Report an error in case none of the invalidation methods are declared. if (!Info.needsInvalidation() && !PartialInfo.needsInvalidation()) { if (Filter.check_MissingInvalidationMethod) - reportNoInvalidationMethod(FirstIvarDecl, IvarToPopertyMap, InterfaceD, + reportNoInvalidationMethod(Filter.checkName_MissingInvalidationMethod, + FirstIvarDecl, IvarToPopertyMap, InterfaceD, /*MissingDeclaration*/ true); // If there are no invalidation methods, there is no ivar validation work // to be done. @@ -532,17 +537,17 @@ reportIvarNeedsInvalidation(I->first, IvarToPopertyMap, 0); } else { // Otherwise, no invalidation methods were implemented. - reportNoInvalidationMethod(FirstIvarDecl, IvarToPopertyMap, InterfaceD, + reportNoInvalidationMethod(Filter.checkName_InstanceVariableInvalidation, + FirstIvarDecl, IvarToPopertyMap, InterfaceD, /*MissingDeclaration*/ false); } } } -void IvarInvalidationCheckerImpl:: -reportNoInvalidationMethod(const ObjCIvarDecl *FirstIvarDecl, - const IvarToPropMapTy &IvarToPopertyMap, - const ObjCInterfaceDecl *InterfaceD, - bool MissingDeclaration) const { +void IvarInvalidationCheckerImpl::reportNoInvalidationMethod( + CheckName CheckName, const ObjCIvarDecl *FirstIvarDecl, + const IvarToPropMapTy &IvarToPopertyMap, + const ObjCInterfaceDecl *InterfaceD, bool MissingDeclaration) const { SmallString<128> sbuf; llvm::raw_svector_ostream os(sbuf); assert(FirstIvarDecl); @@ -557,7 +562,7 @@ PathDiagnosticLocation IvarDecLocation = PathDiagnosticLocation::createBegin(FirstIvarDecl, BR.getSourceManager()); - BR.EmitBasicReport(FirstIvarDecl, "Incomplete invalidation", + BR.EmitBasicReport(FirstIvarDecl, CheckName, "Incomplete invalidation", categories::CoreFoundationObjectiveC, os.str(), IvarDecLocation); } @@ -575,15 +580,16 @@ PathDiagnosticLocation::createEnd(MethodD->getBody(), BR.getSourceManager(), Mgr.getAnalysisDeclContext(MethodD)); - BR.EmitBasicReport(MethodD, "Incomplete invalidation", + BR.EmitBasicReport(MethodD, Filter.checkName_InstanceVariableInvalidation, + "Incomplete invalidation", categories::CoreFoundationObjectiveC, os.str(), MethodDecLocation); } else { - BR.EmitBasicReport(IvarD, "Incomplete invalidation", - categories::CoreFoundationObjectiveC, os.str(), - PathDiagnosticLocation::createBegin(IvarD, - BR.getSourceManager())); - + BR.EmitBasicReport( + IvarD, Filter.checkName_InstanceVariableInvalidation, + "Incomplete invalidation", categories::CoreFoundationObjectiveC, + os.str(), + PathDiagnosticLocation::createBegin(IvarD, BR.getSourceManager())); } } @@ -750,10 +756,13 @@ }; } -#define REGISTER_CHECKER(name) \ -void ento::register##name(CheckerManager &mgr) {\ - mgr.registerChecker()->Filter.check_##name = true;\ -} +#define REGISTER_CHECKER(name) \ + void ento::register##name(CheckerManager &mgr) { \ + IvarInvalidationChecker *checker = \ + mgr.registerChecker(); \ + checker->Filter.check_##name = true; \ + checker->Filter.checkName_##name = mgr.getCurrentCheckName(); \ + } REGISTER_CHECKER(InstanceVariableInvalidation) REGISTER_CHECKER(MissingInvalidationMethod) Index: cfe/trunk/lib/StaticAnalyzer/Checkers/LLVMConventionsChecker.cpp =================================================================== --- cfe/trunk/lib/StaticAnalyzer/Checkers/LLVMConventionsChecker.cpp +++ cfe/trunk/lib/StaticAnalyzer/Checkers/LLVMConventionsChecker.cpp @@ -115,11 +115,14 @@ namespace { class StringRefCheckerVisitor : public StmtVisitor { - BugReporter &BR; const Decl *DeclWithIssue; + BugReporter &BR; + const CheckerBase *Checker; + public: - StringRefCheckerVisitor(const Decl *declWithIssue, BugReporter &br) - : BR(br), DeclWithIssue(declWithIssue) {} + StringRefCheckerVisitor(const Decl *declWithIssue, BugReporter &br, + const CheckerBase *checker) + : DeclWithIssue(declWithIssue), BR(br), Checker(checker) {} void VisitChildren(Stmt *S) { for (Stmt::child_iterator I = S->child_begin(), E = S->child_end() ; I != E; ++I) @@ -133,8 +136,9 @@ }; } // end anonymous namespace -static void CheckStringRefAssignedTemporary(const Decl *D, BugReporter &BR) { - StringRefCheckerVisitor walker(D, BR); +static void CheckStringRefAssignedTemporary(const Decl *D, BugReporter &BR, + const CheckerBase *Checker) { + StringRefCheckerVisitor walker(D, BR, Checker); walker.Visit(D->getBody()); } @@ -179,7 +183,7 @@ "std::string that it outlives"; PathDiagnosticLocation VDLoc = PathDiagnosticLocation::createBegin(VD, BR.getSourceManager()); - BR.EmitBasicReport(DeclWithIssue, desc, "LLVM Conventions", desc, + BR.EmitBasicReport(DeclWithIssue, Checker, desc, "LLVM Conventions", desc, VDLoc, Init->getSourceRange()); } @@ -216,22 +220,26 @@ SmallVector FieldChain; const CXXRecordDecl *Root; BugReporter &BR; + const CheckerBase *Checker; + public: - ASTFieldVisitor(const CXXRecordDecl *root, BugReporter &br) - : Root(root), BR(br) {} + ASTFieldVisitor(const CXXRecordDecl *root, BugReporter &br, + const CheckerBase *checker) + : Root(root), BR(br), Checker(checker) {} void Visit(FieldDecl *D); void ReportError(QualType T); }; } // end anonymous namespace -static void CheckASTMemory(const CXXRecordDecl *R, BugReporter &BR) { +static void CheckASTMemory(const CXXRecordDecl *R, BugReporter &BR, + const CheckerBase *Checker) { if (!IsPartOfAST(R)) return; for (RecordDecl::field_iterator I = R->field_begin(), E = R->field_end(); I != E; ++I) { - ASTFieldVisitor walker(R, BR); + ASTFieldVisitor walker(R, BR, Checker); walker.Visit(*I); } } @@ -284,8 +292,8 @@ // the class may be in the header file, for example). PathDiagnosticLocation L = PathDiagnosticLocation::createBegin( FieldChain.front(), BR.getSourceManager()); - BR.EmitBasicReport(Root, "AST node allocates heap memory", "LLVM Conventions", - os.str(), L); + BR.EmitBasicReport(Root, Checker, "AST node allocates heap memory", + "LLVM Conventions", os.str(), L); } //===----------------------------------------------------------------------===// @@ -300,12 +308,12 @@ void checkASTDecl(const CXXRecordDecl *R, AnalysisManager& mgr, BugReporter &BR) const { if (R->isCompleteDefinition()) - CheckASTMemory(R, BR); + CheckASTMemory(R, BR, this); } void checkASTCodeBody(const Decl *D, AnalysisManager& mgr, BugReporter &BR) const { - CheckStringRefAssignedTemporary(D, BR); + CheckStringRefAssignedTemporary(D, BR, this); } }; } Index: cfe/trunk/lib/StaticAnalyzer/Checkers/MacOSKeychainAPIChecker.cpp =================================================================== --- cfe/trunk/lib/StaticAnalyzer/Checkers/MacOSKeychainAPIChecker.cpp +++ cfe/trunk/lib/StaticAnalyzer/Checkers/MacOSKeychainAPIChecker.cpp @@ -91,7 +91,7 @@ inline void initBugType() const { if (!BT) - BT.reset(new BugType("Improper use of SecKeychain API", + BT.reset(new BugType(this, "Improper use of SecKeychain API", "API Misuse (Apple)")); } Index: cfe/trunk/lib/StaticAnalyzer/Checkers/MacOSXAPIChecker.cpp =================================================================== --- cfe/trunk/lib/StaticAnalyzer/Checkers/MacOSXAPIChecker.cpp +++ cfe/trunk/lib/StaticAnalyzer/Checkers/MacOSXAPIChecker.cpp @@ -67,7 +67,7 @@ return; if (!BT_dispatchOnce) - BT_dispatchOnce.reset(new BugType("Improper use of 'dispatch_once'", + BT_dispatchOnce.reset(new BugType(this, "Improper use of 'dispatch_once'", "API Misuse (Apple)")); // Handle _dispatch_once. In some versions of the OS X SDK we have the case Index: cfe/trunk/lib/StaticAnalyzer/Checkers/MallocChecker.cpp =================================================================== --- cfe/trunk/lib/StaticAnalyzer/Checkers/MallocChecker.cpp +++ cfe/trunk/lib/StaticAnalyzer/Checkers/MallocChecker.cpp @@ -155,31 +155,23 @@ check::Location, eval::Assume> { - mutable OwningPtr BT_DoubleFree; - mutable OwningPtr BT_DoubleDelete; - mutable OwningPtr BT_Leak; - mutable OwningPtr BT_UseFree; - mutable OwningPtr BT_BadFree; - mutable OwningPtr BT_MismatchedDealloc; - mutable OwningPtr BT_OffsetFree; - mutable IdentifierInfo *II_malloc, *II_free, *II_realloc, *II_calloc, - *II_valloc, *II_reallocf, *II_strndup, *II_strdup; - public: MallocChecker() : II_malloc(0), II_free(0), II_realloc(0), II_calloc(0), II_valloc(0), II_reallocf(0), II_strndup(0), II_strdup(0) {} /// In pessimistic mode, the checker assumes that it does not know which /// functions might free the memory. - struct ChecksFilter { - DefaultBool CMallocPessimistic; - DefaultBool CMallocOptimistic; - DefaultBool CNewDeleteChecker; - DefaultBool CNewDeleteLeaksChecker; - DefaultBool CMismatchedDeallocatorChecker; + enum CheckKind { + CK_MallocPessimistic, + CK_MallocOptimistic, + CK_NewDeleteChecker, + CK_NewDeleteLeaksChecker, + CK_MismatchedDeallocatorChecker, + CK_NumCheckKinds }; - ChecksFilter Filter; + DefaultBool ChecksEnabled[CK_NumCheckKinds]; + CheckName CheckNames[CK_NumCheckKinds]; void checkPreCall(const CallEvent &Call, CheckerContext &C) const; void checkPostStmt(const CallExpr *CE, CheckerContext &C) const; @@ -207,6 +199,16 @@ const char *NL, const char *Sep) const; private: + mutable OwningPtr BT_DoubleFree[CK_NumCheckKinds]; + mutable OwningPtr BT_DoubleDelete; + mutable OwningPtr BT_Leak[CK_NumCheckKinds]; + mutable OwningPtr BT_UseFree[CK_NumCheckKinds]; + mutable OwningPtr BT_BadFree[CK_NumCheckKinds]; + mutable OwningPtr BT_MismatchedDealloc; + mutable OwningPtr BT_OffsetFree[CK_NumCheckKinds]; + mutable IdentifierInfo *II_malloc, *II_free, *II_realloc, *II_calloc, + *II_valloc, *II_reallocf, *II_strndup, *II_strdup; + void initIdentifierInfo(ASTContext &C) const; /// \brief Determine family of a deallocation expression. @@ -304,10 +306,12 @@ ///@{ /// Tells if a given family/call/symbol is tracked by the current checker. - bool isTrackedByCurrentChecker(AllocationFamily Family) const; - bool isTrackedByCurrentChecker(CheckerContext &C, - const Stmt *AllocDeallocStmt) const; - bool isTrackedByCurrentChecker(CheckerContext &C, SymbolRef Sym) const; + /// Sets CheckKind to the kind of the checker responsible for this + /// family/call/symbol. + Optional getCheckIfTracked(AllocationFamily Family) const; + Optional getCheckIfTracked(CheckerContext &C, + const Stmt *AllocDeallocStmt) const; + Optional getCheckIfTracked(CheckerContext &C, SymbolRef Sym) const; ///@} static bool SummarizeValue(raw_ostream &os, SVal V); static bool SummarizeRegion(raw_ostream &os, const MemRegion *MR); @@ -507,7 +511,7 @@ return true; } - if (Filter.CMallocOptimistic && FD->hasAttrs()) + if (ChecksEnabled[CK_MallocOptimistic] && FD->hasAttrs()) for (specific_attr_iterator i = FD->specific_attr_begin(), e = FD->specific_attr_end(); @@ -529,7 +533,7 @@ return true; } - if (Filter.CMallocOptimistic && FD->hasAttrs()) + if (ChecksEnabled[CK_MallocOptimistic] && FD->hasAttrs()) for (specific_attr_iterator i = FD->specific_attr_begin(), e = FD->specific_attr_end(); @@ -624,7 +628,8 @@ } } - if (Filter.CMallocOptimistic || Filter.CMismatchedDeallocatorChecker) { + if (ChecksEnabled[CK_MallocOptimistic] || + ChecksEnabled[CK_MismatchedDeallocatorChecker]) { // Check all the attributes, if there are any. // There can be multiple of these attributes. if (FD->hasAttrs()) @@ -671,7 +676,7 @@ void MallocChecker::checkPreStmt(const CXXDeleteExpr *DE, CheckerContext &C) const { - if (!Filter.CNewDeleteChecker) + if (!ChecksEnabled[CK_NewDeleteChecker]) if (SymbolRef Sym = C.getSVal(DE->getArgument()).getAsSymbol()) checkUseAfterFree(Sym, C, DE->getArgument()); @@ -1092,18 +1097,23 @@ RefState::getReleased(Family, ParentExpr)); } -bool MallocChecker::isTrackedByCurrentChecker(AllocationFamily Family) const { +Optional +MallocChecker::getCheckIfTracked(AllocationFamily Family) const { switch (Family) { case AF_Malloc: { - if (!Filter.CMallocOptimistic && !Filter.CMallocPessimistic) - return false; - return true; + if (ChecksEnabled[CK_MallocOptimistic]) { + return CK_MallocOptimistic; + } else if (ChecksEnabled[CK_MallocPessimistic]) { + return CK_MallocPessimistic; + } + return Optional(); } case AF_CXXNew: case AF_CXXNewArray: { - if (!Filter.CNewDeleteChecker) - return false; - return true; + if (ChecksEnabled[CK_NewDeleteChecker]) { + return CK_NewDeleteChecker; + } + return Optional(); } case AF_None: { llvm_unreachable("no family"); @@ -1112,18 +1122,18 @@ llvm_unreachable("unhandled family"); } -bool -MallocChecker::isTrackedByCurrentChecker(CheckerContext &C, - const Stmt *AllocDeallocStmt) const { - return isTrackedByCurrentChecker(getAllocationFamily(C, AllocDeallocStmt)); +Optional +MallocChecker::getCheckIfTracked(CheckerContext &C, + const Stmt *AllocDeallocStmt) const { + return getCheckIfTracked(getAllocationFamily(C, AllocDeallocStmt)); } -bool MallocChecker::isTrackedByCurrentChecker(CheckerContext &C, - SymbolRef Sym) const { +Optional +MallocChecker::getCheckIfTracked(CheckerContext &C, SymbolRef Sym) const { const RefState *RS = C.getState()->get(Sym); assert(RS); - return isTrackedByCurrentChecker(RS->getAllocationFamily()); + return getCheckIfTracked(RS->getAllocationFamily()); } bool MallocChecker::SummarizeValue(raw_ostream &os, SVal V) { @@ -1217,17 +1227,21 @@ SourceRange Range, const Expr *DeallocExpr) const { - if (!Filter.CMallocOptimistic && !Filter.CMallocPessimistic && - !Filter.CNewDeleteChecker) + if (!ChecksEnabled[CK_MallocOptimistic] && + !ChecksEnabled[CK_MallocPessimistic] && + !ChecksEnabled[CK_NewDeleteChecker]) return; - if (!isTrackedByCurrentChecker(C, DeallocExpr)) + Optional CheckKind = + getCheckIfTracked(C, DeallocExpr); + if (!CheckKind.hasValue()) return; if (ExplodedNode *N = C.generateSink()) { - if (!BT_BadFree) - BT_BadFree.reset(new BugType("Bad free", "Memory Error")); - + if (!BT_BadFree[*CheckKind]) + BT_BadFree[*CheckKind].reset( + new BugType(CheckNames[*CheckKind], "Bad free", "Memory Error")); + SmallString<100> buf; llvm::raw_svector_ostream os(buf); @@ -1253,7 +1267,7 @@ printExpectedAllocName(os, C, DeallocExpr); } - BugReport *R = new BugReport(*BT_BadFree, os.str(), N); + BugReport *R = new BugReport(*BT_BadFree[*CheckKind], os.str(), N); R->markInteresting(MR); R->addRange(Range); C.emitReport(R); @@ -1267,14 +1281,15 @@ SymbolRef Sym, bool OwnershipTransferred) const { - if (!Filter.CMismatchedDeallocatorChecker) + if (!ChecksEnabled[CK_MismatchedDeallocatorChecker]) return; if (ExplodedNode *N = C.generateSink()) { if (!BT_MismatchedDealloc) - BT_MismatchedDealloc.reset(new BugType("Bad deallocator", - "Memory Error")); - + BT_MismatchedDealloc.reset( + new BugType(CheckNames[CK_MismatchedDeallocatorChecker], + "Bad deallocator", "Memory Error")); + SmallString<100> buf; llvm::raw_svector_ostream os(buf); @@ -1318,19 +1333,23 @@ SourceRange Range, const Expr *DeallocExpr, const Expr *AllocExpr) const { - if (!Filter.CMallocOptimistic && !Filter.CMallocPessimistic && - !Filter.CNewDeleteChecker) + if (!ChecksEnabled[CK_MallocOptimistic] && + !ChecksEnabled[CK_MallocPessimistic] && + !ChecksEnabled[CK_NewDeleteChecker]) return; - if (!isTrackedByCurrentChecker(C, AllocExpr)) + Optional CheckKind = + getCheckIfTracked(C, AllocExpr); + if (!CheckKind.hasValue()) return; ExplodedNode *N = C.generateSink(); if (N == NULL) return; - if (!BT_OffsetFree) - BT_OffsetFree.reset(new BugType("Offset free", "Memory Error")); + if (!BT_OffsetFree[*CheckKind]) + BT_OffsetFree[*CheckKind].reset( + new BugType(CheckNames[*CheckKind], "Offset free", "Memory Error")); SmallString<100> buf; llvm::raw_svector_ostream os(buf); @@ -1361,7 +1380,7 @@ else os << "allocated memory"; - BugReport *R = new BugReport(*BT_OffsetFree, os.str(), N); + BugReport *R = new BugReport(*BT_OffsetFree[*CheckKind], os.str(), N); R->markInteresting(MR->getBaseRegion()); R->addRange(Range); C.emitReport(R); @@ -1370,18 +1389,21 @@ void MallocChecker::ReportUseAfterFree(CheckerContext &C, SourceRange Range, SymbolRef Sym) const { - if (!Filter.CMallocOptimistic && !Filter.CMallocPessimistic && - !Filter.CNewDeleteChecker) + if (!ChecksEnabled[CK_MallocOptimistic] && + !ChecksEnabled[CK_MallocPessimistic] && + !ChecksEnabled[CK_NewDeleteChecker]) return; - if (!isTrackedByCurrentChecker(C, Sym)) + Optional CheckKind = getCheckIfTracked(C, Sym); + if (!CheckKind.hasValue()) return; if (ExplodedNode *N = C.generateSink()) { - if (!BT_UseFree) - BT_UseFree.reset(new BugType("Use-after-free", "Memory Error")); + if (!BT_UseFree[*CheckKind]) + BT_UseFree[*CheckKind].reset(new BugType( + CheckNames[*CheckKind], "Use-after-free", "Memory Error")); - BugReport *R = new BugReport(*BT_UseFree, + BugReport *R = new BugReport(*BT_UseFree[*CheckKind], "Use of memory after it is freed", N); R->markInteresting(Sym); @@ -1395,21 +1417,25 @@ bool Released, SymbolRef Sym, SymbolRef PrevSym) const { - if (!Filter.CMallocOptimistic && !Filter.CMallocPessimistic && - !Filter.CNewDeleteChecker) + if (!ChecksEnabled[CK_MallocOptimistic] && + !ChecksEnabled[CK_MallocPessimistic] && + !ChecksEnabled[CK_NewDeleteChecker]) return; - if (!isTrackedByCurrentChecker(C, Sym)) + Optional CheckKind = getCheckIfTracked(C, Sym); + if (!CheckKind.hasValue()) return; if (ExplodedNode *N = C.generateSink()) { - if (!BT_DoubleFree) - BT_DoubleFree.reset(new BugType("Double free", "Memory Error")); - - BugReport *R = new BugReport(*BT_DoubleFree, - (Released ? "Attempt to free released memory" - : "Attempt to free non-owned memory"), - N); + if (!BT_DoubleFree[*CheckKind]) + BT_DoubleFree[*CheckKind].reset( + new BugType(CheckNames[*CheckKind], "Double free", "Memory Error")); + + BugReport *R = + new BugReport(*BT_DoubleFree[*CheckKind], + (Released ? "Attempt to free released memory" + : "Attempt to free non-owned memory"), + N); R->addRange(Range); R->markInteresting(Sym); if (PrevSym) @@ -1421,15 +1447,18 @@ void MallocChecker::ReportDoubleDelete(CheckerContext &C, SymbolRef Sym) const { - if (!Filter.CNewDeleteChecker) + if (!ChecksEnabled[CK_NewDeleteChecker]) return; - if (!isTrackedByCurrentChecker(C, Sym)) + Optional CheckKind = getCheckIfTracked(C, Sym); + if (!CheckKind.hasValue()) return; + assert(*CheckKind == CK_NewDeleteChecker && "invalid check kind"); if (ExplodedNode *N = C.generateSink()) { if (!BT_DoubleDelete) - BT_DoubleDelete.reset(new BugType("Double delete", "Memory Error")); + BT_DoubleDelete.reset(new BugType(CheckNames[CK_NewDeleteChecker], + "Double delete", "Memory Error")); BugReport *R = new BugReport(*BT_DoubleDelete, "Attempt to delete released memory", N); @@ -1601,31 +1630,34 @@ void MallocChecker::reportLeak(SymbolRef Sym, ExplodedNode *N, CheckerContext &C) const { - if (!Filter.CMallocOptimistic && !Filter.CMallocPessimistic && - !Filter.CNewDeleteLeaksChecker) + if (!ChecksEnabled[CK_MallocOptimistic] && + !ChecksEnabled[CK_MallocPessimistic] && + !ChecksEnabled[CK_NewDeleteLeaksChecker]) return; const RefState *RS = C.getState()->get(Sym); assert(RS && "cannot leak an untracked symbol"); AllocationFamily Family = RS->getAllocationFamily(); - if (!isTrackedByCurrentChecker(Family)) + Optional CheckKind = getCheckIfTracked(Family); + if (!CheckKind.hasValue()) return; // Special case for new and new[]; these are controlled by a separate checker // flag so that they can be selectively disabled. if (Family == AF_CXXNew || Family == AF_CXXNewArray) - if (!Filter.CNewDeleteLeaksChecker) + if (!ChecksEnabled[CK_NewDeleteLeaksChecker]) return; assert(N); - if (!BT_Leak) { - BT_Leak.reset(new BugType("Memory leak", "Memory Error")); + if (!BT_Leak[*CheckKind]) { + BT_Leak[*CheckKind].reset( + new BugType(CheckNames[*CheckKind], "Memory leak", "Memory Error")); // Leaks should not be reported if they are post-dominated by a sink: // (1) Sinks are higher importance bugs. // (2) NoReturnFunctionChecker uses sink nodes to represent paths ending // with __noreturn functions such as assert() or exit(). We choose not // to report leaks on such paths. - BT_Leak->setSuppressOnSink(true); + BT_Leak[*CheckKind]->setSuppressOnSink(true); } // Most bug reports are cached at the location where they occurred. @@ -1656,9 +1688,9 @@ os << "Potential memory leak"; } - BugReport *R = new BugReport(*BT_Leak, os.str(), N, - LocUsedForUniqueing, - AllocNode->getLocationContext()->getDecl()); + BugReport *R = + new BugReport(*BT_Leak[*CheckKind], os.str(), N, LocUsedForUniqueing, + AllocNode->getLocationContext()->getDecl()); R->markInteresting(Sym); R->addVisitor(new MallocBugVisitor(Sym, true)); C.emitReport(R); @@ -1732,11 +1764,12 @@ if (!FD) return; - if ((Filter.CMallocOptimistic || Filter.CMallocPessimistic) && + if ((ChecksEnabled[CK_MallocOptimistic] || + ChecksEnabled[CK_MallocPessimistic]) && isFreeFunction(FD, C.getASTContext())) return; - if (Filter.CNewDeleteChecker && + if (ChecksEnabled[CK_NewDeleteChecker] && isStandardNewDelete(FD, C.getASTContext())) return; } @@ -2230,17 +2263,26 @@ void ento::registerNewDeleteLeaksChecker(CheckerManager &mgr) { registerCStringCheckerBasic(mgr); - mgr.registerChecker()->Filter.CNewDeleteLeaksChecker = true; + MallocChecker *checker = mgr.registerChecker(); + checker->ChecksEnabled[MallocChecker::CK_NewDeleteLeaksChecker] = true; + checker->CheckNames[MallocChecker::CK_NewDeleteLeaksChecker] = + mgr.getCurrentCheckName(); // We currently treat NewDeleteLeaks checker as a subchecker of NewDelete // checker. - mgr.registerChecker()->Filter.CNewDeleteChecker = true; + if (!checker->ChecksEnabled[MallocChecker::CK_NewDeleteChecker]) { + checker->ChecksEnabled[MallocChecker::CK_NewDeleteChecker] = true; + checker->CheckNames[MallocChecker::CK_NewDeleteChecker] = + mgr.getCurrentCheckName(); + } } -#define REGISTER_CHECKER(name) \ -void ento::register##name(CheckerManager &mgr) {\ - registerCStringCheckerBasic(mgr); \ - mgr.registerChecker()->Filter.C##name = true;\ -} +#define REGISTER_CHECKER(name) \ + void ento::register##name(CheckerManager &mgr) { \ + registerCStringCheckerBasic(mgr); \ + MallocChecker *checker = mgr.registerChecker(); \ + checker->ChecksEnabled[MallocChecker::CK_##name] = true; \ + checker->CheckNames[MallocChecker::CK_##name] = mgr.getCurrentCheckName(); \ + } REGISTER_CHECKER(MallocPessimistic) REGISTER_CHECKER(MallocOptimistic) Index: cfe/trunk/lib/StaticAnalyzer/Checkers/MallocOverflowSecurityChecker.cpp =================================================================== --- cfe/trunk/lib/StaticAnalyzer/Checkers/MallocOverflowSecurityChecker.cpp +++ cfe/trunk/lib/StaticAnalyzer/Checkers/MallocOverflowSecurityChecker.cpp @@ -213,11 +213,12 @@ e = PossibleMallocOverflows.end(); i != e; ++i) { - BR.EmitBasicReport(D, "malloc() size overflow", categories::UnixAPI, - "the computation of the size of the memory allocation may overflow", - PathDiagnosticLocation::createOperatorLoc(i->mulop, - BR.getSourceManager()), - i->mulop->getSourceRange()); + BR.EmitBasicReport( + D, this, "malloc() size overflow", categories::UnixAPI, + "the computation of the size of the memory allocation may overflow", + PathDiagnosticLocation::createOperatorLoc(i->mulop, + BR.getSourceManager()), + i->mulop->getSourceRange()); } } @@ -262,6 +263,7 @@ OutputPossibleOverflows(PossibleMallocOverflows, D, BR, mgr); } -void ento::registerMallocOverflowSecurityChecker(CheckerManager &mgr) { +void +ento::registerMallocOverflowSecurityChecker(CheckerManager &mgr) { mgr.registerChecker(); } Index: cfe/trunk/lib/StaticAnalyzer/Checkers/MallocSizeofChecker.cpp =================================================================== --- cfe/trunk/lib/StaticAnalyzer/Checkers/MallocSizeofChecker.cpp +++ cfe/trunk/lib/StaticAnalyzer/Checkers/MallocSizeofChecker.cpp @@ -236,10 +236,8 @@ PathDiagnosticLocation::createBegin(i->AllocCall->getCallee(), BR.getSourceManager(), ADC); - BR.EmitBasicReport(D, "Allocator sizeof operand mismatch", - categories::UnixAPI, - OS.str(), - L, Ranges); + BR.EmitBasicReport(D, this, "Allocator sizeof operand mismatch", + categories::UnixAPI, OS.str(), L, Ranges); } } } Index: cfe/trunk/lib/StaticAnalyzer/Checkers/NSAutoreleasePoolChecker.cpp =================================================================== --- cfe/trunk/lib/StaticAnalyzer/Checkers/NSAutoreleasePoolChecker.cpp +++ cfe/trunk/lib/StaticAnalyzer/Checkers/NSAutoreleasePoolChecker.cpp @@ -59,7 +59,7 @@ return; if (!BT) - BT.reset(new BugType("Use -drain instead of -release", + BT.reset(new BugType(this, "Use -drain instead of -release", "API Upgrade (Apple)")); ExplodedNode *N = C.addTransition(); Index: cfe/trunk/lib/StaticAnalyzer/Checkers/NSErrorChecker.cpp =================================================================== --- cfe/trunk/lib/StaticAnalyzer/Checkers/NSErrorChecker.cpp +++ cfe/trunk/lib/StaticAnalyzer/Checkers/NSErrorChecker.cpp @@ -75,7 +75,7 @@ "error occurred"; PathDiagnosticLocation L = PathDiagnosticLocation::create(D, BR.getSourceManager()); - BR.EmitBasicReport(D, "Bad return type when passing NSError**", + BR.EmitBasicReport(D, this, "Bad return type when passing NSError**", "Coding conventions (Apple)", err, L); } } @@ -123,7 +123,7 @@ "error occurred"; PathDiagnosticLocation L = PathDiagnosticLocation::create(D, BR.getSourceManager()); - BR.EmitBasicReport(D, "Bad return type when passing CFErrorRef*", + BR.EmitBasicReport(D, this, "Bad return type when passing CFErrorRef*", "Coding conventions (Apple)", err, L); } } @@ -136,14 +136,16 @@ class NSErrorDerefBug : public BugType { public: - NSErrorDerefBug() : BugType("NSError** null dereference", - "Coding conventions (Apple)") {} + NSErrorDerefBug(const CheckerBase *Checker) + : BugType(Checker, "NSError** null dereference", + "Coding conventions (Apple)") {} }; class CFErrorDerefBug : public BugType { public: - CFErrorDerefBug() : BugType("CFErrorRef* null dereference", - "Coding conventions (Apple)") {} + CFErrorDerefBug(const CheckerBase *Checker) + : BugType(Checker, "CFErrorRef* null dereference", + "Coding conventions (Apple)") {} }; } @@ -264,11 +266,10 @@ BugType *bug = 0; if (isNSError) - bug = new NSErrorDerefBug(); + bug = new NSErrorDerefBug(this); else - bug = new CFErrorDerefBug(); - BugReport *report = new BugReport(*bug, os.str(), - event.SinkNode); + bug = new CFErrorDerefBug(this); + BugReport *report = new BugReport(*bug, os.str(), event.SinkNode); BR.emitReport(report); } @@ -305,14 +306,14 @@ void ento::registerNSErrorChecker(CheckerManager &mgr) { mgr.registerChecker(); - NSOrCFErrorDerefChecker * - checker = mgr.registerChecker(); + NSOrCFErrorDerefChecker *checker = + mgr.registerChecker(); checker->ShouldCheckNSError = true; } void ento::registerCFErrorChecker(CheckerManager &mgr) { mgr.registerChecker(); - NSOrCFErrorDerefChecker * - checker = mgr.registerChecker(); + NSOrCFErrorDerefChecker *checker = + mgr.registerChecker(); checker->ShouldCheckCFError = true; } Index: cfe/trunk/lib/StaticAnalyzer/Checkers/NoReturnFunctionChecker.cpp =================================================================== --- cfe/trunk/lib/StaticAnalyzer/Checkers/NoReturnFunctionChecker.cpp +++ cfe/trunk/lib/StaticAnalyzer/Checkers/NoReturnFunctionChecker.cpp @@ -150,7 +150,6 @@ C.generateSink(); } - void ento::registerNoReturnFunctionChecker(CheckerManager &mgr) { mgr.registerChecker(); } Index: cfe/trunk/lib/StaticAnalyzer/Checkers/NonNullParamChecker.cpp =================================================================== --- cfe/trunk/lib/StaticAnalyzer/Checkers/NonNullParamChecker.cpp +++ cfe/trunk/lib/StaticAnalyzer/Checkers/NonNullParamChecker.cpp @@ -162,8 +162,7 @@ // the BugReport is passed to 'EmitWarning'. if (!BTAttrNonNull) BTAttrNonNull.reset(new BugType( - "Argument with 'nonnull' attribute passed null", - "API")); + this, "Argument with 'nonnull' attribute passed null", "API")); BugReport *R = new BugReport(*BTAttrNonNull, "Null pointer passed as an argument to a 'nonnull' parameter", @@ -177,7 +176,7 @@ BugReport *NonNullParamChecker::genReportReferenceToNullPointer( const ExplodedNode *ErrorNode, const Expr *ArgE) const { if (!BTNullRefArg) - BTNullRefArg.reset(new BuiltinBug("Dereference of null pointer")); + BTNullRefArg.reset(new BuiltinBug(this, "Dereference of null pointer")); BugReport *R = new BugReport(*BTNullRefArg, "Forming reference to null pointer", Index: cfe/trunk/lib/StaticAnalyzer/Checkers/ObjCAtSyncChecker.cpp =================================================================== --- cfe/trunk/lib/StaticAnalyzer/Checkers/ObjCAtSyncChecker.cpp +++ cfe/trunk/lib/StaticAnalyzer/Checkers/ObjCAtSyncChecker.cpp @@ -45,8 +45,8 @@ if (V.getAs()) { if (ExplodedNode *N = C.generateSink()) { if (!BT_undef) - BT_undef.reset(new BuiltinBug("Uninitialized value used as mutex " - "for @synchronized")); + BT_undef.reset(new BuiltinBug(this, "Uninitialized value used as mutex " + "for @synchronized")); BugReport *report = new BugReport(*BT_undef, BT_undef->getDescription(), N); bugreporter::trackNullOrUndefValue(N, Ex, *report); @@ -68,8 +68,9 @@ // a null mutex just means no synchronization occurs. if (ExplodedNode *N = C.addTransition(nullState)) { if (!BT_null) - BT_null.reset(new BuiltinBug("Nil value used as mutex for @synchronized() " - "(no synchronization will occur)")); + BT_null.reset(new BuiltinBug( + this, "Nil value used as mutex for @synchronized() " + "(no synchronization will occur)")); BugReport *report = new BugReport(*BT_null, BT_null->getDescription(), N); bugreporter::trackNullOrUndefValue(N, Ex, *report); Index: cfe/trunk/lib/StaticAnalyzer/Checkers/ObjCContainersASTChecker.cpp =================================================================== --- cfe/trunk/lib/StaticAnalyzer/Checkers/ObjCContainersASTChecker.cpp +++ cfe/trunk/lib/StaticAnalyzer/Checkers/ObjCContainersASTChecker.cpp @@ -27,6 +27,7 @@ namespace { class WalkAST : public StmtVisitor { BugReporter &BR; + const CheckerBase *Checker; AnalysisDeclContext* AC; ASTContext &ASTC; uint64_t PtrWidth; @@ -71,9 +72,9 @@ } public: - WalkAST(BugReporter &br, AnalysisDeclContext* ac) - : BR(br), AC(ac), ASTC(AC->getASTContext()), - PtrWidth(ASTC.getTargetInfo().getPointerWidth(0)) {} + WalkAST(BugReporter &br, const CheckerBase *checker, AnalysisDeclContext *ac) + : BR(br), Checker(checker), AC(ac), ASTC(AC->getASTContext()), + PtrWidth(ASTC.getTargetInfo().getPointerWidth(0)) {} // Statement visitor methods. void VisitChildren(Stmt *S); @@ -142,9 +143,9 @@ PathDiagnosticLocation CELoc = PathDiagnosticLocation::createBegin(CE, BR.getSourceManager(), AC); - BR.EmitBasicReport(AC->getDecl(), - OsName.str(), categories::CoreFoundationObjectiveC, - Os.str(), CELoc, Arg->getSourceRange()); + BR.EmitBasicReport(AC->getDecl(), Checker, OsName.str(), + categories::CoreFoundationObjectiveC, Os.str(), CELoc, + Arg->getSourceRange()); } // Recurse and check children. @@ -163,7 +164,7 @@ void checkASTCodeBody(const Decl *D, AnalysisManager& Mgr, BugReporter &BR) const { - WalkAST walker(BR, Mgr.getAnalysisDeclContext(D)); + WalkAST walker(BR, this, Mgr.getAnalysisDeclContext(D)); walker.Visit(D->getBody()); } }; Index: cfe/trunk/lib/StaticAnalyzer/Checkers/ObjCContainersChecker.cpp =================================================================== --- cfe/trunk/lib/StaticAnalyzer/Checkers/ObjCContainersChecker.cpp +++ cfe/trunk/lib/StaticAnalyzer/Checkers/ObjCContainersChecker.cpp @@ -33,7 +33,7 @@ mutable OwningPtr BT; inline void initBugType() const { if (!BT) - BT.reset(new BugType("CFArray API", + BT.reset(new BugType(this, "CFArray API", categories::CoreFoundationObjectiveC)); } Index: cfe/trunk/lib/StaticAnalyzer/Checkers/ObjCMissingSuperCallChecker.cpp =================================================================== --- cfe/trunk/lib/StaticAnalyzer/Checkers/ObjCMissingSuperCallChecker.cpp +++ cfe/trunk/lib/StaticAnalyzer/Checkers/ObjCMissingSuperCallChecker.cpp @@ -212,7 +212,7 @@ << "' instance method in " << SuperclassName.str() << " subclass '" << *D << "' is missing a [super " << S.getAsString() << "] call"; - BR.EmitBasicReport(MD, Name, categories::CoreFoundationObjectiveC, + BR.EmitBasicReport(MD, this, Name, categories::CoreFoundationObjectiveC, os.str(), DLoc); } } Index: cfe/trunk/lib/StaticAnalyzer/Checkers/ObjCSelfInitChecker.cpp =================================================================== --- cfe/trunk/lib/StaticAnalyzer/Checkers/ObjCSelfInitChecker.cpp +++ cfe/trunk/lib/StaticAnalyzer/Checkers/ObjCSelfInitChecker.cpp @@ -81,10 +81,10 @@ namespace { class InitSelfBug : public BugType { - const std::string desc; public: - InitSelfBug() : BugType("Missing \"self = [(super or self) init...]\"", - categories::CoreFoundationObjectiveC) {} + InitSelfBug(const CheckerBase *Checker) + : BugType(Checker, "Missing \"self = [(super or self) init...]\"", + categories::CoreFoundationObjectiveC) {} }; } // end anonymous namespace @@ -147,7 +147,8 @@ } static void checkForInvalidSelf(const Expr *E, CheckerContext &C, - const char *errorStr) { + const char *errorStr, + const CheckerBase *Checker) { if (!E) return; @@ -162,8 +163,7 @@ if (!N) return; - BugReport *report = - new BugReport(*new InitSelfBug(), errorStr, N); + BugReport *report = new BugReport(*new InitSelfBug(Checker), errorStr, N); C.emitReport(report); } @@ -205,9 +205,11 @@ C.getCurrentAnalysisDeclContext()->getDecl()))) return; - checkForInvalidSelf(E->getBase(), C, - "Instance variable used while 'self' is not set to the result of " - "'[(super or self) init...]'"); + checkForInvalidSelf( + E->getBase(), C, + "Instance variable used while 'self' is not set to the result of " + "'[(super or self) init...]'", + this); } void ObjCSelfInitChecker::checkPreStmt(const ReturnStmt *S, @@ -218,8 +220,9 @@ return; checkForInvalidSelf(S->getRetValue(), C, - "Returning 'self' while it is not set to the result of " - "'[(super or self) init...]'"); + "Returning 'self' while it is not set to the result of " + "'[(super or self) init...]'", + this); } // When a call receives a reference to 'self', [Pre/Post]Call pass Index: cfe/trunk/lib/StaticAnalyzer/Checkers/ObjCUnusedIVarsChecker.cpp =================================================================== --- cfe/trunk/lib/StaticAnalyzer/Checkers/ObjCUnusedIVarsChecker.cpp +++ cfe/trunk/lib/StaticAnalyzer/Checkers/ObjCUnusedIVarsChecker.cpp @@ -111,7 +111,8 @@ } static void checkObjCUnusedIvar(const ObjCImplementationDecl *D, - BugReporter &BR) { + BugReporter &BR, + const CheckerBase *Checker) { const ObjCInterfaceDecl *ID = D->getClassInterface(); IvarUsageMap M; @@ -172,7 +173,7 @@ PathDiagnosticLocation L = PathDiagnosticLocation::create(I->first, BR.getSourceManager()); - BR.EmitBasicReport(D, "Unused instance variable", "Optimization", + BR.EmitBasicReport(D, Checker, "Unused instance variable", "Optimization", os.str(), L); } } @@ -187,7 +188,7 @@ public: void checkASTDecl(const ObjCImplementationDecl *D, AnalysisManager& mgr, BugReporter &BR) const { - checkObjCUnusedIvar(D, BR); + checkObjCUnusedIvar(D, BR, this); } }; } Index: cfe/trunk/lib/StaticAnalyzer/Checkers/PointerArithChecker.cpp =================================================================== --- cfe/trunk/lib/StaticAnalyzer/Checkers/PointerArithChecker.cpp +++ cfe/trunk/lib/StaticAnalyzer/Checkers/PointerArithChecker.cpp @@ -53,10 +53,11 @@ if (ExplodedNode *N = C.addTransition()) { if (!BT) - BT.reset(new BuiltinBug("Dangerous pointer arithmetic", - "Pointer arithmetic done on non-array variables " - "means reliance on memory layout, which is " - "dangerous.")); + BT.reset( + new BuiltinBug(this, "Dangerous pointer arithmetic", + "Pointer arithmetic done on non-array variables " + "means reliance on memory layout, which is " + "dangerous.")); BugReport *R = new BugReport(*BT, BT->getDescription(), N); R->addRange(B->getSourceRange()); C.emitReport(R); Index: cfe/trunk/lib/StaticAnalyzer/Checkers/PointerSubChecker.cpp =================================================================== --- cfe/trunk/lib/StaticAnalyzer/Checkers/PointerSubChecker.cpp +++ cfe/trunk/lib/StaticAnalyzer/Checkers/PointerSubChecker.cpp @@ -62,9 +62,10 @@ if (ExplodedNode *N = C.addTransition()) { if (!BT) - BT.reset(new BuiltinBug("Pointer subtraction", - "Subtraction of two pointers that do not point to " - "the same memory chunk may cause incorrect result.")); + BT.reset( + new BuiltinBug(this, "Pointer subtraction", + "Subtraction of two pointers that do not point to " + "the same memory chunk may cause incorrect result.")); BugReport *R = new BugReport(*BT, BT->getDescription(), N); R->addRange(B->getSourceRange()); C.emitReport(R); Index: cfe/trunk/lib/StaticAnalyzer/Checkers/PthreadLockChecker.cpp =================================================================== --- cfe/trunk/lib/StaticAnalyzer/Checkers/PthreadLockChecker.cpp +++ cfe/trunk/lib/StaticAnalyzer/Checkers/PthreadLockChecker.cpp @@ -102,7 +102,7 @@ if (state->contains(lockR)) { if (!BT_doublelock) - BT_doublelock.reset(new BugType("Double locking", "Lock checker")); + BT_doublelock.reset(new BugType(this, "Double locking", "Lock checker")); ExplodedNode *N = C.generateSink(); if (!N) return; @@ -165,7 +165,7 @@ const MemRegion *firstLockR = LS.getHead(); if (firstLockR != lockR) { if (!BT_lor) - BT_lor.reset(new BugType("Lock order reversal", "Lock checker")); + BT_lor.reset(new BugType(this, "Lock order reversal", "Lock checker")); ExplodedNode *N = C.generateSink(); if (!N) return; @@ -184,7 +184,6 @@ C.addTransition(state); } - void ento::registerPthreadLockChecker(CheckerManager &mgr) { mgr.registerChecker(); } Index: cfe/trunk/lib/StaticAnalyzer/Checkers/RetainCountChecker.cpp =================================================================== --- cfe/trunk/lib/StaticAnalyzer/Checkers/RetainCountChecker.cpp +++ cfe/trunk/lib/StaticAnalyzer/Checkers/RetainCountChecker.cpp @@ -1547,8 +1547,9 @@ class CFRefBug : public BugType { protected: - CFRefBug(StringRef name) - : BugType(name, categories::MemoryCoreFoundationObjectiveC) {} + CFRefBug(const CheckerBase *checker, StringRef name) + : BugType(checker, name, categories::MemoryCoreFoundationObjectiveC) {} + public: // FIXME: Eventually remove. @@ -1559,7 +1560,8 @@ class UseAfterRelease : public CFRefBug { public: - UseAfterRelease() : CFRefBug("Use-after-release") {} + UseAfterRelease(const CheckerBase *checker) + : CFRefBug(checker, "Use-after-release") {} const char *getDescription() const { return "Reference-counted object is used after it is released"; @@ -1568,7 +1570,7 @@ class BadRelease : public CFRefBug { public: - BadRelease() : CFRefBug("Bad release") {} + BadRelease(const CheckerBase *checker) : CFRefBug(checker, "Bad release") {} const char *getDescription() const { return "Incorrect decrement of the reference count of an object that is " @@ -1578,8 +1580,8 @@ class DeallocGC : public CFRefBug { public: - DeallocGC() - : CFRefBug("-dealloc called while using garbage collection") {} + DeallocGC(const CheckerBase *checker) + : CFRefBug(checker, "-dealloc called while using garbage collection") {} const char *getDescription() const { return "-dealloc called while using garbage collection"; @@ -1588,8 +1590,8 @@ class DeallocNotOwned : public CFRefBug { public: - DeallocNotOwned() - : CFRefBug("-dealloc sent to non-exclusively owned object") {} + DeallocNotOwned(const CheckerBase *checker) + : CFRefBug(checker, "-dealloc sent to non-exclusively owned object") {} const char *getDescription() const { return "-dealloc sent to object that may be referenced elsewhere"; @@ -1598,8 +1600,8 @@ class OverAutorelease : public CFRefBug { public: - OverAutorelease() - : CFRefBug("Object autoreleased too many times") {} + OverAutorelease(const CheckerBase *checker) + : CFRefBug(checker, "Object autoreleased too many times") {} const char *getDescription() const { return "Object autoreleased too many times"; @@ -1608,8 +1610,8 @@ class ReturnedNotOwnedForOwned : public CFRefBug { public: - ReturnedNotOwnedForOwned() - : CFRefBug("Method should return an owned object") {} + ReturnedNotOwnedForOwned(const CheckerBase *checker) + : CFRefBug(checker, "Method should return an owned object") {} const char *getDescription() const { return "Object with a +0 retain count returned to caller where a +1 " @@ -1619,8 +1621,7 @@ class Leak : public CFRefBug { public: - Leak(StringRef name) - : CFRefBug(name) { + Leak(const CheckerBase *checker, StringRef name) : CFRefBug(checker, name) { // Leaks should not be reported if they are post-dominated by a sink. setSuppressOnSink(true); } @@ -2406,17 +2407,18 @@ bool GCEnabled) const { if (GCEnabled) { if (!leakWithinFunctionGC) - leakWithinFunctionGC.reset(new Leak("Leak of object when using " - "garbage collection")); + leakWithinFunctionGC.reset(new Leak(this, "Leak of object when using " + "garbage collection")); return leakWithinFunctionGC.get(); } else { if (!leakWithinFunction) { if (LOpts.getGC() == LangOptions::HybridGC) { - leakWithinFunction.reset(new Leak("Leak of object when not using " + leakWithinFunction.reset(new Leak(this, + "Leak of object when not using " "garbage collection (GC) in " "dual GC/non-GC code")); } else { - leakWithinFunction.reset(new Leak("Leak")); + leakWithinFunction.reset(new Leak(this, "Leak")); } } return leakWithinFunction.get(); @@ -2426,17 +2428,19 @@ CFRefBug *getLeakAtReturnBug(const LangOptions &LOpts, bool GCEnabled) const { if (GCEnabled) { if (!leakAtReturnGC) - leakAtReturnGC.reset(new Leak("Leak of returned object when using " + leakAtReturnGC.reset(new Leak(this, + "Leak of returned object when using " "garbage collection")); return leakAtReturnGC.get(); } else { if (!leakAtReturn) { if (LOpts.getGC() == LangOptions::HybridGC) { - leakAtReturn.reset(new Leak("Leak of returned object when not using " + leakAtReturn.reset(new Leak(this, + "Leak of returned object when not using " "garbage collection (GC) in dual " "GC/non-GC code")); } else { - leakAtReturn.reset(new Leak("Leak of returned object")); + leakAtReturn.reset(new Leak(this, "Leak of returned object")); } } return leakAtReturn.get(); @@ -3075,22 +3079,22 @@ llvm_unreachable("Unhandled error."); case RefVal::ErrorUseAfterRelease: if (!useAfterRelease) - useAfterRelease.reset(new UseAfterRelease()); + useAfterRelease.reset(new UseAfterRelease(this)); BT = &*useAfterRelease; break; case RefVal::ErrorReleaseNotOwned: if (!releaseNotOwned) - releaseNotOwned.reset(new BadRelease()); + releaseNotOwned.reset(new BadRelease(this)); BT = &*releaseNotOwned; break; case RefVal::ErrorDeallocGC: if (!deallocGC) - deallocGC.reset(new DeallocGC()); + deallocGC.reset(new DeallocGC(this)); BT = &*deallocGC; break; case RefVal::ErrorDeallocNotOwned: if (!deallocNotOwned) - deallocNotOwned.reset(new DeallocNotOwned()); + deallocNotOwned.reset(new DeallocNotOwned(this)); BT = &*deallocNotOwned; break; } @@ -3342,7 +3346,7 @@ ExplodedNode *N = C.addTransition(state, Pred, &ReturnNotOwnedTag); if (N) { if (!returnNotOwnedForOwned) - returnNotOwnedForOwned.reset(new ReturnedNotOwnedForOwned()); + returnNotOwnedForOwned.reset(new ReturnedNotOwnedForOwned(this)); CFRefReport *report = new CFRefReport(*returnNotOwnedForOwned, @@ -3530,7 +3534,7 @@ os << "has a +" << V.getCount() << " retain count"; if (!overAutorelease) - overAutorelease.reset(new OverAutorelease()); + overAutorelease.reset(new OverAutorelease(this)); const LangOptions &LOpts = Ctx.getASTContext().getLangOpts(); CFRefReport *report = Index: cfe/trunk/lib/StaticAnalyzer/Checkers/ReturnPointerRangeChecker.cpp =================================================================== --- cfe/trunk/lib/StaticAnalyzer/Checkers/ReturnPointerRangeChecker.cpp +++ cfe/trunk/lib/StaticAnalyzer/Checkers/ReturnPointerRangeChecker.cpp @@ -69,9 +69,10 @@ // FIXME: This bug correspond to CWE-466. Eventually we should have bug // types explicitly reference such exploit categories (when applicable). if (!BT) - BT.reset(new BuiltinBug("Return of pointer value outside of expected range", - "Returned pointer value points outside the original object " - "(potential buffer overflow)")); + BT.reset(new BuiltinBug( + this, "Return of pointer value outside of expected range", + "Returned pointer value points outside the original object " + "(potential buffer overflow)")); // FIXME: It would be nice to eventually make this diagnostic more clear, // e.g., by referencing the original declaration or by saying *why* this Index: cfe/trunk/lib/StaticAnalyzer/Checkers/ReturnUndefChecker.cpp =================================================================== --- cfe/trunk/lib/StaticAnalyzer/Checkers/ReturnUndefChecker.cpp +++ cfe/trunk/lib/StaticAnalyzer/Checkers/ReturnUndefChecker.cpp @@ -94,9 +94,9 @@ void ReturnUndefChecker::emitUndef(CheckerContext &C, const Expr *RetE) const { if (!BT_Undef) - BT_Undef.reset(new BuiltinBug("Garbage return value", - "Undefined or garbage value " - "returned to caller")); + BT_Undef.reset( + new BuiltinBug(this, "Garbage return value", + "Undefined or garbage value returned to caller")); emitBug(C, *BT_Undef, RetE); } @@ -113,7 +113,7 @@ // The return value is known to be null. Emit a bug report. if (!BT_NullReference) - BT_NullReference.reset(new BuiltinBug("Returning null reference")); + BT_NullReference.reset(new BuiltinBug(this, "Returning null reference")); emitBug(C, *BT_NullReference, RetE, bugreporter::getDerefExpr(RetE)); } Index: cfe/trunk/lib/StaticAnalyzer/Checkers/SimpleStreamChecker.cpp =================================================================== --- cfe/trunk/lib/StaticAnalyzer/Checkers/SimpleStreamChecker.cpp +++ cfe/trunk/lib/StaticAnalyzer/Checkers/SimpleStreamChecker.cpp @@ -109,11 +109,11 @@ SimpleStreamChecker::SimpleStreamChecker() : IIfopen(0), IIfclose(0) { // Initialize the bug types. - DoubleCloseBugType.reset(new BugType("Double fclose", - "Unix Stream API Error")); + DoubleCloseBugType.reset( + new BugType(this, "Double fclose", "Unix Stream API Error")); - LeakBugType.reset(new BugType("Resource Leak", - "Unix Stream API Error")); + LeakBugType.reset( + new BugType(this, "Resource Leak", "Unix Stream API Error")); // Sinks are higher importance bugs as well as calls to assert() or exit(0). LeakBugType->setSuppressOnSink(true); } Index: cfe/trunk/lib/StaticAnalyzer/Checkers/StackAddrEscapeChecker.cpp =================================================================== --- cfe/trunk/lib/StaticAnalyzer/Checkers/StackAddrEscapeChecker.cpp +++ cfe/trunk/lib/StaticAnalyzer/Checkers/StackAddrEscapeChecker.cpp @@ -100,8 +100,8 @@ return; if (!BT_returnstack) - BT_returnstack.reset( - new BuiltinBug("Return of address to stack-allocated memory")); + BT_returnstack.reset( + new BuiltinBug(this, "Return of address to stack-allocated memory")); // Generate a report for this bug. SmallString<512> buf; @@ -217,11 +217,11 @@ if (!BT_stackleak) BT_stackleak.reset( - new BuiltinBug("Stack address stored into global variable", - "Stack address was saved into a global variable. " - "This is dangerous because the address will become " - "invalid after returning from the function")); - + new BuiltinBug(this, "Stack address stored into global variable", + "Stack address was saved into a global variable. " + "This is dangerous because the address will become " + "invalid after returning from the function")); + for (unsigned i = 0, e = cb.V.size(); i != e; ++i) { // Generate a report for this bug. SmallString<512> buf; Index: cfe/trunk/lib/StaticAnalyzer/Checkers/StreamChecker.cpp =================================================================== --- cfe/trunk/lib/StaticAnalyzer/Checkers/StreamChecker.cpp +++ cfe/trunk/lib/StaticAnalyzer/Checkers/StreamChecker.cpp @@ -270,9 +270,10 @@ if (ExplodedNode *N = C.addTransition(state)) { if (!BT_illegalwhence) - BT_illegalwhence.reset(new BuiltinBug("Illegal whence argument", - "The whence argument to fseek() should be " - "SEEK_SET, SEEK_END, or SEEK_CUR.")); + BT_illegalwhence.reset( + new BuiltinBug(this, "Illegal whence argument", + "The whence argument to fseek() should be " + "SEEK_SET, SEEK_END, or SEEK_CUR.")); BugReport *R = new BugReport(*BT_illegalwhence, BT_illegalwhence->getDescription(), N); C.emitReport(R); @@ -348,8 +349,8 @@ if (!stateNotNull && stateNull) { if (ExplodedNode *N = C.generateSink(stateNull)) { if (!BT_nullfp) - BT_nullfp.reset(new BuiltinBug("NULL stream pointer", - "Stream pointer might be NULL.")); + BT_nullfp.reset(new BuiltinBug(this, "NULL stream pointer", + "Stream pointer might be NULL.")); BugReport *R =new BugReport(*BT_nullfp, BT_nullfp->getDescription(), N); C.emitReport(R); } @@ -378,9 +379,9 @@ ExplodedNode *N = C.generateSink(); if (N) { if (!BT_doubleclose) - BT_doubleclose.reset(new BuiltinBug("Double fclose", - "Try to close a file Descriptor already" - " closed. Cause undefined behaviour.")); + BT_doubleclose.reset(new BuiltinBug( + this, "Double fclose", "Try to close a file Descriptor already" + " closed. Cause undefined behaviour.")); BugReport *R = new BugReport(*BT_doubleclose, BT_doubleclose->getDescription(), N); C.emitReport(R); @@ -407,8 +408,9 @@ ExplodedNode *N = C.generateSink(); if (N) { if (!BT_ResourceLeak) - BT_ResourceLeak.reset(new BuiltinBug("Resource Leak", - "Opened File never closed. Potential Resource leak.")); + BT_ResourceLeak.reset(new BuiltinBug( + this, "Resource Leak", + "Opened File never closed. Potential Resource leak.")); BugReport *R = new BugReport(*BT_ResourceLeak, BT_ResourceLeak->getDescription(), N); C.emitReport(R); Index: cfe/trunk/lib/StaticAnalyzer/Checkers/TaintTesterChecker.cpp =================================================================== --- cfe/trunk/lib/StaticAnalyzer/Checkers/TaintTesterChecker.cpp +++ cfe/trunk/lib/StaticAnalyzer/Checkers/TaintTesterChecker.cpp @@ -38,7 +38,7 @@ inline void TaintTesterChecker::initBugType() const { if (!BT) - BT.reset(new BugType("Tainted data", "General")); + BT.reset(new BugType(this, "Tainted data", "General")); } void TaintTesterChecker::checkPostStmt(const Expr *E, Index: cfe/trunk/lib/StaticAnalyzer/Checkers/UndefBranchChecker.cpp =================================================================== --- cfe/trunk/lib/StaticAnalyzer/Checkers/UndefBranchChecker.cpp +++ cfe/trunk/lib/StaticAnalyzer/Checkers/UndefBranchChecker.cpp @@ -67,8 +67,8 @@ ExplodedNode *N = Ctx.generateSink(); if (N) { if (!BT) - BT.reset( - new BuiltinBug("Branch condition evaluates to a garbage value")); + BT.reset(new BuiltinBug( + this, "Branch condition evaluates to a garbage value")); // What's going on here: we want to highlight the subexpression of the // condition that is the most likely source of the "uninitialized Index: cfe/trunk/lib/StaticAnalyzer/Checkers/UndefCapturedBlockVarChecker.cpp =================================================================== --- cfe/trunk/lib/StaticAnalyzer/Checkers/UndefCapturedBlockVarChecker.cpp +++ cfe/trunk/lib/StaticAnalyzer/Checkers/UndefCapturedBlockVarChecker.cpp @@ -79,7 +79,8 @@ state->getSVal(I.getOriginalRegion()).getAs()) { if (ExplodedNode *N = C.generateSink()) { if (!BT) - BT.reset(new BuiltinBug("uninitialized variable captured by block")); + BT.reset( + new BuiltinBug(this, "uninitialized variable captured by block")); // Generate a bug report. SmallString<128> buf; Index: cfe/trunk/lib/StaticAnalyzer/Checkers/UndefResultChecker.cpp =================================================================== --- cfe/trunk/lib/StaticAnalyzer/Checkers/UndefResultChecker.cpp +++ cfe/trunk/lib/StaticAnalyzer/Checkers/UndefResultChecker.cpp @@ -55,7 +55,8 @@ return; if (!BT) - BT.reset(new BuiltinBug("Result of operation is garbage or undefined")); + BT.reset( + new BuiltinBug(this, "Result of operation is garbage or undefined")); SmallString<256> sbuf; llvm::raw_svector_ostream OS(sbuf); Index: cfe/trunk/lib/StaticAnalyzer/Checkers/UndefinedArraySubscriptChecker.cpp =================================================================== --- cfe/trunk/lib/StaticAnalyzer/Checkers/UndefinedArraySubscriptChecker.cpp +++ cfe/trunk/lib/StaticAnalyzer/Checkers/UndefinedArraySubscriptChecker.cpp @@ -50,7 +50,7 @@ if (!N) return; if (!BT) - BT.reset(new BuiltinBug("Array subscript is undefined")); + BT.reset(new BuiltinBug(this, "Array subscript is undefined")); // Generate a report for this bug. BugReport *R = new BugReport(*BT, BT->getName(), N); Index: cfe/trunk/lib/StaticAnalyzer/Checkers/UndefinedAssignmentChecker.cpp =================================================================== --- cfe/trunk/lib/StaticAnalyzer/Checkers/UndefinedAssignmentChecker.cpp +++ cfe/trunk/lib/StaticAnalyzer/Checkers/UndefinedAssignmentChecker.cpp @@ -54,7 +54,7 @@ const char *str = "Assigned value is garbage or undefined"; if (!BT) - BT.reset(new BuiltinBug(str)); + BT.reset(new BuiltinBug(this, str)); // Generate a report for this bug. const Expr *ex = 0; Index: cfe/trunk/lib/StaticAnalyzer/Checkers/UnixAPIChecker.cpp =================================================================== --- cfe/trunk/lib/StaticAnalyzer/Checkers/UnixAPIChecker.cpp +++ cfe/trunk/lib/StaticAnalyzer/Checkers/UnixAPIChecker.cpp @@ -57,21 +57,15 @@ const unsigned numArgs, const unsigned sizeArg, const char *fn) const; + void LazyInitialize(OwningPtr &BT, const char *name) const { + if (BT) + return; + BT.reset(new BugType(this, name, categories::UnixAPI)); + } }; } //end anonymous namespace //===----------------------------------------------------------------------===// -// Utility functions. -//===----------------------------------------------------------------------===// - -static inline void LazyInitialize(OwningPtr &BT, - const char *name) { - if (BT) - return; - BT.reset(new BugType(name, categories::UnixAPI)); -} - -//===----------------------------------------------------------------------===// // "open" (man 2 open) //===----------------------------------------------------------------------===// @@ -217,7 +211,7 @@ return false; LazyInitialize(BT_mallocZero, - "Undefined allocation of 0 bytes (CERT MEM04-C; CWE-131)"); + "Undefined allocation of 0 bytes (CERT MEM04-C; CWE-131)"); SmallString<256> S; llvm::raw_svector_ostream os(S); Index: cfe/trunk/lib/StaticAnalyzer/Checkers/UnreachableCodeChecker.cpp =================================================================== --- cfe/trunk/lib/StaticAnalyzer/Checkers/UnreachableCodeChecker.cpp +++ cfe/trunk/lib/StaticAnalyzer/Checkers/UnreachableCodeChecker.cpp @@ -165,7 +165,7 @@ if (SM.isInSystemHeader(SL) || SM.isInExternCSystemHeader(SL)) continue; - B.EmitBasicReport(D, "Unreachable code", "Dead code", + B.EmitBasicReport(D, this, "Unreachable code", "Dead code", "This statement is never executed", DL, SR); } } Index: cfe/trunk/lib/StaticAnalyzer/Checkers/VLASizeChecker.cpp =================================================================== --- cfe/trunk/lib/StaticAnalyzer/Checkers/VLASizeChecker.cpp +++ cfe/trunk/lib/StaticAnalyzer/Checkers/VLASizeChecker.cpp @@ -51,7 +51,8 @@ return; if (!BT) - BT.reset(new BuiltinBug("Dangerous variable-length array (VLA) declaration")); + BT.reset(new BuiltinBug( + this, "Dangerous variable-length array (VLA) declaration")); SmallString<256> buf; llvm::raw_svector_ostream os(buf); Index: cfe/trunk/lib/StaticAnalyzer/Checkers/VirtualCallChecker.cpp =================================================================== --- cfe/trunk/lib/StaticAnalyzer/Checkers/VirtualCallChecker.cpp +++ cfe/trunk/lib/StaticAnalyzer/Checkers/VirtualCallChecker.cpp @@ -28,6 +28,7 @@ namespace { class WalkAST : public StmtVisitor { + const CheckerBase *Checker; BugReporter &BR; AnalysisDeclContext *AC; @@ -58,11 +59,10 @@ const CallExpr *visitingCallExpr; public: - WalkAST(BugReporter &br, AnalysisDeclContext *ac) - : BR(br), - AC(ac), - visitingCallExpr(0) {} - + WalkAST(const CheckerBase *checker, BugReporter &br, + AnalysisDeclContext *ac) + : Checker(checker), BR(br), AC(ac), visitingCallExpr(0) {} + bool hasWork() const { return !WList.empty(); } /// This method adds a CallExpr to the worklist and marks the callee as @@ -187,21 +187,19 @@ if (isPure) { os << "\n" << "Call pure virtual functions during construction or " << "destruction may leads undefined behaviour"; - BR.EmitBasicReport(AC->getDecl(), + BR.EmitBasicReport(AC->getDecl(), Checker, "Call pure virtual function during construction or " "Destruction", - "Cplusplus", - os.str(), CELoc, R); + "Cplusplus", os.str(), CELoc, R); return; } else { os << "\n" << "Call virtual functions during construction or " << "destruction will never go to a more derived class"; - BR.EmitBasicReport(AC->getDecl(), + BR.EmitBasicReport(AC->getDecl(), Checker, "Call virtual function during construction or " "Destruction", - "Cplusplus", - os.str(), CELoc, R); + "Cplusplus", os.str(), CELoc, R); return; } } @@ -215,7 +213,7 @@ public: void checkASTDecl(const CXXRecordDecl *RD, AnalysisManager& mgr, BugReporter &BR) const { - WalkAST walker(BR, mgr.getAnalysisDeclContext(RD)); + WalkAST walker(this, BR, mgr.getAnalysisDeclContext(RD)); // Check the constructors. for (CXXRecordDecl::ctor_iterator I = RD->ctor_begin(), E = RD->ctor_end(); Index: cfe/trunk/lib/StaticAnalyzer/Core/BugReporter.cpp =================================================================== --- cfe/trunk/lib/StaticAnalyzer/Core/BugReporter.cpp +++ cfe/trunk/lib/StaticAnalyzer/Core/BugReporter.cpp @@ -3420,7 +3420,8 @@ BugType& BT = exampleReport->getBugType(); OwningPtr - D(new PathDiagnostic(exampleReport->getDeclWithIssue(), + D(new PathDiagnostic(exampleReport->getBugType().getCheckName(), + exampleReport->getDeclWithIssue(), exampleReport->getBugType().getName(), exampleReport->getDescription(), exampleReport->getShortDescription(/*Fallback=*/false), @@ -3472,13 +3473,21 @@ } void BugReporter::EmitBasicReport(const Decl *DeclWithIssue, - StringRef name, - StringRef category, + const CheckerBase *Checker, + StringRef Name, StringRef Category, + StringRef Str, PathDiagnosticLocation Loc, + ArrayRef Ranges) { + EmitBasicReport(DeclWithIssue, Checker->getCheckName(), Name, Category, Str, + Loc, Ranges); +} +void BugReporter::EmitBasicReport(const Decl *DeclWithIssue, + CheckName CheckName, + StringRef name, StringRef category, StringRef str, PathDiagnosticLocation Loc, ArrayRef Ranges) { // 'BT' is owned by BugReporter. - BugType *BT = getBugTypeForName(name, category); + BugType *BT = getBugTypeForName(CheckName, name, category); BugReport *R = new BugReport(*BT, str, Loc); R->setDeclWithIssue(DeclWithIssue); for (ArrayRef::iterator I = Ranges.begin(), E = Ranges.end(); @@ -3487,15 +3496,16 @@ emitReport(R); } -BugType *BugReporter::getBugTypeForName(StringRef name, +BugType *BugReporter::getBugTypeForName(CheckName CheckName, StringRef name, StringRef category) { SmallString<136> fullDesc; - llvm::raw_svector_ostream(fullDesc) << name << ":" << category; + llvm::raw_svector_ostream(fullDesc) << CheckName.getName() << ":" << name + << ":" << category; llvm::StringMapEntry & entry = StrBugTypes.GetOrCreateValue(fullDesc); BugType *BT = entry.getValue(); if (!BT) { - BT = new BugType(name, category); + BT = new BugType(CheckName, name, category); entry.setValue(BT); } return BT; Index: cfe/trunk/lib/StaticAnalyzer/Core/Checker.cpp =================================================================== --- cfe/trunk/lib/StaticAnalyzer/Core/Checker.cpp +++ cfe/trunk/lib/StaticAnalyzer/Core/Checker.cpp @@ -22,6 +22,8 @@ return "A Checker"; } +CheckName CheckerBase::getCheckName() const { return Name; } + void CheckerFullName)); (*i)->Initialize(checkerMgr); } } Index: cfe/trunk/lib/StaticAnalyzer/Core/PathDiagnostic.cpp =================================================================== --- cfe/trunk/lib/StaticAnalyzer/Core/PathDiagnostic.cpp +++ cfe/trunk/lib/StaticAnalyzer/Core/PathDiagnostic.cpp @@ -106,12 +106,13 @@ PathDiagnostic::~PathDiagnostic() {} -PathDiagnostic::PathDiagnostic(const Decl *declWithIssue, +PathDiagnostic::PathDiagnostic(StringRef CheckName, const Decl *declWithIssue, StringRef bugtype, StringRef verboseDesc, StringRef shortDesc, StringRef category, PathDiagnosticLocation LocationToUnique, const Decl *DeclToUnique) - : DeclWithIssue(declWithIssue), + : CheckName(CheckName), + DeclWithIssue(declWithIssue), BugType(StripTrailingDots(bugtype)), VerboseDesc(StripTrailingDots(verboseDesc)), ShortDesc(StripTrailingDots(shortDesc)),