Index: include/clang/StaticAnalyzer/Checkers/Checkers.td =================================================================== --- include/clang/StaticAnalyzer/Checkers/Checkers.td +++ include/clang/StaticAnalyzer/Checkers/Checkers.td @@ -132,7 +132,7 @@ HelpText<"Generate dynamic type information">, DescFile<"DynamicTypePropagation.cpp">; -def NonnullStringConstantsChecker: Checker<"NonnilStringConstants">, +def NonnullGlobalConstantsChecker: Checker<"NonnilStringConstants">, HelpText<"Assume that const string-like globals are non-null">, DescFile<"NonilStringConstantsChecker.cpp">; Index: lib/StaticAnalyzer/Checkers/CMakeLists.txt =================================================================== --- lib/StaticAnalyzer/Checkers/CMakeLists.txt +++ lib/StaticAnalyzer/Checkers/CMakeLists.txt @@ -57,7 +57,7 @@ NSErrorChecker.cpp NoReturnFunctionChecker.cpp NonNullParamChecker.cpp - NonnullStringConstantsChecker.cpp + NonnullGlobalConstantsChecker.cpp NullabilityChecker.cpp NumberObjectConversionChecker.cpp ObjCAtSyncChecker.cpp Index: lib/StaticAnalyzer/Checkers/NonnullGlobalConstantsChecker.cpp =================================================================== --- lib/StaticAnalyzer/Checkers/NonnullGlobalConstantsChecker.cpp +++ lib/StaticAnalyzer/Checkers/NonnullGlobalConstantsChecker.cpp @@ -1,4 +1,4 @@ -//==- NonnullStringConstantsChecker.cpp ---------------------------*- C++ -*--// +//==- NonnullGlobalConstantsChecker.cpp ---------------------------*- C++ -*--// // // The LLVM Compiler Infrastructure // @@ -7,15 +7,17 @@ // //===----------------------------------------------------------------------===// // -// This checker adds an assumption that constant string-like globals are +// This checker adds an assumption that constant globals of certain types* are // non-null, as otherwise they generally do not convey any useful information. -// The assumption is useful, as many framework use such global const strings, +// The assumption is useful, as many framework use e. g. global const strings, // and the analyzer might not be able to infer the global value if the // definition is in a separate translation unit. -// The following types (and their typedef aliases) are considered string-like: +// The following types (and their typedef aliases) are considered to be +// non-null: // - `char* const` // - `const CFStringRef` from CoreFoundation // - `NSString* const` from Foundation +// - `CFBooleanRef` from Foundation // //===----------------------------------------------------------------------===// @@ -31,12 +33,13 @@ namespace { -class NonnullStringConstantsChecker : public Checker { +class NonnullGlobalConstantsChecker : public Checker { mutable IdentifierInfo *NSStringII = nullptr; mutable IdentifierInfo *CFStringRefII = nullptr; + mutable IdentifierInfo *CFBooleanRefII = nullptr; public: - NonnullStringConstantsChecker() {} + NonnullGlobalConstantsChecker() {} void checkLocation(SVal l, bool isLoad, const Stmt *S, CheckerContext &C) const; @@ -46,22 +49,23 @@ bool isGlobalConstString(SVal V) const; - bool isStringlike(QualType Ty) const; + bool isNonnullType(QualType Ty) const; }; } // namespace /// Lazily initialize cache for required identifier informations. -void NonnullStringConstantsChecker::initIdentifierInfo(ASTContext &Ctx) const { +void NonnullGlobalConstantsChecker::initIdentifierInfo(ASTContext &Ctx) const { if (NSStringII) return; NSStringII = &Ctx.Idents.get("NSString"); CFStringRefII = &Ctx.Idents.get("CFStringRef"); + CFBooleanRefII = &Ctx.Idents.get("CFBooleanRef"); } /// Add an assumption that const string-like globals are non-null. -void NonnullStringConstantsChecker::checkLocation(SVal location, bool isLoad, +void NonnullGlobalConstantsChecker::checkLocation(SVal location, bool isLoad, const Stmt *S, CheckerContext &C) const { initIdentifierInfo(C.getASTContext()); @@ -85,7 +89,7 @@ /// \param V loaded lvalue. /// \return whether {@code val} is a string-like const global. -bool NonnullStringConstantsChecker::isGlobalConstString(SVal V) const { +bool NonnullGlobalConstantsChecker::isGlobalConstString(SVal V) const { Optional RegionVal = V.getAs(); if (!RegionVal) return false; @@ -99,7 +103,7 @@ QualType Ty = Decl->getType(); bool HasConst = Ty.isConstQualified(); - if (isStringlike(Ty) && HasConst) + if (isNonnullType(Ty) && HasConst) return true; // Look through the typedefs. @@ -109,14 +113,14 @@ // It is sufficient for any intermediate typedef // to be classified const. HasConst = HasConst || Ty.isConstQualified(); - if (isStringlike(Ty) && HasConst) + if (isNonnullType(Ty) && HasConst) return true; } return false; } -/// \return whether {@code type} is a string-like type. -bool NonnullStringConstantsChecker::isStringlike(QualType Ty) const { +/// \return whether {@code type} is extremely unlikely to be null +bool NonnullGlobalConstantsChecker::isNonnullType(QualType Ty) const { if (Ty->isPointerType() && Ty->getPointeeType()->isCharType()) return true; @@ -125,11 +129,12 @@ return T->getInterfaceDecl() && T->getInterfaceDecl()->getIdentifier() == NSStringII; } else if (auto *T = dyn_cast(Ty)) { - return T->getDecl()->getIdentifier() == CFStringRefII; + IdentifierInfo* II = T->getDecl()->getIdentifier(); + return II == CFStringRefII || II == CFBooleanRefII; } return false; } -void ento::registerNonnullStringConstantsChecker(CheckerManager &Mgr) { - Mgr.registerChecker(); +void ento::registerNonnullGlobalConstantsChecker(CheckerManager &Mgr) { + Mgr.registerChecker(); } Index: test/Analysis/nonnull-global-constants.mm =================================================================== --- test/Analysis/nonnull-global-constants.mm +++ test/Analysis/nonnull-global-constants.mm @@ -1,12 +1,13 @@ // RUN: %clang_analyze_cc1 -analyzer-checker=core,debug.ExprInspection -verify %s // Nullability of const string-like globals, testing -// NonnullStringConstantsChecker. +// NonnullGlobalConstantsChecker. void clang_analyzer_eval(bool); @class NSString; typedef const struct __CFString *CFStringRef; +typedef const struct __CFBoolean * CFBooleanRef; // Global NSString* is non-null. extern NSString *const StringConstGlobal; @@ -88,3 +89,15 @@ void testNestedTypedefsForNSString() { clang_analyzer_eval(nglobalStr2); // expected-warning{{TRUE}} } + +// And for CFBooleanRefs. +extern const CFBooleanRef kBool; +void testNonnullBool() { + clang_analyzer_eval(kBool); // expected-warning{{TRUE}} +} + +// And again, only for const one. +extern CFBooleanRef kBoolMutable; +void testNonnullNonconstBool() { + clang_analyzer_eval(kBoolMutable); // expected-warning{{UNKNOWN}} +}