Index: include/clang/StaticAnalyzer/Checkers/CheckerBase.td =================================================================== --- include/clang/StaticAnalyzer/Checkers/CheckerBase.td +++ include/clang/StaticAnalyzer/Checkers/CheckerBase.td @@ -37,8 +37,23 @@ /// Note that a checker has a name (e.g.: 'NullDereference'), and a fullname, /// that is autogenerated with the help of the ParentPackage field, that also /// includes package names (e.g.: 'core.NullDereference'). +/// Example: +/// def DereferenceChecker : Checker<"NullDereference">, +/// HelpText<"Check for dereferences of null pointers">; class Checker { - string CheckerName = name; - string HelpText; - Package ParentPackage; + string CheckerName = name; + string HelpText; + Package ParentPackage; + list Dependencies; +} + +/// Describes dependencies in between checkers. For example, InnerPointerChecker +/// relies on information MallocBase gathers. +/// Example: +/// def InnerPointerChecker : Checker<"InnerPointer">, +/// HelpText<"Check for inner pointers of C++ containers used after " +/// "re/deallocation">, +/// Dependencies<[MallocBase]>; +class Dependencies Deps = []> { + list Dependencies = Deps; } Index: include/clang/StaticAnalyzer/Checkers/Checkers.td =================================================================== --- include/clang/StaticAnalyzer/Checkers/Checkers.td +++ include/clang/StaticAnalyzer/Checkers/Checkers.td @@ -122,8 +122,12 @@ def UndefResultChecker : Checker<"UndefinedBinaryOperatorResult">, HelpText<"Check for undefined results of binary operators">; +def StackAddrEscapeBase : Checker<"StackAddrEscapeBase">, + HelpText<"Generate information about stack address escapes.">; + def StackAddrEscapeChecker : Checker<"StackAddressEscape">, - HelpText<"Check that addresses to stack memory do not escape the function">; + HelpText<"Check that addresses to stack memory do not escape the function">, + Dependencies<[StackAddrEscapeBase]>; def DynamicTypePropagation : Checker<"DynamicTypePropagation">, HelpText<"Generate dynamic type information">; @@ -168,7 +172,8 @@ def CallAndMessageUnInitRefArg : Checker<"CallAndMessageUnInitRefArg">, HelpText<"Check for logical errors for function calls and Objective-C " "message expressions (e.g., uninitialized arguments, null function " - "pointers, and pointer to undefined variables)">; + "pointers, and pointer to undefined variables)">, + Dependencies<[CallAndMessageChecker]>; def TestAfterDivZeroChecker : Checker<"TestAfterDivZero">, HelpText<"Check for division by variable that is later compared against 0. " @@ -179,33 +184,50 @@ "are unrelated.">; def StackAddrAsyncEscapeChecker : Checker<"StackAddressAsyncEscape">, - HelpText<"Check that addresses to stack memory do not escape the function">; + HelpText<"Check that addresses to stack memory do not escape the function">, + Dependencies<[StackAddrEscapeBase]>; } // end "alpha.core" +//===----------------------------------------------------------------------===// +// Nullability checkers. +//===----------------------------------------------------------------------===// + let ParentPackage = Nullability in { +def NullabilityBase : Checker<"NullabilityBase">, + HelpText<"Stores information during the analysis about nullability.">; + def NullPassedToNonnullChecker : Checker<"NullPassedToNonnull">, HelpText<"Warns when a null pointer is passed to a pointer which has a " - "_Nonnull type.">; + "_Nonnull type.">, + Dependencies<[NullabilityBase]>; def NullReturnedFromNonnullChecker : Checker<"NullReturnedFromNonnull">, HelpText<"Warns when a null pointer is returned from a function that has " - "_Nonnull return type.">; + "_Nonnull return type.">, + Dependencies<[NullabilityBase]>; def NullableDereferencedChecker : Checker<"NullableDereferenced">, - HelpText<"Warns when a nullable pointer is dereferenced.">; + HelpText<"Warns when a nullable pointer is dereferenced.">, + Dependencies<[NullabilityBase]>; def NullablePassedToNonnullChecker : Checker<"NullablePassedToNonnull">, HelpText<"Warns when a nullable pointer is passed to a pointer which has a " - "_Nonnull type.">; + "_Nonnull type.">, + Dependencies<[NullabilityBase]>; def NullableReturnedFromNonnullChecker : Checker<"NullableReturnedFromNonnull">, HelpText<"Warns when a nullable pointer is returned from a function that has " - "_Nonnull return type.">; + "_Nonnull return type.">, + Dependencies<[NullabilityBase]>; } // end "nullability" +//===----------------------------------------------------------------------===// +// APIModeling. +//===----------------------------------------------------------------------===// + let ParentPackage = APIModeling in { def StdCLibraryFunctionsChecker : Checker<"StdCLibraryFunctions">, @@ -255,22 +277,111 @@ } // end "core.uninitialized" +//===----------------------------------------------------------------------===// +// Unix API checkers. +//===----------------------------------------------------------------------===// + +let ParentPackage = UnixAlpha in { + +def ChrootChecker : Checker<"Chroot">, + HelpText<"Check improper use of chroot">; + +def PthreadLockChecker : Checker<"PthreadLock">, + HelpText<"Simple lock -> unlock checker">; + +def StreamChecker : Checker<"Stream">, + HelpText<"Check stream handling functions">; + +def SimpleStreamChecker : Checker<"SimpleStream">, + HelpText<"Check for misuses of stream APIs">; + +def BlockInCriticalSectionChecker : Checker<"BlockInCriticalSection">, + HelpText<"Check for calls to blocking functions inside a critical section">; + +} // end "alpha.unix" + +let ParentPackage = CString in { + +def CStringModeling : Checker<"CStringModeling">, + HelpText<"The base of several CString related checkers. On it's own it emits " + "no reports, but adds valuable information to the analysis when " + "enabled.">; + +def CStringNullArg : Checker<"NullArg">, + HelpText<"Check for null pointers being passed as arguments to C string " + "functions">, + Dependencies<[CStringModeling]>; + +def CStringSyntaxChecker : Checker<"BadSizeArg">, + HelpText<"Check the size argument passed into C string functions for common " + "erroneous patterns">, + Dependencies<[CStringModeling]>; + +} // end "unix.cstring" + +let ParentPackage = CStringAlpha in { + +def CStringOutOfBounds : Checker<"OutOfBounds">, + HelpText<"Check for out-of-bounds access in string functions">, + Dependencies<[CStringModeling]>; + +def CStringBufferOverlap : Checker<"BufferOverlap">, + HelpText<"Checks for overlap in two buffer arguments">, + Dependencies<[CStringModeling]>; + +def CStringNotNullTerm : Checker<"NotNullTerminated">, + HelpText<"Check for arguments which are not null-terminating strings">, + Dependencies<[CStringModeling]>; + +} // end "alpha.unix.cstring" + +let ParentPackage = Unix in { + +def UnixAPIMisuseChecker : Checker<"API">, + HelpText<"Check calls to various UNIX/Posix functions">; + +def DynamicMemoryModeling: Checker<"DynamicMemoryModeling">, + HelpText<"The base of several malloc() related checkers. On it's own it " + "emits no reports, but adds valuable information to the analysis " + "when enabled.">, + Dependencies<[CStringModeling]>; + +def MallocChecker: Checker<"Malloc">, + HelpText<"Check for memory leaks, double free, and use-after-free problems. " + "Traces memory managed by malloc()/free().">, + Dependencies<[DynamicMemoryModeling]>; + +def MallocSizeofChecker : Checker<"MallocSizeof">, + HelpText<"Check for dubious malloc arguments involving sizeof">; + +def MismatchedDeallocatorChecker : Checker<"MismatchedDeallocator">, + HelpText<"Check for mismatched deallocators.">, + Dependencies<[DynamicMemoryModeling]>; + +def VforkChecker : Checker<"Vfork">, + HelpText<"Check for proper usage of vfork">; + +} // end "unix" + //===----------------------------------------------------------------------===// // C++ checkers. //===----------------------------------------------------------------------===// let ParentPackage = Cplusplus in { -def InnerPointerChecker : Checker<"InnerPointer">, - HelpText<"Check for inner pointers of C++ containers used after " - "re/deallocation">; - def NewDeleteChecker : Checker<"NewDelete">, HelpText<"Check for double-free and use-after-free problems. Traces memory " - "managed by new/delete.">; + "managed by new/delete.">, + Dependencies<[DynamicMemoryModeling]>; + +def InnerPointerChecker : Checker<"InnerPointer">, + HelpText<"Check for inner pointers of C++ containers used after " + "re/deallocation">, + Dependencies<[DynamicMemoryModeling]>; def NewDeleteLeaksChecker : Checker<"NewDeleteLeaks">, - HelpText<"Check for memory leaks. Traces memory managed by new/delete.">; + HelpText<"Check for memory leaks. Traces memory managed by new/delete.">, + Dependencies<[NewDeleteChecker]>; def CXXSelfAssignmentChecker : Checker<"SelfAssignment">, HelpText<"Checks C++ copy and move assignment operators for self assignment">; @@ -293,15 +404,21 @@ def EnumCastOutOfRangeChecker : Checker<"EnumCastOutOfRange">, HelpText<"Check integer to enumeration casts for out of range values">; +def IteratorModeling : Checker<"IteratorModeling">, + HelpText<"Models iterators of C++ containers">; + def InvalidatedIteratorChecker : Checker<"InvalidatedIterator">, - HelpText<"Check for use of invalidated iterators">; + HelpText<"Check for use of invalidated iterators">, + Dependencies<[IteratorModeling]>; def IteratorRangeChecker : Checker<"IteratorRange">, - HelpText<"Check for iterators used outside their valid ranges">; + HelpText<"Check for iterators used outside their valid ranges">, + Dependencies<[IteratorModeling]>; def MismatchedIteratorChecker : Checker<"MismatchedIterator">, HelpText<"Check for use of iterators of different containers where iterators " - "of the same container are expected">; + "of the same container are expected">, + Dependencies<[IteratorModeling]>; def MisusedMovedObjectChecker: Checker<"MisusedMovedObject">, HelpText<"Method calls on a moved-from object and copying a moved-from " @@ -319,14 +436,20 @@ let ParentPackage = Valist in { +def ValistBase : Checker<"ValistBase">, + HelpText<"Gathers information about va_lists.">; + def UninitializedChecker : Checker<"Uninitialized">, - HelpText<"Check for usages of uninitialized (or already released) va_lists.">; + HelpText<"Check for usages of uninitialized (or already released) va_lists.">, + Dependencies<[ValistBase]>; def UnterminatedChecker : Checker<"Unterminated">, - HelpText<"Check for va_lists which are not released by a va_end call.">; + HelpText<"Check for va_lists which are not released by a va_end call.">, + Dependencies<[ValistBase]>; def CopyToSelfChecker : Checker<"CopyToSelf">, - HelpText<"Check for va_lists which are copied onto itself.">; + HelpText<"Check for va_lists which are copied onto itself.">, + Dependencies<[ValistBase]>; } // end : "valist" @@ -366,30 +489,54 @@ let ParentPackage = InsecureAPI in { +def SecuritySyntaxChecker : Checker<"SecuritySyntaxChecker">, + HelpText<"Base of various security function related checkers">; + def bcmp : Checker<"bcmp">, - HelpText<"Warn on uses of the 'bcmp' function">; + HelpText<"Warn on uses of the 'bcmp' function">, + Dependencies<[SecuritySyntaxChecker]>; + def bcopy : Checker<"bcopy">, - HelpText<"Warn on uses of the 'bcopy' function">; + HelpText<"Warn on uses of the 'bcopy' function">, + Dependencies<[SecuritySyntaxChecker]>; + def bzero : Checker<"bzero">, - HelpText<"Warn on uses of the 'bzero' function">; + HelpText<"Warn on uses of the 'bzero' function">, + Dependencies<[SecuritySyntaxChecker]>; + def gets : Checker<"gets">, - HelpText<"Warn on uses of the 'gets' function">; + HelpText<"Warn on uses of the 'gets' function">, + Dependencies<[SecuritySyntaxChecker]>; + def getpw : Checker<"getpw">, - HelpText<"Warn on uses of the 'getpw' function">; + HelpText<"Warn on uses of the 'getpw' function">, + Dependencies<[SecuritySyntaxChecker]>; + def mktemp : Checker<"mktemp">, - HelpText<"Warn on uses of the 'mktemp' function">; + HelpText<"Warn on uses of the 'mktemp' function">, + Dependencies<[SecuritySyntaxChecker]>; + def mkstemp : Checker<"mkstemp">, HelpText<"Warn when 'mkstemp' is passed fewer than 6 X's in the format " - "string">; + "string">, + Dependencies<[SecuritySyntaxChecker]>; + def rand : Checker<"rand">, - HelpText<"Warn on uses of the 'rand', 'random', and related functions">; + HelpText<"Warn on uses of the 'rand', 'random', and related functions">, + Dependencies<[SecuritySyntaxChecker]>; + def strcpy : Checker<"strcpy">, - HelpText<"Warn on uses of the 'strcpy' and 'strcat' functions">; + HelpText<"Warn on uses of the 'strcpy' and 'strcat' functions">, + Dependencies<[SecuritySyntaxChecker]>; + def vfork : Checker<"vfork">, - HelpText<"Warn on uses of the 'vfork' function">; + HelpText<"Warn on uses of the 'vfork' function">, + Dependencies<[SecuritySyntaxChecker]>; + def UncheckedReturn : Checker<"UncheckedReturn">, HelpText<"Warn on uses of functions whose return values must be always " - "checked">; + "checked">, + Dependencies<[SecuritySyntaxChecker]>; } // end "security.insecureAPI" @@ -434,74 +581,6 @@ } // end "alpha.security.taint" -//===----------------------------------------------------------------------===// -// Unix API checkers. -//===----------------------------------------------------------------------===// - -let ParentPackage = Unix in { - -def UnixAPIMisuseChecker : Checker<"API">, - HelpText<"Check calls to various UNIX/Posix functions">; - -def MallocChecker: Checker<"Malloc">, - HelpText<"Check for memory leaks, double free, and use-after-free problems. " - "Traces memory managed by malloc()/free().">; - -def MallocSizeofChecker : Checker<"MallocSizeof">, - HelpText<"Check for dubious malloc arguments involving sizeof">; - -def MismatchedDeallocatorChecker : Checker<"MismatchedDeallocator">, - HelpText<"Check for mismatched deallocators.">; - -def VforkChecker : Checker<"Vfork">, - HelpText<"Check for proper usage of vfork">; - -} // end "unix" - -let ParentPackage = UnixAlpha in { - -def ChrootChecker : Checker<"Chroot">, - HelpText<"Check improper use of chroot">; - -def PthreadLockChecker : Checker<"PthreadLock">, - HelpText<"Simple lock -> unlock checker">; - -def StreamChecker : Checker<"Stream">, - HelpText<"Check stream handling functions">; - -def SimpleStreamChecker : Checker<"SimpleStream">, - HelpText<"Check for misuses of stream APIs">; - -def BlockInCriticalSectionChecker : Checker<"BlockInCriticalSection">, - HelpText<"Check for calls to blocking functions inside a critical section">; - -} // end "alpha.unix" - -let ParentPackage = CString in { - -def CStringNullArg : Checker<"NullArg">, - HelpText<"Check for null pointers being passed as arguments to C string " - "functions">; - -def CStringSyntaxChecker : Checker<"BadSizeArg">, - HelpText<"Check the size argument passed into C string functions for common " - "erroneous patterns">; - -} // end "unix.cstring" - -let ParentPackage = CStringAlpha in { - -def CStringOutOfBounds : Checker<"OutOfBounds">, - HelpText<"Check for out-of-bounds access in string functions">; - -def CStringBufferOverlap : Checker<"BufferOverlap">, - HelpText<"Checks for overlap in two buffer arguments">; - -def CStringNotNullTerm : Checker<"NotNullTerminated">, - HelpText<"Check for arguments which are not null-terminating strings">; - -} // end "alpha.unix.cstring" - //===----------------------------------------------------------------------===// // Mac OS X, Cocoa, and Core Foundation checkers. //===----------------------------------------------------------------------===// @@ -521,6 +600,9 @@ def ObjCPropertyChecker : Checker<"ObjCProperty">, HelpText<"Check for proper uses of Objective-C properties">; +def NSOrCFErrorDerefChecker : Checker<"NSOrCFErrorDerefChecker">, + HelpText<"Implementation checker for NSErrorChecker and CFErrorChecker">; + } // end "osx" let ParentPackage = Cocoa in { @@ -569,13 +651,15 @@ "super">; def NSErrorChecker : Checker<"NSError">, - HelpText<"Check usage of NSError** parameters">; + HelpText<"Check usage of NSError** parameters">, + Dependencies<[NSOrCFErrorDerefChecker]>; def RetainCountChecker : Checker<"RetainCount">, HelpText<"Check for leaks and improper reference count management">; def ObjCGenericsChecker : Checker<"ObjCGenerics">, - HelpText<"Check for type errors when using Objective-C generics">; + HelpText<"Check for type errors when using Objective-C generics">, + Dependencies<[DynamicTypePropagation]>; def ObjCDeallocChecker : Checker<"Dealloc">, HelpText<"Warn about Objective-C classes that lack a correct implementation " @@ -599,13 +683,20 @@ let ParentPackage = CocoaAlpha in { +def IvarInvalidationModeling : Checker<"IvarInvalidationModeling">, + HelpText<"Gathers information for annotation driven invalidation checking " + "for classes that contains a method annotated with " + "'objc_instance_variable_invalidator'">; + def InstanceVariableInvalidation : Checker<"InstanceVariableInvalidation">, HelpText<"Check that the invalidatable instance variables are invalidated in " - "the methods annotated with objc_instance_variable_invalidator">; + "the methods annotated with objc_instance_variable_invalidator">, + Dependencies<[IvarInvalidationModeling]>; def MissingInvalidationMethod : Checker<"MissingInvalidationMethod">, HelpText<"Check that the invalidation methods are present in classes that " - "contain invalidatable instance variables">; + "contain invalidatable instance variables">, + Dependencies<[IvarInvalidationModeling]>; def DirectIvarAssignment : Checker<"DirectIvarAssignment">, HelpText<"Check for direct assignments to instance variables">; @@ -613,7 +704,8 @@ def DirectIvarAssignmentForAnnotatedFunctions : Checker<"DirectIvarAssignmentForAnnotatedFunctions">, HelpText<"Check for direct assignments to instance variables in the methods " - "annotated with objc_no_direct_instance_variable_assignment">; + "annotated with objc_no_direct_instance_variable_assignment">, + Dependencies<[DirectIvarAssignment]>; } // end "alpha.osx.cocoa" @@ -626,7 +718,8 @@ HelpText<"Check for null arguments to CFRetain/CFRelease/CFMakeCollectable">; def CFErrorChecker : Checker<"CFError">, - HelpText<"Check usage of CFErrorRef* parameters">; + HelpText<"Check usage of CFErrorRef* parameters">, + Dependencies<[NSOrCFErrorDerefChecker]>; } // end "osx.coreFoundation" Index: include/clang/StaticAnalyzer/Frontend/CheckerRegistry.h =================================================================== --- include/clang/StaticAnalyzer/Frontend/CheckerRegistry.h +++ include/clang/StaticAnalyzer/Frontend/CheckerRegistry.h @@ -90,20 +90,24 @@ using InitializationFunction = void (*)(CheckerManager &); using ShouldRegisterFunction = bool (*)(const LangOptions &); + struct CheckerInfo; + + using CheckerInfoList = std::vector; + using ConstCheckerInfoList = llvm::SmallVector; + using CheckerInfoSet = llvm::SetVector; + struct CheckerInfo { InitializationFunction Initialize; ShouldRegisterFunction ShouldRegister; StringRef FullName; StringRef Desc; + llvm::SmallVector Dependencies; CheckerInfo(InitializationFunction rfn, ShouldRegisterFunction sfn, StringRef name, StringRef desc) : Initialize(rfn), ShouldRegister(sfn), FullName(name), Desc(desc) {} }; - using CheckerInfoList = std::vector; - using CheckerInfoSet = llvm::SetVector; - private: template static void initializeManager(CheckerManager &mgr) { @@ -132,6 +136,28 @@ &CheckerRegistry::returnTrue, fullName, desc); } + /// Makes the checker with the full name \p fullName depends on the checker + /// called \p dependency. + void addDependency(StringRef fullName, StringRef dependency) { + auto CheckerThatNeedsDeps = + [&fullName](const CheckerInfo &Chk) { return Chk.FullName == fullName; }; + auto Dependency = + [&dependency](const CheckerInfo &Chk) { + return Chk.FullName == dependency; + }; + + auto CheckerIt = llvm::find_if(Checkers, CheckerThatNeedsDeps); + assert(CheckerIt != Checkers.end() && + "Failed to find the checker while attempting to set up it's " + "dependencies!"); + + auto DependencyIt = llvm::find_if(Checkers, Dependency); + assert(DependencyIt != Checkers.end() && + "Failed to find the dependency of a checker!"); + + CheckerIt->Dependencies.push_back(&*DependencyIt); + } + /// Initializes a CheckerManager by calling the initialization functions for /// all checkers specified by the given CheckerOptInfo list. The order of this /// list is significant; later options can be used to reverse earlier ones. @@ -148,10 +174,13 @@ void printList(raw_ostream &out, const AnalyzerOptions &opts) const; private: + /// Collect all enabled checkers. The returned container preserves the order + /// of insertion, as dependencies have to be enabled before the checkers that + /// depend on them. CheckerInfoSet getEnabledCheckers(const AnalyzerOptions &Opts) const; - mutable CheckerInfoList Checkers; - mutable llvm::StringMap Packages; + CheckerInfoList Checkers; + llvm::StringMap Packages; DiagnosticsEngine &Diags; const LangOptions &LangOpts; Index: lib/StaticAnalyzer/Checkers/CStringChecker.cpp =================================================================== --- lib/StaticAnalyzer/Checkers/CStringChecker.cpp +++ lib/StaticAnalyzer/Checkers/CStringChecker.cpp @@ -2404,6 +2404,14 @@ C.addTransition(state); } +void ento::registerCStringModeling(CheckerManager &Mgr) { + Mgr.registerChecker(); +} + +bool ento::shouldRegisterCStringModeling(const LangOptions &LO) { + return true; +} + #define REGISTER_CHECKER(name) \ void ento::register##name(CheckerManager &mgr) { \ CStringChecker *checker = mgr.registerChecker(); \ @@ -2419,7 +2427,3 @@ REGISTER_CHECKER(CStringOutOfBounds) REGISTER_CHECKER(CStringBufferOverlap) REGISTER_CHECKER(CStringNotNullTerm) - - void ento::registerCStringCheckerBasic(CheckerManager &Mgr) { - Mgr.registerChecker(); - } Index: lib/StaticAnalyzer/Checkers/CallAndMessageChecker.cpp =================================================================== --- lib/StaticAnalyzer/Checkers/CallAndMessageChecker.cpp +++ lib/StaticAnalyzer/Checkers/CallAndMessageChecker.cpp @@ -29,14 +29,6 @@ namespace { -struct ChecksFilter { - DefaultBool Check_CallAndMessageUnInitRefArg; - DefaultBool Check_CallAndMessageChecker; - - CheckName CheckName_CallAndMessageUnInitRefArg; - CheckName CheckName_CallAndMessageChecker; -}; - class CallAndMessageChecker : public Checker< check::PreStmt, check::PreStmt, @@ -57,7 +49,8 @@ mutable std::unique_ptr BT_call_few_args; public: - ChecksFilter Filter; + DefaultBool Check_CallAndMessageUnInitRefArg; + CheckName CheckName_CallAndMessageUnInitRefArg; void checkPreStmt(const CallExpr *CE, CheckerContext &C) const; void checkPreStmt(const CXXDeleteExpr *DE, CheckerContext &C) const; @@ -152,7 +145,7 @@ CheckerContext &C, const SVal &V, SourceRange ArgRange, const Expr *ArgEx, std::unique_ptr &BT, const ParmVarDecl *ParamDecl, const char *BD, int ArgumentNumber) const { - if (!Filter.Check_CallAndMessageUnInitRefArg) + if (!Check_CallAndMessageUnInitRefArg) return false; // No parameter declaration available, i.e. variadic function argument. @@ -608,17 +601,21 @@ C.addTransition(state); } -#define REGISTER_CHECKER(name) \ - void ento::register##name(CheckerManager &mgr) { \ - CallAndMessageChecker *Checker = \ - mgr.registerChecker(); \ - Checker->Filter.Check_##name = true; \ - Checker->Filter.CheckName_##name = mgr.getCurrentCheckName(); \ - } \ - \ - bool ento::shouldRegister##name(const LangOptions &LO) { \ - return true; \ - } +void ento::registerCallAndMessageChecker(CheckerManager &mgr) { + mgr.registerChecker(); +} + +bool ento::shouldRegisterCallAndMessageChecker(const LangOptions &LO) { + return true; +} -REGISTER_CHECKER(CallAndMessageUnInitRefArg) -REGISTER_CHECKER(CallAndMessageChecker) +void ento::registerCallAndMessageUnInitRefArg(CheckerManager &mgr) { + CallAndMessageChecker *Checker = + mgr.registerChecker(); + Checker->Check_CallAndMessageUnInitRefArg = true; + Checker->CheckName_CallAndMessageUnInitRefArg = mgr.getCurrentCheckName(); +} + +bool ento::shouldRegisterCallAndMessageUnInitRefArg(const LangOptions &LO) { + return true; +} Index: lib/StaticAnalyzer/Checkers/CheckSecuritySyntaxOnly.cpp =================================================================== --- lib/StaticAnalyzer/Checkers/CheckSecuritySyntaxOnly.cpp +++ lib/StaticAnalyzer/Checkers/CheckSecuritySyntaxOnly.cpp @@ -905,6 +905,14 @@ }; } +void ento::registerSecuritySyntaxChecker(CheckerManager &mgr) { + mgr.registerChecker(); +} + +bool ento::shouldRegisterSecuritySyntaxChecker(const LangOptions &LO) { + return true; +} + #define REGISTER_CHECKER(name) \ void ento::register##name(CheckerManager &mgr) { \ SecuritySyntaxChecker *checker = \ Index: lib/StaticAnalyzer/Checkers/InterCheckerAPI.h =================================================================== --- lib/StaticAnalyzer/Checkers/InterCheckerAPI.h +++ lib/StaticAnalyzer/Checkers/InterCheckerAPI.h @@ -17,9 +17,6 @@ namespace ento { -/// Register the checker which evaluates CString API calls. -void registerCStringCheckerBasic(CheckerManager &Mgr); - /// Register the part of MallocChecker connected to InnerPointerChecker. void registerInnerPointerCheckerAux(CheckerManager &Mgr); Index: lib/StaticAnalyzer/Checkers/IteratorChecker.cpp =================================================================== --- lib/StaticAnalyzer/Checkers/IteratorChecker.cpp +++ lib/StaticAnalyzer/Checkers/IteratorChecker.cpp @@ -2349,6 +2349,14 @@ } // namespace +void ento::registerIteratorModeling(CheckerManager &mgr) { + mgr.registerChecker(); +} + +bool ento::shouldRegisterIteratorModeling(const LangOptions &LO) { + return true; +} + #define REGISTER_CHECKER(name) \ void ento::register##name(CheckerManager &Mgr) { \ auto *checker = Mgr.registerChecker(); \ Index: lib/StaticAnalyzer/Checkers/IvarInvalidationChecker.cpp =================================================================== --- lib/StaticAnalyzer/Checkers/IvarInvalidationChecker.cpp +++ lib/StaticAnalyzer/Checkers/IvarInvalidationChecker.cpp @@ -736,6 +736,14 @@ }; } // end anonymous namespace +void ento::registerIvarInvalidationModeling(CheckerManager &mgr) { + mgr.registerChecker(); +} + +bool ento::shouldRegisterIvarInvalidationModeling(const LangOptions &LO) { + return true; +} + #define REGISTER_CHECKER(name) \ void ento::register##name(CheckerManager &mgr) { \ IvarInvalidationChecker *checker = \ Index: lib/StaticAnalyzer/Checkers/MallocChecker.cpp =================================================================== --- lib/StaticAnalyzer/Checkers/MallocChecker.cpp +++ lib/StaticAnalyzer/Checkers/MallocChecker.cpp @@ -3087,33 +3087,9 @@ } // end namespace ento } // end namespace clang -void ento::registerNewDeleteLeaksChecker(CheckerManager &mgr) { - registerCStringCheckerBasic(mgr); - MallocChecker *checker = mgr.registerChecker(); - checker->IsOptimistic = mgr.getAnalyzerOptions().getCheckerBooleanOption( - "Optimistic", false, checker); - checker->ChecksEnabled[MallocChecker::CK_NewDeleteLeaksChecker] = true; - checker->CheckNames[MallocChecker::CK_NewDeleteLeaksChecker] = - mgr.getCurrentCheckName(); - // We currently treat NewDeleteLeaks checker as a subchecker of NewDelete - // checker. - if (!checker->ChecksEnabled[MallocChecker::CK_NewDeleteChecker]) { - checker->ChecksEnabled[MallocChecker::CK_NewDeleteChecker] = true; - // FIXME: This does not set the correct name, but without this workaround - // no name will be set at all. - checker->CheckNames[MallocChecker::CK_NewDeleteChecker] = - mgr.getCurrentCheckName(); - } -} - -bool ento::shouldRegisterNewDeleteLeaksChecker(const LangOptions &LO) { - return true; -} - // Intended to be used in InnerPointerChecker to register the part of // MallocChecker connected to it. void ento::registerInnerPointerCheckerAux(CheckerManager &mgr) { - registerCStringCheckerBasic(mgr); MallocChecker *checker = mgr.registerChecker(); checker->IsOptimistic = mgr.getAnalyzerOptions().getCheckerBooleanOption( "Optimistic", false, checker); @@ -3122,9 +3098,16 @@ mgr.getCurrentCheckName(); } +void ento::registerDynamicMemoryModeling(CheckerManager &mgr) { + mgr.registerChecker(); +} + +bool ento::shouldRegisterDynamicMemoryModeling(const LangOptions &LO) { + return true; +} + #define REGISTER_CHECKER(name) \ void ento::register##name(CheckerManager &mgr) { \ - registerCStringCheckerBasic(mgr); \ MallocChecker *checker = mgr.registerChecker(); \ checker->IsOptimistic = mgr.getAnalyzerOptions().getCheckerBooleanOption( \ "Optimistic", false, checker); \ @@ -3138,4 +3121,5 @@ REGISTER_CHECKER(MallocChecker) REGISTER_CHECKER(NewDeleteChecker) +REGISTER_CHECKER(NewDeleteLeaksChecker) REGISTER_CHECKER(MismatchedDeallocatorChecker) Index: lib/StaticAnalyzer/Checkers/NSErrorChecker.cpp =================================================================== --- lib/StaticAnalyzer/Checkers/NSErrorChecker.cpp +++ lib/StaticAnalyzer/Checkers/NSErrorChecker.cpp @@ -308,6 +308,14 @@ return TT->getDecl()->getIdentifier() == II; } +void ento::registerNSOrCFErrorDerefChecker(CheckerManager &mgr) { + mgr.registerChecker(); +} + +bool ento::shouldRegisterNSOrCFErrorDerefChecker(const LangOptions &LO) { + return true; +} + void ento::registerNSErrorChecker(CheckerManager &mgr) { mgr.registerChecker(); NSOrCFErrorDerefChecker *checker = Index: lib/StaticAnalyzer/Checkers/NullabilityChecker.cpp =================================================================== --- lib/StaticAnalyzer/Checkers/NullabilityChecker.cpp +++ lib/StaticAnalyzer/Checkers/NullabilityChecker.cpp @@ -1182,6 +1182,14 @@ } } +void ento::registerNullabilityBase(CheckerManager &mgr) { + mgr.registerChecker(); +} + +bool ento::shouldRegisterNullabilityBase(const LangOptions &LO) { + return true; +} + #define REGISTER_CHECKER(name, trackingRequired) \ void ento::register##name##Checker(CheckerManager &mgr) { \ NullabilityChecker *checker = mgr.registerChecker(); \ Index: lib/StaticAnalyzer/Checkers/StackAddrEscapeChecker.cpp =================================================================== --- lib/StaticAnalyzer/Checkers/StackAddrEscapeChecker.cpp +++ lib/StaticAnalyzer/Checkers/StackAddrEscapeChecker.cpp @@ -360,6 +360,14 @@ } } +void ento::registerStackAddrEscapeBase(CheckerManager &mgr) { + mgr.registerChecker(); +} + +bool ento::shouldRegisterStackAddrEscapeBase(const LangOptions &LO) { + return true; +} + #define REGISTER_CHECKER(name) \ void ento::register##name(CheckerManager &Mgr) { \ StackAddrEscapeChecker *Chk = \ Index: lib/StaticAnalyzer/Checkers/ValistChecker.cpp =================================================================== --- lib/StaticAnalyzer/Checkers/ValistChecker.cpp +++ lib/StaticAnalyzer/Checkers/ValistChecker.cpp @@ -400,6 +400,14 @@ return std::make_shared(Pos, Msg, true); } +void ento::registerValistBase(CheckerManager &mgr) { + mgr.registerChecker(); +} + +bool ento::shouldRegisterValistBase(const LangOptions &LO) { + return true; +} + #define REGISTER_CHECKER(name) \ void ento::register##name##Checker(CheckerManager &mgr) { \ ValistChecker *checker = mgr.registerChecker(); \ Index: lib/StaticAnalyzer/Frontend/CheckerRegistry.cpp =================================================================== --- lib/StaticAnalyzer/Frontend/CheckerRegistry.cpp +++ lib/StaticAnalyzer/Frontend/CheckerRegistry.cpp @@ -38,14 +38,21 @@ return strcmp(versionString, CLANG_ANALYZER_API_VERSION_STRING) == 0; } +static bool checkerNameLT(const CheckerRegistry::CheckerInfo &a, + const CheckerRegistry::CheckerInfo &b) { + return a.FullName < b.FullName; +} + CheckerRegistry::CheckerRegistry(ArrayRef plugins, DiagnosticsEngine &diags, const LangOptions &LangOpts) : Diags(diags), LangOpts(LangOpts) { #define GET_CHECKERS + #define CHECKER(FULLNAME, CLASS, HELPTEXT) \ addChecker(register##CLASS, shouldRegister##CLASS, FULLNAME, HELPTEXT); + #include "clang/StaticAnalyzer/Checkers/Checkers.inc" #undef CHECKER #undef GET_CHECKERS @@ -79,15 +86,21 @@ if (registerPluginCheckers) registerPluginCheckers(*this); } -} -static constexpr char PackageSeparator = '.'; + llvm::sort(Checkers, checkerNameLT); -static bool checkerNameLT(const CheckerRegistry::CheckerInfo &a, - const CheckerRegistry::CheckerInfo &b) { - return a.FullName < b.FullName; +#define GET_CHECKER_DEPENDENCIES + +#define CHECKER_DEPENDENCY(FULLNAME, DEPENDENCY) \ + addDependency(FULLNAME, DEPENDENCY); + +#include "clang/StaticAnalyzer/Checkers/Checkers.inc" +#undef CHECKER_DEPENDENCY +#undef GET_CHECKER_DEPENDENCIES } +static constexpr char PackageSeparator = '.'; + static bool isInPackage(const CheckerRegistry::CheckerInfo &checker, StringRef packageName) { // Does the checker's full name have the package as a prefix? @@ -105,9 +118,48 @@ return false; } +/// Collects dependencies in \p ret, returns false on failure. +static bool collectDependenciesImpl( + const CheckerRegistry::ConstCheckerInfoList &deps, + const LangOptions &LO, + CheckerRegistry::CheckerInfoSet &ret); + +/// Collects dependenies in \p enabledCheckers. Return None on failure. +LLVM_NODISCARD +static llvm::Optional collectDependencies( + const CheckerRegistry::CheckerInfo &checker, const LangOptions &LO) { + + CheckerRegistry::CheckerInfoSet ret; + // Add dependencies to the enabled checkers only if all of them can be + // enabled. + if (!collectDependenciesImpl(checker.Dependencies, LO, ret)) + return None; + + return ret; +} + +static bool collectDependenciesImpl( + const CheckerRegistry::ConstCheckerInfoList &deps, + const LangOptions &LO, + CheckerRegistry::CheckerInfoSet &ret) { + + for (const CheckerRegistry::CheckerInfo *dependency : deps) { + + if (!dependency->ShouldRegister(LO)) + return false; + + // Collect dependencies recursively. + if (!collectDependenciesImpl(dependency->Dependencies, LO, ret)) + return false; + + ret.insert(dependency); + } + + return true; +} + CheckerRegistry::CheckerInfoSet CheckerRegistry::getEnabledCheckers( const AnalyzerOptions &Opts) const { - assert(std::is_sorted(Checkers.begin(), Checkers.end(), checkerNameLT) && "In order to efficiently gather checkers, this function expects them " "to be already sorted!"); @@ -117,7 +169,10 @@ for (const std::pair &opt : Opts.CheckersControlList) { // Use a binary search to find the possible start of the package. - CheckerRegistry::CheckerInfo packageInfo(nullptr, nullptr, opt.first, ""); + CheckerInfo packageInfo(nullptr, nullptr, opt.first, ""); + + assert(std::is_sorted(Checkers.cbegin(), Checkers.cend(), checkerNameLT)); + auto firstRelatedChecker = std::lower_bound(Checkers.cbegin(), end, packageInfo, checkerNameLT); @@ -139,13 +194,31 @@ // Step through all the checkers in the package. for (auto lastRelatedChecker = firstRelatedChecker+size; - firstRelatedChecker != lastRelatedChecker; ++firstRelatedChecker) + firstRelatedChecker != lastRelatedChecker; ++firstRelatedChecker) { if (opt.second) { - if (firstRelatedChecker->ShouldRegister(LangOpts)) - enabledCheckers.insert(&*firstRelatedChecker); + if (!firstRelatedChecker->ShouldRegister(LangOpts)) + continue; + + // Recursively enable it's dependencies. + llvm::Optional deps = + collectDependencies(*firstRelatedChecker, LangOpts); + + if (!deps) { + // If we failed to enable any of the dependencies, don't enable this + // checker. + continue; + } + + // Enable the checker. + enabledCheckers.insert(&*firstRelatedChecker); + + // Note that set_union also preserves the order of insertion. + enabledCheckers.set_union(*deps); } else { + // Disable the checker. enabledCheckers.remove(&*firstRelatedChecker); } + } } return enabledCheckers; @@ -167,9 +240,6 @@ void CheckerRegistry::initializeManager(CheckerManager &checkerMgr, const AnalyzerOptions &Opts) const { - // Sort checkers for efficient collection. - llvm::sort(Checkers, checkerNameLT); - // Collect checkers enabled by the options. CheckerInfoSet enabledCheckers = getEnabledCheckers(Opts); @@ -206,8 +276,6 @@ // FIXME: Alphabetical sort puts 'experimental' in the middle. // Would it be better to name it '~experimental' or something else // that's ASCIIbetically last? - llvm::sort(Checkers, checkerNameLT); - // FIXME: Print available packages. out << "CHECKERS:\n"; @@ -241,9 +309,6 @@ void CheckerRegistry::printList(raw_ostream &out, const AnalyzerOptions &opts) const { - // Sort checkers for efficient collection. - llvm::sort(Checkers, checkerNameLT); - // Collect checkers enabled by the options. CheckerInfoSet enabledCheckers = getEnabledCheckers(opts); Index: test/Analysis/NewDelete+MismatchedDeallocator_intersections.cpp =================================================================== --- test/Analysis/NewDelete+MismatchedDeallocator_intersections.cpp +++ test/Analysis/NewDelete+MismatchedDeallocator_intersections.cpp @@ -1,5 +1,14 @@ -// RUN: %clang_analyze_cc1 -analyzer-checker=core,cplusplus.NewDelete,unix.MismatchedDeallocator -std=c++11 -verify %s -// RUN: %clang_analyze_cc1 -analyzer-checker=core,cplusplus.NewDelete,cplusplus.NewDeleteLeaks,unix.MismatchedDeallocator -DLEAKS -std=c++11 -verify %s +// RUN: %clang_analyze_cc1 -std=c++11 -verify %s \ +// RUN: -analyzer-checker=core \ +// RUN: -analyzer-checker=cplusplus.NewDelete \ +// RUN: -analyzer-checker=unix.MismatchedDeallocator +// +// RUN: %clang_analyze_cc1 -std=c++11 -verify %s \ +// RUN: -analyzer-checker=core \ +// RUN: -analyzer-checker=cplusplus.NewDelete \ +// RUN: -analyzer-checker=cplusplus.NewDeleteLeaks \ +// RUN: -analyzer-checker=unix.MismatchedDeallocator + // expected-no-diagnostics typedef __typeof(sizeof(int)) size_t; Index: test/Analysis/NewDelete-checker-test.cpp =================================================================== --- test/Analysis/NewDelete-checker-test.cpp +++ test/Analysis/NewDelete-checker-test.cpp @@ -1,11 +1,42 @@ -// RUN: %clang_analyze_cc1 -analyzer-checker=core,cplusplus.NewDelete -std=c++11 -fblocks -verify %s -// RUN: %clang_analyze_cc1 -analyzer-checker=core,cplusplus.NewDeleteLeaks -DLEAKS -std=c++11 -fblocks -verify %s -// RUN: %clang_analyze_cc1 -analyzer-checker=core,cplusplus.NewDelete -std=c++11 -fblocks -analyzer-config c++-allocator-inlining=true -verify %s -// RUN: %clang_analyze_cc1 -analyzer-checker=core,cplusplus.NewDeleteLeaks -DLEAKS -std=c++11 -fblocks -analyzer-config c++-allocator-inlining=true -verify %s -// RUN: %clang_analyze_cc1 -analyzer-checker=core,cplusplus.NewDelete -std=c++11 -fblocks -DTEST_INLINABLE_ALLOCATORS -verify %s -// RUN: %clang_analyze_cc1 -analyzer-checker=core,cplusplus.NewDeleteLeaks -DLEAKS -std=c++11 -fblocks -DTEST_INLINABLE_ALLOCATORS -verify %s -// RUN: %clang_analyze_cc1 -analyzer-checker=core,cplusplus.NewDelete -std=c++11 -fblocks -analyzer-config c++-allocator-inlining=true -DTEST_INLINABLE_ALLOCATORS -verify %s -// RUN: %clang_analyze_cc1 -analyzer-checker=core,cplusplus.NewDeleteLeaks -DLEAKS -std=c++11 -fblocks -analyzer-config c++-allocator-inlining=true -DTEST_INLINABLE_ALLOCATORS -verify %s +// RUN: %clang_analyze_cc1 -std=c++11 -fblocks -verify %s \ +// RUN: -analyzer-checker=core \ +// RUN: -analyzer-checker=cplusplus.NewDelete +// +// RUN: %clang_analyze_cc1 -DLEAKS -std=c++11 -fblocks -verify %s \ +// RUN: -analyzer-checker=core \ +// RUN: -analyzer-checker=cplusplus.NewDeleteLeaks +// +// RUN: %clang_analyze_cc1 -std=c++11 -fblocks -verify %s \ +// RUN: -analyzer-checker=core \ +// RUN: -analyzer-checker=cplusplus.NewDelete \ +// RUN: -analyzer-config c++-allocator-inlining=true +// +// RUN: %clang_analyze_cc1 -DLEAKS -std=c++11 -fblocks -verify %s \ +// RUN: -analyzer-checker=core \ +// RUN: -analyzer-checker=cplusplus.NewDeleteLeaks \ +// RUN: -analyzer-config c++-allocator-inlining=true +// +// RUN: %clang_analyze_cc1 -DTEST_INLINABLE_ALLOCATORS \ +// RUN: -std=c++11 -fblocks -verify %s \ +// RUN: -analyzer-checker=core \ +// RUN: -analyzer-checker=cplusplus.NewDelete +// +// RUN: %clang_analyze_cc1 -DLEAKS -DTEST_INLINABLE_ALLOCATORS \ +// RUN: -std=c++11 -fblocks -verify %s \ +// RUN: -analyzer-checker=core \ +// RUN: -analyzer-checker=cplusplus.NewDeleteLeaks +// +// RUN: %clang_analyze_cc1 -DTEST_INLINABLE_ALLOCATORS \ +// RUN: -std=c++11 -fblocks -verify %s \ +// RUN: -analyzer-checker=core \ +// RUN: -analyzer-checker=cplusplus.NewDelete \ +// RUN: -analyzer-config c++-allocator-inlining=true +// +// RUN: %clang_analyze_cc1 -DLEAKS -DTEST_INLINABLE_ALLOCATORS \ +// RUN: -std=c++11 -fblocks -verify %s \ +// RUN: -analyzer-checker=core \ +// RUN: -analyzer-checker=cplusplus.NewDeleteLeaks \ +// RUN: -analyzer-config c++-allocator-inlining=true #include "Inputs/system-header-simulator-cxx.h" Index: utils/TableGen/ClangSACheckersEmitter.cpp =================================================================== --- utils/TableGen/ClangSACheckersEmitter.cpp +++ utils/TableGen/ClangSACheckersEmitter.cpp @@ -57,6 +57,13 @@ return std::string(); } +static void printChecker(llvm::raw_ostream &OS, const Record &R) { + OS << '\"'; + OS.write_escaped(getCheckerFullName(&R)) << "\", "; + OS << R.getName() << ", \""; + OS.write_escaped(getStringValue(R, "HelpText")) << '\"'; +} + namespace clang { void EmitClangSACheckers(RecordKeeper &Records, raw_ostream &OS) { std::vector checkers = Records.getAllDerivedDefinitions("Checker"); @@ -64,7 +71,12 @@ using SortedRecords = llvm::StringMap; - OS << "\n#ifdef GET_PACKAGES\n"; + // Emit packages. + // + // PACKAGE(PACKAGENAME) + // - PACKAGENAME: The name of the package. + OS << "\n" + "#ifdef GET_PACKAGES\n"; { SortedRecords sortedPackages; for (unsigned i = 0, e = packages.size(); i != e; ++i) @@ -79,19 +91,51 @@ OS << ")\n"; } } - OS << "#endif // GET_PACKAGES\n\n"; - - OS << "\n#ifdef GET_CHECKERS\n"; - for (unsigned i = 0, e = checkers.size(); i != e; ++i) { - const Record &R = *checkers[i]; + OS << "#endif // GET_PACKAGES\n" + "\n"; - OS << "CHECKER(" << "\""; - OS.write_escaped(getCheckerFullName(&R)) << "\", "; - OS << R.getName() << ", "; - OS << "\""; - OS.write_escaped(getStringValue(R, "HelpText")) << '\"'; + // Emit checkers. + // + // CHECKER(FULLNAME, CLASS, HELPTEXT) + // - FULLNAME: The full name of the checker, including packages, e.g.: + // alpha.cplusplus.UninitializedObject + // - CLASS: The name of the checker, with "Checker" appended, e.g.: + // UninitializedObjectChecker + // - HELPTEXT: The description of the checker. + OS << "\n" + "#ifdef GET_CHECKERS\n" + "\n"; + for (const Record *checker : checkers) { + OS << "CHECKER("; + printChecker(OS, *checker); OS << ")\n"; } - OS << "#endif // GET_CHECKERS\n\n"; + OS << "\n" + "#endif // GET_CHECKERS\n" + "\n"; + + // Emit dependencies. + // + // CHECKER_DEPENDENCY(FULLNAME, DEPENDENCY) + // - FULLNAME: The full name of the checker that depends on another checker. + // - DEPENDENCY: The full name of the checker FULLNAME depends on. + OS << "\n" + "#ifdef GET_CHECKER_DEPENDENCIES\n"; + for (const Record *checker : checkers) { + if (checker->isValueUnset("Dependencies")) + continue; + + for (const Record *Dependency : + checker->getValueAsListOfDefs("Dependencies")) { + OS << "CHECKER_DEPENDENCY("; + OS << '\"'; + OS.write_escaped(getCheckerFullName(checker)) << "\", "; + OS << '\"'; + OS.write_escaped(getCheckerFullName(Dependency)) << '\"'; + OS << ")\n"; + } + } + OS << "\n" + "#endif // GET_CHECKER_DEPENDENCIES\n"; } } // end namespace clang