Skip to content

Commit

Permalink
[Analyzer] Assume that CFBooleanRef const globals are non-null
Browse files Browse the repository at this point in the history
Differential Revision: https://reviews.llvm.org/D38867

llvm-svn: 315655
  • Loading branch information
George Karpenkov committed Oct 13, 2017
1 parent 4516cae commit 9a542f7
Show file tree
Hide file tree
Showing 4 changed files with 38 additions and 20 deletions.
2 changes: 1 addition & 1 deletion clang/include/clang/StaticAnalyzer/Checkers/Checkers.td
Expand Up @@ -132,7 +132,7 @@ def DynamicTypePropagation : Checker<"DynamicTypePropagation">,
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">;

Expand Down
2 changes: 1 addition & 1 deletion clang/lib/StaticAnalyzer/Checkers/CMakeLists.txt
Expand Up @@ -57,7 +57,7 @@ add_clang_library(clangStaticAnalyzerCheckers
NSErrorChecker.cpp
NoReturnFunctionChecker.cpp
NonNullParamChecker.cpp
NonnullStringConstantsChecker.cpp
NonnullGlobalConstantsChecker.cpp
NullabilityChecker.cpp
NumberObjectConversionChecker.cpp
ObjCAtSyncChecker.cpp
Expand Down
@@ -1,4 +1,4 @@
//==- NonnullStringConstantsChecker.cpp ---------------------------*- C++ -*--//
//==- NonnullGlobalConstantsChecker.cpp ---------------------------*- C++ -*--//
//
// The LLVM Compiler Infrastructure
//
Expand All @@ -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
//
//===----------------------------------------------------------------------===//

Expand All @@ -31,12 +33,13 @@ using namespace ento;

namespace {

class NonnullStringConstantsChecker : public Checker<check::Location> {
class NonnullGlobalConstantsChecker : public Checker<check::Location> {
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;
Expand All @@ -46,22 +49,23 @@ class NonnullStringConstantsChecker : public Checker<check::Location> {

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());
Expand All @@ -85,7 +89,7 @@ void NonnullStringConstantsChecker::checkLocation(SVal location, bool isLoad,

/// \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<loc::MemRegionVal> RegionVal = V.getAs<loc::MemRegionVal>();
if (!RegionVal)
return false;
Expand All @@ -99,7 +103,7 @@ bool NonnullStringConstantsChecker::isGlobalConstString(SVal V) const {

QualType Ty = Decl->getType();
bool HasConst = Ty.isConstQualified();
if (isStringlike(Ty) && HasConst)
if (isNonnullType(Ty) && HasConst)
return true;

// Look through the typedefs.
Expand All @@ -109,14 +113,14 @@ bool NonnullStringConstantsChecker::isGlobalConstString(SVal V) const {
// 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;
Expand All @@ -125,11 +129,12 @@ bool NonnullStringConstantsChecker::isStringlike(QualType Ty) const {
return T->getInterfaceDecl() &&
T->getInterfaceDecl()->getIdentifier() == NSStringII;
} else if (auto *T = dyn_cast<TypedefType>(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<NonnullStringConstantsChecker>();
void ento::registerNonnullGlobalConstantsChecker(CheckerManager &Mgr) {
Mgr.registerChecker<NonnullGlobalConstantsChecker>();
}
@@ -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;
Expand Down Expand Up @@ -88,3 +89,15 @@ void testNestedTypedefs() {
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}}
}

0 comments on commit 9a542f7

Please sign in to comment.