1 //=-- ExprEngineObjC.cpp - ExprEngine support for Objective-C ---*- 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 file defines ExprEngine's support for Objective-C expressions.
11 //===----------------------------------------------------------------------===//
13 #include "clang/AST/StmtObjC.h"
14 #include "clang/StaticAnalyzer/Core/CheckerManager.h"
15 #include "clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h"
16 #include "clang/StaticAnalyzer/Core/PathSensitive/ExprEngine.h"
18 using namespace clang
;
21 void ExprEngine::VisitLvalObjCIvarRefExpr(const ObjCIvarRefExpr
*Ex
,
23 ExplodedNodeSet
&Dst
) {
24 ProgramStateRef state
= Pred
->getState();
25 const LocationContext
*LCtx
= Pred
->getLocationContext();
26 SVal baseVal
= state
->getSVal(Ex
->getBase(), LCtx
);
27 SVal location
= state
->getLValue(Ex
->getDecl(), baseVal
);
29 ExplodedNodeSet dstIvar
;
30 StmtNodeBuilder
Bldr(Pred
, dstIvar
, *currBldrCtx
);
31 Bldr
.generateNode(Ex
, Pred
, state
->BindExpr(Ex
, LCtx
, location
));
33 // Perform the post-condition check of the ObjCIvarRefExpr and store
34 // the created nodes in 'Dst'.
35 getCheckerManager().runCheckersForPostStmt(Dst
, dstIvar
, Ex
, *this);
38 void ExprEngine::VisitObjCAtSynchronizedStmt(const ObjCAtSynchronizedStmt
*S
,
40 ExplodedNodeSet
&Dst
) {
41 getCheckerManager().runCheckersForPreStmt(Dst
, Pred
, S
, *this);
44 /// Generate a node in \p Bldr for an iteration statement using ObjC
45 /// for-loop iterator.
46 static void populateObjCForDestinationSet(
47 ExplodedNodeSet
&dstLocation
, SValBuilder
&svalBuilder
,
48 const ObjCForCollectionStmt
*S
, const Stmt
*elem
, SVal elementV
,
49 SymbolManager
&SymMgr
, const NodeBuilderContext
*currBldrCtx
,
50 StmtNodeBuilder
&Bldr
, bool hasElements
) {
52 for (ExplodedNode
*Pred
: dstLocation
) {
53 ProgramStateRef state
= Pred
->getState();
54 const LocationContext
*LCtx
= Pred
->getLocationContext();
56 ProgramStateRef nextState
=
57 ExprEngine::setWhetherHasMoreIteration(state
, S
, LCtx
, hasElements
);
59 if (auto MV
= elementV
.getAs
<loc::MemRegionVal
>())
60 if (const auto *R
= dyn_cast
<TypedValueRegion
>(MV
->getRegion())) {
61 // FIXME: The proper thing to do is to really iterate over the
62 // container. We will do this with dispatch logic to the store.
63 // For now, just 'conjure' up a symbolic value.
64 QualType T
= R
->getValueType();
65 assert(Loc::isLocType(T
));
69 SymbolRef Sym
= SymMgr
.conjureSymbol(elem
, LCtx
, T
,
70 currBldrCtx
->blockCount());
71 V
= svalBuilder
.makeLoc(Sym
);
73 V
= svalBuilder
.makeIntVal(0, T
);
76 nextState
= nextState
->bindLoc(elementV
, V
, LCtx
);
79 Bldr
.generateNode(S
, Pred
, nextState
);
83 void ExprEngine::VisitObjCForCollectionStmt(const ObjCForCollectionStmt
*S
,
85 ExplodedNodeSet
&Dst
) {
87 // ObjCForCollectionStmts are processed in two places. This method
88 // handles the case where an ObjCForCollectionStmt* occurs as one of the
89 // statements within a basic block. This transfer function does two things:
91 // (1) binds the next container value to 'element'. This creates a new
92 // node in the ExplodedGraph.
94 // (2) note whether the collection has any more elements (or in other words,
95 // whether the loop has more iterations). This will be tested in
98 // FIXME: Eventually this logic should actually do dispatches to
99 // 'countByEnumeratingWithState:objects:count:' (NSFastEnumeration).
100 // This will require simulating a temporary NSFastEnumerationState, either
101 // through an SVal or through the use of MemRegions. This value can
102 // be affixed to the ObjCForCollectionStmt* instead of 0/1; when the loop
103 // terminates we reclaim the temporary (it goes out of scope) and we
104 // we can test if the SVal is 0 or if the MemRegion is null (depending
105 // on what approach we take).
107 // For now: simulate (1) by assigning either a symbol or nil if the
108 // container is empty. Thus this transfer function will by default
109 // result in state splitting.
111 const Stmt
*elem
= S
->getElement();
112 const Stmt
*collection
= S
->getCollection();
113 ProgramStateRef state
= Pred
->getState();
114 SVal collectionV
= state
->getSVal(collection
, Pred
->getLocationContext());
117 if (const auto *DS
= dyn_cast
<DeclStmt
>(elem
)) {
118 const VarDecl
*elemD
= cast
<VarDecl
>(DS
->getSingleDecl());
119 assert(elemD
->getInit() == nullptr);
120 elementV
= state
->getLValue(elemD
, Pred
->getLocationContext());
122 elementV
= state
->getSVal(elem
, Pred
->getLocationContext());
125 bool isContainerNull
= state
->isNull(collectionV
).isConstrainedTrue();
127 ExplodedNodeSet dstLocation
;
128 evalLocation(dstLocation
, S
, elem
, Pred
, state
, elementV
, false);
131 StmtNodeBuilder
Bldr(Pred
, Tmp
, *currBldrCtx
);
133 if (!isContainerNull
)
134 populateObjCForDestinationSet(dstLocation
, svalBuilder
, S
, elem
, elementV
,
135 SymMgr
, currBldrCtx
, Bldr
,
136 /*hasElements=*/true);
138 populateObjCForDestinationSet(dstLocation
, svalBuilder
, S
, elem
, elementV
,
139 SymMgr
, currBldrCtx
, Bldr
,
140 /*hasElements=*/false);
142 // Finally, run any custom checkers.
143 // FIXME: Eventually all pre- and post-checks should live in VisitStmt.
144 getCheckerManager().runCheckersForPostStmt(Dst
, Tmp
, S
, *this);
147 void ExprEngine::VisitObjCMessage(const ObjCMessageExpr
*ME
,
149 ExplodedNodeSet
&Dst
) {
150 CallEventManager
&CEMgr
= getStateManager().getCallEventManager();
151 CallEventRef
<ObjCMethodCall
> Msg
= CEMgr
.getObjCMethodCall(
152 ME
, Pred
->getState(), Pred
->getLocationContext(), getCFGElementRef());
154 // There are three cases for the receiver:
155 // (1) it is definitely nil,
156 // (2) it is definitely non-nil, and
157 // (3) we don't know.
159 // If the receiver is definitely nil, we skip the pre/post callbacks and
160 // instead call the ObjCMessageNil callbacks and return.
162 // If the receiver is definitely non-nil, we call the pre- callbacks,
163 // evaluate the call, and call the post- callbacks.
165 // If we don't know, we drop the potential nil flow and instead
166 // continue from the assumed non-nil state as in (2). This approach
167 // intentionally drops coverage in order to prevent false alarms
168 // in the following scenario:
170 // id result = [o someMethod]
173 // // <-- This program point should be unreachable because if o is nil
174 // // it must the case that result is nil as well.
178 // However, it also loses coverage of the nil path prematurely,
179 // leading to missed reports.
181 // It's possible to handle this by performing a state split on every call:
182 // explore the state where the receiver is non-nil, and independently
183 // explore the state where it's nil. But this is not only slow, but
184 // completely unwarranted. The mere presence of the message syntax in the code
185 // isn't sufficient evidence that nil is a realistic possibility.
187 // An ideal solution would be to add the following constraint that captures
188 // both possibilities without splitting the state:
190 // ($x == 0) => ($y == 0) (1)
192 // where in our case '$x' is the receiver symbol, '$y' is the returned symbol,
193 // and '=>' is logical implication. But RangeConstraintManager can't handle
194 // such constraints yet, so for now we go with a simpler, more restrictive
195 // constraint: $x != 0, from which (1) follows as a vacuous truth.
196 if (Msg
->isInstanceMessage()) {
197 SVal recVal
= Msg
->getReceiverSVal();
198 if (!recVal
.isUndef()) {
199 // Bifurcate the state into nil and non-nil ones.
200 DefinedOrUnknownSVal receiverVal
=
201 recVal
.castAs
<DefinedOrUnknownSVal
>();
202 ProgramStateRef State
= Pred
->getState();
204 ProgramStateRef notNilState
, nilState
;
205 std::tie(notNilState
, nilState
) = State
->assume(receiverVal
);
207 // Receiver is definitely nil, so run ObjCMessageNil callbacks and return.
208 if (nilState
&& !notNilState
) {
209 ExplodedNodeSet dstNil
;
210 StmtNodeBuilder
Bldr(Pred
, dstNil
, *currBldrCtx
);
211 bool HasTag
= Pred
->getLocation().getTag();
212 Pred
= Bldr
.generateNode(ME
, Pred
, nilState
, nullptr,
213 ProgramPoint::PreStmtKind
);
214 assert((Pred
|| HasTag
) && "Should have cached out already!");
219 ExplodedNodeSet dstPostCheckers
;
220 getCheckerManager().runCheckersForObjCMessageNil(dstPostCheckers
, Pred
,
222 for (auto *I
: dstPostCheckers
)
223 finishArgumentConstruction(Dst
, I
, *Msg
);
227 ExplodedNodeSet dstNonNil
;
228 StmtNodeBuilder
Bldr(Pred
, dstNonNil
, *currBldrCtx
);
229 // Generate a transition to the non-nil state, dropping any potential
231 if (notNilState
!= State
) {
232 bool HasTag
= Pred
->getLocation().getTag();
233 Pred
= Bldr
.generateNode(ME
, Pred
, notNilState
);
234 assert((Pred
|| HasTag
) && "Should have cached out already!");
242 // Handle the previsits checks.
243 ExplodedNodeSet dstPrevisit
;
244 getCheckerManager().runCheckersForPreObjCMessage(dstPrevisit
, Pred
,
246 ExplodedNodeSet dstGenericPrevisit
;
247 getCheckerManager().runCheckersForPreCall(dstGenericPrevisit
, dstPrevisit
,
250 // Proceed with evaluate the message expression.
251 ExplodedNodeSet dstEval
;
252 StmtNodeBuilder
Bldr(dstGenericPrevisit
, dstEval
, *currBldrCtx
);
254 for (ExplodedNodeSet::iterator DI
= dstGenericPrevisit
.begin(),
255 DE
= dstGenericPrevisit
.end(); DI
!= DE
; ++DI
) {
256 ExplodedNode
*Pred
= *DI
;
257 ProgramStateRef State
= Pred
->getState();
258 CallEventRef
<ObjCMethodCall
> UpdatedMsg
= Msg
.cloneWithState(State
);
260 if (UpdatedMsg
->isInstanceMessage()) {
261 SVal recVal
= UpdatedMsg
->getReceiverSVal();
262 if (!recVal
.isUndef()) {
263 if (ObjCNoRet
.isImplicitNoReturn(ME
)) {
264 // If we raise an exception, for now treat it as a sink.
265 // Eventually we will want to handle exceptions properly.
266 Bldr
.generateSink(ME
, Pred
, State
);
271 // Check for special class methods that are known to not return
272 // and that we should treat as a sink.
273 if (ObjCNoRet
.isImplicitNoReturn(ME
)) {
274 // If we raise an exception, for now treat it as a sink.
275 // Eventually we will want to handle exceptions properly.
276 Bldr
.generateSink(ME
, Pred
, Pred
->getState());
281 defaultEvalCall(Bldr
, Pred
, *UpdatedMsg
);
284 // If there were constructors called for object-type arguments, clean them up.
285 ExplodedNodeSet dstArgCleanup
;
286 for (auto *I
: dstEval
)
287 finishArgumentConstruction(dstArgCleanup
, I
, *Msg
);
289 ExplodedNodeSet dstPostvisit
;
290 getCheckerManager().runCheckersForPostCall(dstPostvisit
, dstArgCleanup
,
293 // Finally, perform the post-condition check of the ObjCMessageExpr and store
294 // the created nodes in 'Dst'.
295 getCheckerManager().runCheckersForPostObjCMessage(Dst
, dstPostvisit
,