Index: cfe/trunk/include/clang/StaticAnalyzer/Checkers/Checkers.td =================================================================== --- cfe/trunk/include/clang/StaticAnalyzer/Checkers/Checkers.td +++ cfe/trunk/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: cfe/trunk/lib/StaticAnalyzer/Checkers/CMakeLists.txt =================================================================== --- cfe/trunk/lib/StaticAnalyzer/Checkers/CMakeLists.txt +++ cfe/trunk/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: cfe/trunk/lib/StaticAnalyzer/Checkers/NonnullGlobalConstantsChecker.cpp =================================================================== --- cfe/trunk/lib/StaticAnalyzer/Checkers/NonnullGlobalConstantsChecker.cpp +++ cfe/trunk/lib/StaticAnalyzer/Checkers/NonnullGlobalConstantsChecker.cpp @@ -0,0 +1,140 @@ +//==- NonnullGlobalConstantsChecker.cpp ---------------------------*- C++ -*--// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// 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 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 to be +// non-null: +// - `char* const` +// - `const CFStringRef` from CoreFoundation +// - `NSString* const` from Foundation +// - `CFBooleanRef` from Foundation +// +//===----------------------------------------------------------------------===// + +#include "ClangSACheckers.h" +#include "clang/StaticAnalyzer/Core/BugReporter/BugType.h" +#include "clang/StaticAnalyzer/Core/Checker.h" +#include "clang/StaticAnalyzer/Core/CheckerManager.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/ExprEngine.h" + +using namespace clang; +using namespace ento; + +namespace { + +class NonnullGlobalConstantsChecker : public Checker { + mutable IdentifierInfo *NSStringII = nullptr; + mutable IdentifierInfo *CFStringRefII = nullptr; + mutable IdentifierInfo *CFBooleanRefII = nullptr; + +public: + NonnullGlobalConstantsChecker() {} + + void checkLocation(SVal l, bool isLoad, const Stmt *S, + CheckerContext &C) const; + +private: + void initIdentifierInfo(ASTContext &Ctx) const; + + bool isGlobalConstString(SVal V) const; + + bool isNonnullType(QualType Ty) const; +}; + +} // namespace + +/// Lazily initialize cache for required identifier informations. +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 NonnullGlobalConstantsChecker::checkLocation(SVal location, bool isLoad, + const Stmt *S, + CheckerContext &C) const { + initIdentifierInfo(C.getASTContext()); + if (!isLoad || !location.isValid()) + return; + + ProgramStateRef State = C.getState(); + SVal V = State->getSVal(location.castAs()); + + if (isGlobalConstString(location)) { + Optional Constr = V.getAs(); + + if (Constr) { + + // Assume that the variable is non-null. + ProgramStateRef OutputState = State->assume(*Constr, true); + C.addTransition(OutputState); + } + } +} + +/// \param V loaded lvalue. +/// \return whether {@code val} is a string-like const global. +bool NonnullGlobalConstantsChecker::isGlobalConstString(SVal V) const { + Optional RegionVal = V.getAs(); + if (!RegionVal) + return false; + auto *Region = dyn_cast(RegionVal->getAsRegion()); + if (!Region) + return false; + const VarDecl *Decl = Region->getDecl(); + + if (!Decl->hasGlobalStorage()) + return false; + + QualType Ty = Decl->getType(); + bool HasConst = Ty.isConstQualified(); + if (isNonnullType(Ty) && HasConst) + return true; + + // Look through the typedefs. + while (auto *T = dyn_cast(Ty)) { + Ty = T->getDecl()->getUnderlyingType(); + + // It is sufficient for any intermediate typedef + // to be classified const. + HasConst = HasConst || Ty.isConstQualified(); + if (isNonnullType(Ty) && HasConst) + return true; + } + return false; +} + +/// \return whether {@code type} is extremely unlikely to be null +bool NonnullGlobalConstantsChecker::isNonnullType(QualType Ty) const { + + if (Ty->isPointerType() && Ty->getPointeeType()->isCharType()) + return true; + + if (auto *T = dyn_cast(Ty)) { + return T->getInterfaceDecl() && + T->getInterfaceDecl()->getIdentifier() == NSStringII; + } else if (auto *T = dyn_cast(Ty)) { + IdentifierInfo* II = T->getDecl()->getIdentifier(); + return II == CFStringRefII || II == CFBooleanRefII; + } + return false; +} + +void ento::registerNonnullGlobalConstantsChecker(CheckerManager &Mgr) { + Mgr.registerChecker(); +} Index: cfe/trunk/lib/StaticAnalyzer/Checkers/NonnullStringConstantsChecker.cpp =================================================================== --- cfe/trunk/lib/StaticAnalyzer/Checkers/NonnullStringConstantsChecker.cpp +++ cfe/trunk/lib/StaticAnalyzer/Checkers/NonnullStringConstantsChecker.cpp @@ -1,135 +0,0 @@ -//==- NonnullStringConstantsChecker.cpp ---------------------------*- C++ -*--// -// -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. -// -//===----------------------------------------------------------------------===// -// -// This checker adds an assumption that constant string-like globals 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, -// 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: -// - `char* const` -// - `const CFStringRef` from CoreFoundation -// - `NSString* const` from Foundation -// -//===----------------------------------------------------------------------===// - -#include "ClangSACheckers.h" -#include "clang/StaticAnalyzer/Core/BugReporter/BugType.h" -#include "clang/StaticAnalyzer/Core/Checker.h" -#include "clang/StaticAnalyzer/Core/CheckerManager.h" -#include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h" -#include "clang/StaticAnalyzer/Core/PathSensitive/ExprEngine.h" - -using namespace clang; -using namespace ento; - -namespace { - -class NonnullStringConstantsChecker : public Checker { - mutable IdentifierInfo *NSStringII = nullptr; - mutable IdentifierInfo *CFStringRefII = nullptr; - -public: - NonnullStringConstantsChecker() {} - - void checkLocation(SVal l, bool isLoad, const Stmt *S, - CheckerContext &C) const; - -private: - void initIdentifierInfo(ASTContext &Ctx) const; - - bool isGlobalConstString(SVal V) const; - - bool isStringlike(QualType Ty) const; -}; - -} // namespace - -/// Lazily initialize cache for required identifier informations. -void NonnullStringConstantsChecker::initIdentifierInfo(ASTContext &Ctx) const { - if (NSStringII) - return; - - NSStringII = &Ctx.Idents.get("NSString"); - CFStringRefII = &Ctx.Idents.get("CFStringRef"); -} - -/// Add an assumption that const string-like globals are non-null. -void NonnullStringConstantsChecker::checkLocation(SVal location, bool isLoad, - const Stmt *S, - CheckerContext &C) const { - initIdentifierInfo(C.getASTContext()); - if (!isLoad || !location.isValid()) - return; - - ProgramStateRef State = C.getState(); - SVal V = State->getSVal(location.castAs()); - - if (isGlobalConstString(location)) { - Optional Constr = V.getAs(); - - if (Constr) { - - // Assume that the variable is non-null. - ProgramStateRef OutputState = State->assume(*Constr, true); - C.addTransition(OutputState); - } - } -} - -/// \param V loaded lvalue. -/// \return whether {@code val} is a string-like const global. -bool NonnullStringConstantsChecker::isGlobalConstString(SVal V) const { - Optional RegionVal = V.getAs(); - if (!RegionVal) - return false; - auto *Region = dyn_cast(RegionVal->getAsRegion()); - if (!Region) - return false; - const VarDecl *Decl = Region->getDecl(); - - if (!Decl->hasGlobalStorage()) - return false; - - QualType Ty = Decl->getType(); - bool HasConst = Ty.isConstQualified(); - if (isStringlike(Ty) && HasConst) - return true; - - // Look through the typedefs. - while (auto *T = dyn_cast(Ty)) { - Ty = T->getDecl()->getUnderlyingType(); - - // It is sufficient for any intermediate typedef - // to be classified const. - HasConst = HasConst || Ty.isConstQualified(); - if (isStringlike(Ty) && HasConst) - return true; - } - return false; -} - -/// \return whether {@code type} is a string-like type. -bool NonnullStringConstantsChecker::isStringlike(QualType Ty) const { - - if (Ty->isPointerType() && Ty->getPointeeType()->isCharType()) - return true; - - if (auto *T = dyn_cast(Ty)) { - return T->getInterfaceDecl() && - T->getInterfaceDecl()->getIdentifier() == NSStringII; - } else if (auto *T = dyn_cast(Ty)) { - return T->getDecl()->getIdentifier() == CFStringRefII; - } - return false; -} - -void ento::registerNonnullStringConstantsChecker(CheckerManager &Mgr) { - Mgr.registerChecker(); -} Index: cfe/trunk/test/Analysis/nonnull-global-constants.mm =================================================================== --- cfe/trunk/test/Analysis/nonnull-global-constants.mm +++ cfe/trunk/test/Analysis/nonnull-global-constants.mm @@ -0,0 +1,103 @@ +// RUN: %clang_analyze_cc1 -analyzer-checker=core,debug.ExprInspection -verify %s + +// Nullability of const string-like globals, testing +// 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; +void stringConstGlobal() { + clang_analyzer_eval(StringConstGlobal); // expected-warning{{TRUE}} +} + +// The logic does not apply to local variables though. +extern NSString *stringGetter(); +void stringConstLocal() { + NSString *const local = stringGetter(); + clang_analyzer_eval(local); // expected-warning{{UNKNOWN}} +} + +// Global const CFStringRef's are also assumed to be non-null. +extern const CFStringRef CFStringConstGlobal; +void cfStringCheckGlobal() { + clang_analyzer_eval(CFStringConstGlobal); // expected-warning{{TRUE}} +} + +// But only "const" ones. +extern CFStringRef CFStringNonConstGlobal; +void cfStringCheckMutableGlobal() { + clang_analyzer_eval(CFStringNonConstGlobal); // expected-warning{{UNKNOWN}} +} + +// char* const is also assumed to be non-null. +extern const char *const ConstCharStarConst; +void constCharStarCheckGlobal() { + clang_analyzer_eval(ConstCharStarConst); // expected-warning{{TRUE}} +} + +// Pointer value can be mutable. +extern char *const CharStarConst; +void charStarCheckGlobal() { + clang_analyzer_eval(CharStarConst); // expected-warning{{TRUE}} +} + +// But the pointer itself should be immutable. +extern char *CharStar; +void charStartCheckMutableGlobal() { + clang_analyzer_eval(CharStar); // expected-warning{{UNKNOWN}} +} + +// Type definitions should also work across typedefs, for pointers: +typedef char *const str; +extern str globalStr; +void charStarCheckTypedef() { + clang_analyzer_eval(globalStr); // expected-warning{{TRUE}} +} + +// And for types. +typedef NSString *const NStr; +extern NStr globalNSString; +void NSStringCheckTypedef() { + clang_analyzer_eval(globalNSString); // expected-warning{{TRUE}} +} + +// Note that constness could be either inside +// the var declaration, or in a typedef. +typedef NSString *NStr2; +extern const NStr2 globalNSString2; +void NSStringCheckConstTypedef() { + clang_analyzer_eval(globalNSString2); // expected-warning{{TRUE}} +} + +// Nested typedefs should work as well. +typedef const CFStringRef str1; +typedef str1 str2; +extern str2 globalStr2; +void testNestedTypedefs() { + clang_analyzer_eval(globalStr2); // expected-warning{{TRUE}} +} + +// And for NSString *. +typedef NSString *const nstr1; +typedef nstr1 nstr2; +extern nstr2 nglobalStr2; +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}} +} Index: cfe/trunk/test/Analysis/nonnull-string-constants.mm =================================================================== --- cfe/trunk/test/Analysis/nonnull-string-constants.mm +++ cfe/trunk/test/Analysis/nonnull-string-constants.mm @@ -1,90 +0,0 @@ -// RUN: %clang_analyze_cc1 -analyzer-checker=core,debug.ExprInspection -verify %s - -// Nullability of const string-like globals, testing -// NonnullStringConstantsChecker. - -void clang_analyzer_eval(bool); - -@class NSString; -typedef const struct __CFString *CFStringRef; - -// Global NSString* is non-null. -extern NSString *const StringConstGlobal; -void stringConstGlobal() { - clang_analyzer_eval(StringConstGlobal); // expected-warning{{TRUE}} -} - -// The logic does not apply to local variables though. -extern NSString *stringGetter(); -void stringConstLocal() { - NSString *const local = stringGetter(); - clang_analyzer_eval(local); // expected-warning{{UNKNOWN}} -} - -// Global const CFStringRef's are also assumed to be non-null. -extern const CFStringRef CFStringConstGlobal; -void cfStringCheckGlobal() { - clang_analyzer_eval(CFStringConstGlobal); // expected-warning{{TRUE}} -} - -// But only "const" ones. -extern CFStringRef CFStringNonConstGlobal; -void cfStringCheckMutableGlobal() { - clang_analyzer_eval(CFStringNonConstGlobal); // expected-warning{{UNKNOWN}} -} - -// char* const is also assumed to be non-null. -extern const char *const ConstCharStarConst; -void constCharStarCheckGlobal() { - clang_analyzer_eval(ConstCharStarConst); // expected-warning{{TRUE}} -} - -// Pointer value can be mutable. -extern char *const CharStarConst; -void charStarCheckGlobal() { - clang_analyzer_eval(CharStarConst); // expected-warning{{TRUE}} -} - -// But the pointer itself should be immutable. -extern char *CharStar; -void charStartCheckMutableGlobal() { - clang_analyzer_eval(CharStar); // expected-warning{{UNKNOWN}} -} - -// Type definitions should also work across typedefs, for pointers: -typedef char *const str; -extern str globalStr; -void charStarCheckTypedef() { - clang_analyzer_eval(globalStr); // expected-warning{{TRUE}} -} - -// And for types. -typedef NSString *const NStr; -extern NStr globalNSString; -void NSStringCheckTypedef() { - clang_analyzer_eval(globalNSString); // expected-warning{{TRUE}} -} - -// Note that constness could be either inside -// the var declaration, or in a typedef. -typedef NSString *NStr2; -extern const NStr2 globalNSString2; -void NSStringCheckConstTypedef() { - clang_analyzer_eval(globalNSString2); // expected-warning{{TRUE}} -} - -// Nested typedefs should work as well. -typedef const CFStringRef str1; -typedef str1 str2; -extern str2 globalStr2; -void testNestedTypedefs() { - clang_analyzer_eval(globalStr2); // expected-warning{{TRUE}} -} - -// And for NSString *. -typedef NSString *const nstr1; -typedef nstr1 nstr2; -extern nstr2 nglobalStr2; -void testNestedTypedefsForNSString() { - clang_analyzer_eval(nglobalStr2); // expected-warning{{TRUE}} -}