1 //==- NonnullGlobalConstantsChecker.cpp ---------------------------*- C++ -*--//
3 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4 // See https://llvm.org/LICENSE.txt for license information.
5 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
7 //===----------------------------------------------------------------------===//
9 // This checker adds an assumption that constant globals of certain types* are
10 // non-null, as otherwise they generally do not convey any useful information.
11 // The assumption is useful, as many framework use e. g. global const strings,
12 // and the analyzer might not be able to infer the global value if the
13 // definition is in a separate translation unit.
14 // The following types (and their typedef aliases) are considered to be
17 // - `const CFStringRef` from CoreFoundation
18 // - `NSString* const` from Foundation
19 // - `CFBooleanRef` from Foundation
21 //===----------------------------------------------------------------------===//
23 #include "clang/StaticAnalyzer/Checkers/BuiltinCheckerRegistration.h"
24 #include "clang/StaticAnalyzer/Core/BugReporter/BugType.h"
25 #include "clang/StaticAnalyzer/Core/Checker.h"
26 #include "clang/StaticAnalyzer/Core/CheckerManager.h"
27 #include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h"
28 #include "clang/StaticAnalyzer/Core/PathSensitive/ExprEngine.h"
31 using namespace clang
;
36 class NonnullGlobalConstantsChecker
: public Checker
<check::Location
> {
37 mutable IdentifierInfo
*NSStringII
= nullptr;
38 mutable IdentifierInfo
*CFStringRefII
= nullptr;
39 mutable IdentifierInfo
*CFBooleanRefII
= nullptr;
40 mutable IdentifierInfo
*CFNullRefII
= nullptr;
43 NonnullGlobalConstantsChecker() {}
45 void checkLocation(SVal l
, bool isLoad
, const Stmt
*S
,
46 CheckerContext
&C
) const;
49 void initIdentifierInfo(ASTContext
&Ctx
) const;
51 bool isGlobalConstString(SVal V
) const;
53 bool isNonnullType(QualType Ty
) const;
58 /// Lazily initialize cache for required identifier information.
59 void NonnullGlobalConstantsChecker::initIdentifierInfo(ASTContext
&Ctx
) const {
63 NSStringII
= &Ctx
.Idents
.get("NSString");
64 CFStringRefII
= &Ctx
.Idents
.get("CFStringRef");
65 CFBooleanRefII
= &Ctx
.Idents
.get("CFBooleanRef");
66 CFNullRefII
= &Ctx
.Idents
.get("CFNullRef");
69 /// Add an assumption that const string-like globals are non-null.
70 void NonnullGlobalConstantsChecker::checkLocation(SVal location
, bool isLoad
,
72 CheckerContext
&C
) const {
73 initIdentifierInfo(C
.getASTContext());
74 if (!isLoad
|| !location
.isValid())
77 ProgramStateRef State
= C
.getState();
79 if (isGlobalConstString(location
)) {
80 SVal V
= State
->getSVal(location
.castAs
<Loc
>());
81 std::optional
<DefinedOrUnknownSVal
> Constr
=
82 V
.getAs
<DefinedOrUnknownSVal
>();
86 // Assume that the variable is non-null.
87 ProgramStateRef OutputState
= State
->assume(*Constr
, true);
88 C
.addTransition(OutputState
);
93 /// \param V loaded lvalue.
94 /// \return whether @c val is a string-like const global.
95 bool NonnullGlobalConstantsChecker::isGlobalConstString(SVal V
) const {
96 std::optional
<loc::MemRegionVal
> RegionVal
= V
.getAs
<loc::MemRegionVal
>();
99 auto *Region
= dyn_cast
<VarRegion
>(RegionVal
->getAsRegion());
102 const VarDecl
*Decl
= Region
->getDecl();
104 if (!Decl
->hasGlobalStorage())
107 QualType Ty
= Decl
->getType();
108 bool HasConst
= Ty
.isConstQualified();
109 if (isNonnullType(Ty
) && HasConst
)
112 // Look through the typedefs.
113 while (const Type
*T
= Ty
.getTypePtr()) {
114 if (const auto *AT
= dyn_cast
<AttributedType
>(T
)) {
115 if (AT
->getAttrKind() == attr::TypeNonNull
)
117 Ty
= AT
->getModifiedType();
118 } else if (const auto *ET
= dyn_cast
<ElaboratedType
>(T
)) {
119 const auto *TT
= dyn_cast
<TypedefType
>(ET
->getNamedType());
122 Ty
= TT
->getDecl()->getUnderlyingType();
123 // It is sufficient for any intermediate typedef
124 // to be classified const.
125 HasConst
= HasConst
|| Ty
.isConstQualified();
126 if (isNonnullType(Ty
) && HasConst
)
135 /// \return whether @c type is extremely unlikely to be null
136 bool NonnullGlobalConstantsChecker::isNonnullType(QualType Ty
) const {
138 if (Ty
->isPointerType() && Ty
->getPointeeType()->isCharType())
141 if (auto *T
= dyn_cast
<ObjCObjectPointerType
>(Ty
)) {
142 return T
->getInterfaceDecl() &&
143 T
->getInterfaceDecl()->getIdentifier() == NSStringII
;
144 } else if (auto *T
= Ty
->getAs
<TypedefType
>()) {
145 IdentifierInfo
* II
= T
->getDecl()->getIdentifier();
146 return II
== CFStringRefII
|| II
== CFBooleanRefII
|| II
== CFNullRefII
;
151 void ento::registerNonnullGlobalConstantsChecker(CheckerManager
&Mgr
) {
152 Mgr
.registerChecker
<NonnullGlobalConstantsChecker
>();
155 bool ento::shouldRegisterNonnullGlobalConstantsChecker(const CheckerManager
&mgr
) {