Index: include/clang/StaticAnalyzer/Checkers/CheckerBase.td =================================================================== --- include/clang/StaticAnalyzer/Checkers/CheckerBase.td +++ include/clang/StaticAnalyzer/Checkers/CheckerBase.td @@ -48,9 +48,24 @@ /// 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; - bits<2> Documentation; - Package ParentPackage; + string CheckerName = name; + string HelpText; + list Dependencies; + bits<2> Documentation; + Package ParentPackage; +} + +/// 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 @@ -127,8 +127,13 @@ HelpText<"Check for undefined results of binary operators">, Documentation; +def StackAddrEscapeBase : Checker<"StackAddrEscapeBase">, + HelpText<"Generate information about stack address escapes.">, + Documentation; + def StackAddrEscapeChecker : Checker<"StackAddressEscape">, HelpText<"Check that addresses to stack memory do not escape the function">, + Dependencies<[StackAddrEscapeBase]>, Documentation; def DynamicTypePropagation : Checker<"DynamicTypePropagation">, @@ -186,6 +191,7 @@ 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)">, + Dependencies<[CallAndMessageChecker]>, Documentation; def TestAfterDivZeroChecker : Checker<"TestAfterDivZero">, @@ -200,15 +206,25 @@ def StackAddrAsyncEscapeChecker : Checker<"StackAddressAsyncEscape">, HelpText<"Check that addresses to stack memory do not escape the function">, + Dependencies<[StackAddrEscapeBase]>, Documentation; } // end "alpha.core" +//===----------------------------------------------------------------------===// +// Nullability checkers. +//===----------------------------------------------------------------------===// + let ParentPackage = Nullability in { +def NullabilityBase : Checker<"NullabilityBase">, + HelpText<"Stores information during the analysis about nullability.">, + Documentation; + def NullPassedToNonnullChecker : Checker<"NullPassedToNonnull">, HelpText<"Warns when a null pointer is passed to a pointer which has a " "_Nonnull type.">, + Dependencies<[NullabilityBase]>, Documentation; def NullReturnedFromNonnullChecker : Checker<"NullReturnedFromNonnull">, @@ -218,20 +234,27 @@ def NullableDereferencedChecker : Checker<"NullableDereferenced">, HelpText<"Warns when a nullable pointer is dereferenced.">, + Dependencies<[NullabilityBase]>, Documentation; def NullablePassedToNonnullChecker : Checker<"NullablePassedToNonnull">, HelpText<"Warns when a nullable pointer is passed to a pointer which has a " "_Nonnull type.">, + Dependencies<[NullabilityBase]>, Documentation; def NullableReturnedFromNonnullChecker : Checker<"NullableReturnedFromNonnull">, HelpText<"Warns when a nullable pointer is returned from a function that has " "_Nonnull return type.">, + Dependencies<[NullabilityBase]>, Documentation; } // end "nullability" +//===----------------------------------------------------------------------===// +// APIModeling. +//===----------------------------------------------------------------------===// + let ParentPackage = APIModeling in { def StdCLibraryFunctionsChecker : Checker<"StdCLibraryFunctions">, @@ -290,6 +313,109 @@ } // end "core.uninitialized" +//===----------------------------------------------------------------------===// +// Unix API checkers. +//===----------------------------------------------------------------------===// + +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.">, + Documentation; + +def CStringNullArg : Checker<"NullArg">, + HelpText<"Check for null pointers being passed as arguments to C string " + "functions">, + Dependencies<[CStringModeling]>, + Documentation; + +def CStringSyntaxChecker : Checker<"BadSizeArg">, + HelpText<"Check the size argument passed into C string functions for common " + "erroneous patterns">, + Dependencies<[CStringModeling]>, + Documentation; + +} // end "unix.cstring" + +let ParentPackage = CStringAlpha in { + +def CStringOutOfBounds : Checker<"OutOfBounds">, + HelpText<"Check for out-of-bounds access in string functions">, + Dependencies<[CStringModeling]>, + Documentation; + +def CStringBufferOverlap : Checker<"BufferOverlap">, + HelpText<"Checks for overlap in two buffer arguments">, + Dependencies<[CStringModeling]>, + Documentation; + +def CStringNotNullTerm : Checker<"NotNullTerminated">, + HelpText<"Check for arguments which are not null-terminating strings">, + Dependencies<[CStringModeling]>, + Documentation; + +} // end "alpha.unix.cstring" + +let ParentPackage = Unix in { + +def UnixAPIMisuseChecker : Checker<"API">, + HelpText<"Check calls to various UNIX/Posix functions">, + Documentation; + +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]>, + Documentation; + +def MallocChecker: Checker<"Malloc">, + HelpText<"Check for memory leaks, double free, and use-after-free problems. " + "Traces memory managed by malloc()/free().">, + Dependencies<[DynamicMemoryModeling]>, + Documentation; + +def MallocSizeofChecker : Checker<"MallocSizeof">, + HelpText<"Check for dubious malloc arguments involving sizeof">, + Documentation; + +def MismatchedDeallocatorChecker : Checker<"MismatchedDeallocator">, + HelpText<"Check for mismatched deallocators.">, + Dependencies<[DynamicMemoryModeling]>, + Documentation; + +def VforkChecker : Checker<"Vfork">, + HelpText<"Check for proper usage of vfork">, + Documentation; + +} // end "unix" + +let ParentPackage = UnixAlpha in { + +def ChrootChecker : Checker<"Chroot">, + HelpText<"Check improper use of chroot">, + Documentation; + +def PthreadLockChecker : Checker<"PthreadLock">, + HelpText<"Simple lock -> unlock checker">, + Documentation; + +def StreamChecker : Checker<"Stream">, + HelpText<"Check stream handling functions">, + Documentation; + +def SimpleStreamChecker : Checker<"SimpleStream">, + HelpText<"Check for misuses of stream APIs">, + Documentation; + +def BlockInCriticalSectionChecker : Checker<"BlockInCriticalSection">, + HelpText<"Check for calls to blocking functions inside a critical section">, + Documentation; + +} // end "alpha.unix" + //===----------------------------------------------------------------------===// // C++ checkers. //===----------------------------------------------------------------------===// @@ -299,15 +425,18 @@ def InnerPointerChecker : Checker<"InnerPointer">, HelpText<"Check for inner pointers of C++ containers used after " "re/deallocation">, + Dependencies<[DynamicMemoryModeling]>, Documentation; def NewDeleteChecker : Checker<"NewDelete">, HelpText<"Check for double-free and use-after-free problems. Traces memory " "managed by new/delete.">, + Dependencies<[DynamicMemoryModeling]>, Documentation; def NewDeleteLeaksChecker : Checker<"NewDeleteLeaks">, HelpText<"Check for memory leaks. Traces memory managed by new/delete.">, + Dependencies<[NewDeleteChecker]>, Documentation; def CXXSelfAssignmentChecker : Checker<"SelfAssignment">, @@ -339,17 +468,24 @@ HelpText<"Check integer to enumeration casts for out of range values">, Documentation; +def IteratorModeling : Checker<"IteratorModeling">, + HelpText<"Models iterators of C++ containers">, + Documentation; + def InvalidatedIteratorChecker : Checker<"InvalidatedIterator">, HelpText<"Check for use of invalidated iterators">, + Dependencies<[IteratorModeling]>, Documentation; def IteratorRangeChecker : Checker<"IteratorRange">, HelpText<"Check for iterators used outside their valid ranges">, + Dependencies<[IteratorModeling]>, Documentation; def MismatchedIteratorChecker : Checker<"MismatchedIterator">, HelpText<"Check for use of iterators of different containers where iterators " "of the same container are expected">, + Dependencies<[IteratorModeling]>, Documentation; def UninitializedObjectChecker: Checker<"UninitializedObject">, @@ -365,16 +501,22 @@ let ParentPackage = Valist in { +def ValistBase : Checker<"ValistBase">, + HelpText<"Gathers information about va_lists.">, + Documentation; + def UninitializedChecker : Checker<"Uninitialized">, HelpText<"Check for usages of uninitialized (or already released) va_lists.">, Documentation; def UnterminatedChecker : Checker<"Unterminated">, HelpText<"Check for va_lists which are not released by a va_end call.">, + Dependencies<[ValistBase]>, Documentation; def CopyToSelfChecker : Checker<"CopyToSelf">, HelpText<"Check for va_lists which are copied onto itself.">, + Dependencies<[ValistBase]>, Documentation; } // end : "valist" @@ -418,40 +560,65 @@ let ParentPackage = InsecureAPI in { +def SecuritySyntaxChecker : Checker<"SecuritySyntaxChecker">, + HelpText<"Base of various security function related checkers">, + Documentation; + def bcmp : Checker<"bcmp">, HelpText<"Warn on uses of the 'bcmp' function">, + Dependencies<[SecuritySyntaxChecker]>, Documentation; + def bcopy : Checker<"bcopy">, HelpText<"Warn on uses of the 'bcopy' function">, + Dependencies<[SecuritySyntaxChecker]>, Documentation; + def bzero : Checker<"bzero">, HelpText<"Warn on uses of the 'bzero' function">, + Dependencies<[SecuritySyntaxChecker]>, Documentation; + def gets : Checker<"gets">, HelpText<"Warn on uses of the 'gets' function">, + Dependencies<[SecuritySyntaxChecker]>, Documentation; + def getpw : Checker<"getpw">, HelpText<"Warn on uses of the 'getpw' function">, + Dependencies<[SecuritySyntaxChecker]>, Documentation; + def mktemp : Checker<"mktemp">, HelpText<"Warn on uses of the 'mktemp' function">, + Dependencies<[SecuritySyntaxChecker]>, Documentation; + def mkstemp : Checker<"mkstemp">, HelpText<"Warn when 'mkstemp' is passed fewer than 6 X's in the format " "string">, + Dependencies<[SecuritySyntaxChecker]>, Documentation; + def rand : Checker<"rand">, HelpText<"Warn on uses of the 'rand', 'random', and related functions">, + Dependencies<[SecuritySyntaxChecker]>, Documentation; + def strcpy : Checker<"strcpy">, HelpText<"Warn on uses of the 'strcpy' and 'strcat' functions">, + Dependencies<[SecuritySyntaxChecker]>, Documentation; + def vfork : Checker<"vfork">, HelpText<"Warn on uses of the 'vfork' function">, + Dependencies<[SecuritySyntaxChecker]>, Documentation; + def UncheckedReturn : Checker<"UncheckedReturn">, HelpText<"Warn on uses of functions whose return values must be always " "checked">, + Dependencies<[SecuritySyntaxChecker]>, Documentation; } // end "security.insecureAPI" @@ -461,6 +628,7 @@ def FloatLoopCounter : Checker<"FloatLoopCounter">, HelpText<"Warn on using a floating point value as a loop counter (CERT: " "FLP30-C, FLP30-CPP)">, + Dependencies<[SecuritySyntaxChecker]>, Documentation; } // end "security" @@ -505,94 +673,23 @@ } // end "alpha.security.taint" //===----------------------------------------------------------------------===// -// Unix API checkers. +// Mac OS X, Cocoa, and Core Foundation checkers. //===----------------------------------------------------------------------===// -let ParentPackage = Unix in { - -def UnixAPIMisuseChecker : Checker<"API">, - HelpText<"Check calls to various UNIX/Posix functions">, - Documentation; - -def MallocChecker: Checker<"Malloc">, - HelpText<"Check for memory leaks, double free, and use-after-free problems. " - "Traces memory managed by malloc()/free().">, - Documentation; - -def MallocSizeofChecker : Checker<"MallocSizeof">, - HelpText<"Check for dubious malloc arguments involving sizeof">, - Documentation; - -def MismatchedDeallocatorChecker : Checker<"MismatchedDeallocator">, - HelpText<"Check for mismatched deallocators.">, - Documentation; - -def VforkChecker : Checker<"Vfork">, - HelpText<"Check for proper usage of vfork">, - Documentation; - -} // end "unix" - -let ParentPackage = UnixAlpha in { - -def ChrootChecker : Checker<"Chroot">, - HelpText<"Check improper use of chroot">, - Documentation; - -def PthreadLockChecker : Checker<"PthreadLock">, - HelpText<"Simple lock -> unlock checker">, - Documentation; - -def StreamChecker : Checker<"Stream">, - HelpText<"Check stream handling functions">, - Documentation; - -def SimpleStreamChecker : Checker<"SimpleStream">, - HelpText<"Check for misuses of stream APIs">, - Documentation; - -def BlockInCriticalSectionChecker : Checker<"BlockInCriticalSection">, - HelpText<"Check for calls to blocking functions inside a critical section">, - Documentation; - -} // end "alpha.unix" - -let ParentPackage = CString in { - -def CStringNullArg : Checker<"NullArg">, - HelpText<"Check for null pointers being passed as arguments to C string " - "functions">, - Documentation; - -def CStringSyntaxChecker : Checker<"BadSizeArg">, - HelpText<"Check the size argument passed into C string functions for common " - "erroneous patterns">, - Documentation; - -} // end "unix.cstring" - -let ParentPackage = CStringAlpha in { - -def CStringOutOfBounds : Checker<"OutOfBounds">, - HelpText<"Check for out-of-bounds access in string functions">, - Documentation; - -def CStringBufferOverlap : Checker<"BufferOverlap">, - HelpText<"Checks for overlap in two buffer arguments">, - Documentation; - -def CStringNotNullTerm : Checker<"NotNullTerminated">, - HelpText<"Check for arguments which are not null-terminating strings">, - Documentation; +let ParentPackage = Cocoa in { -} // end "alpha.unix.cstring" +def RetainCountBase : Checker<"RetainCountBase">, + HelpText<"Common base of various retain count related checkers">, + Documentation; -//===----------------------------------------------------------------------===// -// Mac OS X, Cocoa, and Core Foundation checkers. -//===----------------------------------------------------------------------===// +} // end "osx.cocoa" let ParentPackage = OSX in { +def NSOrCFErrorDerefChecker : Checker<"NSOrCFErrorDerefChecker">, + HelpText<"Implementation checker for NSErrorChecker and CFErrorChecker">, + Documentation; + def NumberObjectConversionChecker : Checker<"NumberObjectConversion">, HelpText<"Check for erroneous conversions of objects representing numbers " "into numbers">, @@ -611,7 +708,9 @@ Documentation; def OSObjectRetainCountChecker : Checker<"OSObjectRetainCount">, - HelpText<"Check for leaks and improper reference count management for OSObject">, + HelpText<"Check for leaks and improper reference count management for " + "OSObject">, + Dependencies<[RetainCountBase]>, Documentation; } // end "osx" @@ -675,14 +774,17 @@ def NSErrorChecker : Checker<"NSError">, HelpText<"Check usage of NSError** parameters">, + Dependencies<[NSOrCFErrorDerefChecker]>, Documentation; def RetainCountChecker : Checker<"RetainCount">, HelpText<"Check for leaks and improper reference count management">, + Dependencies<[RetainCountBase]>, Documentation; def ObjCGenericsChecker : Checker<"ObjCGenerics">, HelpText<"Check for type errors when using Objective-C generics">, + Dependencies<[DynamicTypePropagation]>, Documentation; def ObjCDeallocChecker : Checker<"Dealloc">, @@ -711,14 +813,22 @@ 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'">, + Documentation; + def InstanceVariableInvalidation : Checker<"InstanceVariableInvalidation">, HelpText<"Check that the invalidatable instance variables are invalidated in " "the methods annotated with objc_instance_variable_invalidator">, + Dependencies<[IvarInvalidationModeling]>, Documentation; def MissingInvalidationMethod : Checker<"MissingInvalidationMethod">, HelpText<"Check that the invalidation methods are present in classes that " "contain invalidatable instance variables">, + Dependencies<[IvarInvalidationModeling]>, Documentation; def DirectIvarAssignment : Checker<"DirectIvarAssignment">, @@ -729,6 +839,7 @@ Checker<"DirectIvarAssignmentForAnnotatedFunctions">, HelpText<"Check for direct assignments to instance variables in the methods " "annotated with objc_no_direct_instance_variable_assignment">, + Dependencies<[DirectIvarAssignment]>, Documentation; } // end "alpha.osx.cocoa" @@ -745,6 +856,7 @@ def CFErrorChecker : Checker<"CFError">, HelpText<"Check usage of CFErrorRef* parameters">, + Dependencies<[NSOrCFErrorDerefChecker]>, Documentation; } // end "osx.coreFoundation" Index: include/clang/StaticAnalyzer/Frontend/CheckerRegistry.h =================================================================== --- include/clang/StaticAnalyzer/Frontend/CheckerRegistry.h +++ include/clang/StaticAnalyzer/Frontend/CheckerRegistry.h @@ -89,6 +89,13 @@ using InitializationFunction = void (*)(CheckerManager &); using ShouldRegisterFunction = bool (*)(const LangOptions &); + struct CheckerInfo; + + using CheckerInfoList = std::vector; + using CheckerInfoListRange = llvm::iterator_range; + using ConstCheckerInfoList = llvm::SmallVector; + using CheckerInfoSet = llvm::SetVector; + struct CheckerInfo { enum class StateFromCmdLine { // This checker wasn't explicitly enabled or disabled. @@ -106,10 +113,16 @@ StringRef DocumentationUri; StateFromCmdLine State = StateFromCmdLine::State_Unspecified; + ConstCheckerInfoList Dependencies; + bool isEnabled(const LangOptions &LO) const { return State == StateFromCmdLine::State_Enabled && ShouldRegister(LO); } + bool isDisabled(const LangOptions &LO) const { + return State == StateFromCmdLine::State_Disabled && ShouldRegister(LO); + } + CheckerInfo(InitializationFunction Fn, ShouldRegisterFunction sfn, StringRef Name, StringRef Desc, StringRef DocsUri) : Initialize(Fn), ShouldRegister(sfn), FullName(Name), Desc(Desc), @@ -117,9 +130,6 @@ }; using StateFromCmdLine = CheckerInfo::StateFromCmdLine; - using CheckerInfoList = std::vector; - using CheckerInfoListRange = llvm::iterator_range; - using CheckerInfoSet = llvm::SetVector; private: template @@ -149,6 +159,28 @@ &CheckerRegistry::returnTrue, FullName, Desc, DocsUri); } + /// 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); + } + // FIXME: This *really* should be added to the frontend flag descriptions. /// Initializes a CheckerManager by calling the initialization functions for /// all checkers specified by the given CheckerOptInfo list. The order of this @@ -165,6 +197,9 @@ void printList(raw_ostream &out) 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; /// Return an iterator range of mutable CheckerInfos \p CmdLineArg applies to. Index: lib/StaticAnalyzer/Checkers/CStringChecker.cpp =================================================================== --- lib/StaticAnalyzer/Checkers/CStringChecker.cpp +++ lib/StaticAnalyzer/Checkers/CStringChecker.cpp @@ -2475,6 +2475,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(); \ @@ -2490,7 +2498,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 @@ -28,14 +28,6 @@ namespace { -struct ChecksFilter { - DefaultBool Check_CallAndMessageUnInitRefArg; - DefaultBool Check_CallAndMessageChecker; - - CheckName CheckName_CallAndMessageUnInitRefArg; - CheckName CheckName_CallAndMessageChecker; -}; - class CallAndMessageChecker : public Checker< check::PreStmt, check::PreStmt, @@ -56,7 +48,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; @@ -151,7 +144,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. @@ -607,17 +600,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 @@ -16,9 +16,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 @@ -2394,6 +2394,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 @@ -735,6 +735,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 @@ -3086,44 +3086,27 @@ } // 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); + "Optimistic", false, checker); checker->ChecksEnabled[MallocChecker::CK_InnerPointerChecker] = true; checker->CheckNames[MallocChecker::CK_InnerPointerChecker] = 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); \ @@ -3137,4 +3120,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 @@ -307,6 +307,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 @@ -1191,6 +1191,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/RetainCountChecker/RetainCountChecker.cpp =================================================================== --- lib/StaticAnalyzer/Checkers/RetainCountChecker/RetainCountChecker.cpp +++ lib/StaticAnalyzer/Checkers/RetainCountChecker/RetainCountChecker.cpp @@ -1423,6 +1423,14 @@ // Checker registration. //===----------------------------------------------------------------------===// +void ento::registerRetainCountBase(CheckerManager &Mgr) { + Mgr.registerChecker(); +} + +bool ento::shouldRegisterRetainCountBase(const LangOptions &LO) { + return true; +} + void ento::registerRetainCountChecker(CheckerManager &Mgr) { auto *Chk = Mgr.registerChecker(); Chk->TrackObjCAndCFObjects = true; Index: lib/StaticAnalyzer/Checkers/StackAddrEscapeChecker.cpp =================================================================== --- lib/StaticAnalyzer/Checkers/StackAddrEscapeChecker.cpp +++ lib/StaticAnalyzer/Checkers/StackAddrEscapeChecker.cpp @@ -359,6 +359,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 @@ -399,6 +399,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 @@ -143,6 +143,15 @@ // that's ASCIIbetically last? llvm::sort(Checkers, checkerNameLT); +#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 + // Parse '-analyzer-checker' and '-analyzer-disable-checker' options from the // command line. for (const std::pair &opt : AnOpts.CheckersControlList) { @@ -161,13 +170,69 @@ } } +/// 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->isDisabled(LO)) + return false; + + // Collect dependencies recursively. + if (!collectDependenciesImpl(dependency->Dependencies, LO, ret)) + return false; + + ret.insert(dependency); + } + + return true; +} + CheckerRegistry::CheckerInfoSet CheckerRegistry::getEnabledCheckers() const { CheckerInfoSet enabledCheckers; for (const CheckerInfo &checker : Checkers) { - if (checker.isEnabled(LangOpts)) - enabledCheckers.insert(&checker); + if (!checker.isEnabled(LangOpts)) + continue; + + // Recursively enable it's dependencies. + llvm::Optional deps = + collectDependencies(checker, LangOpts); + + if (!deps) { + // If we failed to enable any of the dependencies, don't enable this + // checker. + continue; + } + + // Note that set_union also preserves the order of insertion. + enabledCheckers.set_union(*deps); + + // Enable the checker. + enabledCheckers.insert(&checker); } return enabledCheckers; @@ -188,7 +253,6 @@ } void CheckerRegistry::initializeManager(CheckerManager &checkerMgr) const { - // Collect checkers enabled by the options. CheckerInfoSet enabledCheckers = getEnabledCheckers(); Index: test/Analysis/Inputs/expected-plists/nullability-notes.m.plist =================================================================== --- test/Analysis/Inputs/expected-plists/nullability-notes.m.plist +++ test/Analysis/Inputs/expected-plists/nullability-notes.m.plist @@ -173,9 +173,9 @@ descriptionNullable pointer is passed to a callee that requires a non-null 1st parameter categoryMemory error typeNullability - check_namenullability.NullPassedToNonnull + check_namenullability.NullabilityBase - issue_hash_content_of_line_in_contextb6bc8126de8e6eb3375483a656fe858d + issue_hash_content_of_line_in_contextff735bea0eb12d4d172b139143c32365 issue_context_kindObjective-C method issue_contextmethod issue_hash_function_offset3 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: test/Analysis/malloc-annotations.c =================================================================== --- test/Analysis/malloc-annotations.c +++ test/Analysis/malloc-annotations.c @@ -1,8 +1,10 @@ -// RUN: %clang_analyze_cc1 -analyzer-store=region -verify %s \ +// RUN: %clang_analyze_cc1 -analyzer-store=region -verify \ // RUN: -analyzer-checker=core \ // RUN: -analyzer-checker=alpha.deadcode.UnreachableCode \ -// RUN: -analyzer-checker=alpha.core.CastSize,unix.Malloc \ -// RUN: -analyzer-config unix.Malloc:Optimistic=true +// RUN: -analyzer-checker=alpha.core.CastSize \ +// RUN: -analyzer-checker=unix.Malloc \ +// RUN: -analyzer-config unix.DynamicMemoryModeling:Optimistic=true %s + typedef __typeof(sizeof(int)) size_t; void *malloc(size_t); void free(void *); Index: test/Analysis/test-separate-retaincount.cpp =================================================================== --- test/Analysis/test-separate-retaincount.cpp +++ test/Analysis/test-separate-retaincount.cpp @@ -1,6 +1,14 @@ -// RUN: %clang_analyze_cc1 -analyzer-checker=core,osx -analyzer-disable-checker osx.cocoa.RetainCount -DNO_CF_OBJECT -verify %s -// RUN: %clang_analyze_cc1 -analyzer-checker=core,osx -analyzer-disable-checker osx.OSObjectRetainCount -DNO_OS_OBJECT -verify %s -// RUN: %clang_analyze_cc1 -analyzer-checker=core,osx -analyzer-config "osx.cocoa.RetainCount:CheckOSObject=false" -DNO_OS_OBJECT -verify %s +// RUN: %clang_analyze_cc1 -DNO_CF_OBJECT -verify %s \ +// RUN: -analyzer-checker=core,osx \ +// RUN: -analyzer-disable-checker osx.cocoa.RetainCount +// +// RUN: %clang_analyze_cc1 -DNO_OS_OBJECT -verify %s \ +// RUN: -analyzer-checker=core,osx \ +// RUN: -analyzer-disable-checker osx.OSObjectRetainCount +// +// RUN: %clang_analyze_cc1 -DNO_OS_OBJECT -verify %s \ +// RUN: -analyzer-checker=core,osx \ +// RUN: -analyzer-config "osx.cocoa.RetainCount:CheckOSObject=false" #include "os_object_base.h" Index: utils/TableGen/ClangSACheckersEmitter.cpp =================================================================== --- utils/TableGen/ClangSACheckersEmitter.cpp +++ utils/TableGen/ClangSACheckersEmitter.cpp @@ -90,6 +90,17 @@ .str(); } +static void printChecker(llvm::raw_ostream &OS, const Record &R) { + OS << "CHECKER(" << "\""; + OS.write_escaped(getCheckerFullName(&R)) << "\", "; + OS << R.getName() << ", "; + OS << "\""; + OS.write_escaped(getStringValue(R, "HelpText")) << "\", "; + OS << "\""; + OS.write_escaped(getCheckerDocs(R)); + OS << "\""; +} + namespace clang { void EmitClangSACheckers(RecordKeeper &Records, raw_ostream &OS) { std::vector checkers = Records.getAllDerivedDefinitions("Checker"); @@ -100,7 +111,12 @@ OS << "// This file is automatically generated. Do not edit this file by " "hand.\n"; - 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) @@ -115,22 +131,50 @@ 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 << "CHECKER(" << "\""; - OS.write_escaped(getCheckerFullName(&R)) << "\", "; - OS << R.getName() << ", "; - OS << "\""; - OS.write_escaped(getStringValue(R, "HelpText")) << "\", "; - OS << "\""; - OS.write_escaped(getCheckerDocs(R)); - OS << "\""; + OS << "#endif // GET_PACKAGES\n" + "\n"; + + // 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) { + 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