]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - lib/StaticAnalyzer/Core/ExprEngineObjC.cpp
Vendor import of clang trunk r338150:
[FreeBSD/FreeBSD.git] / lib / StaticAnalyzer / Core / ExprEngineObjC.cpp
1 //=-- ExprEngineObjC.cpp - ExprEngine support for Objective-C ---*- C++ -*-===//
2 //
3 //                     The LLVM Compiler Infrastructure
4 //
5 // This file is distributed under the University of Illinois Open Source
6 // License. See LICENSE.TXT for details.
7 //
8 //===----------------------------------------------------------------------===//
9 //
10 //  This file defines ExprEngine's support for Objective-C expressions.
11 //
12 //===----------------------------------------------------------------------===//
13
14 #include "clang/AST/StmtObjC.h"
15 #include "clang/StaticAnalyzer/Core/CheckerManager.h"
16 #include "clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h"
17 #include "clang/StaticAnalyzer/Core/PathSensitive/ExprEngine.h"
18
19 using namespace clang;
20 using namespace ento;
21
22 void ExprEngine::VisitLvalObjCIvarRefExpr(const ObjCIvarRefExpr *Ex,
23                                           ExplodedNode *Pred,
24                                           ExplodedNodeSet &Dst) {
25   ProgramStateRef state = Pred->getState();
26   const LocationContext *LCtx = Pred->getLocationContext();
27   SVal baseVal = state->getSVal(Ex->getBase(), LCtx);
28   SVal location = state->getLValue(Ex->getDecl(), baseVal);
29
30   ExplodedNodeSet dstIvar;
31   StmtNodeBuilder Bldr(Pred, dstIvar, *currBldrCtx);
32   Bldr.generateNode(Ex, Pred, state->BindExpr(Ex, LCtx, location));
33
34   // Perform the post-condition check of the ObjCIvarRefExpr and store
35   // the created nodes in 'Dst'.
36   getCheckerManager().runCheckersForPostStmt(Dst, dstIvar, Ex, *this);
37 }
38
39 void ExprEngine::VisitObjCAtSynchronizedStmt(const ObjCAtSynchronizedStmt *S,
40                                              ExplodedNode *Pred,
41                                              ExplodedNodeSet &Dst) {
42   getCheckerManager().runCheckersForPreStmt(Dst, Pred, S, *this);
43 }
44
45 /// Generate a node in \p Bldr for an iteration statement using ObjC
46 /// for-loop iterator.
47 static void populateObjCForDestinationSet(
48     ExplodedNodeSet &dstLocation, SValBuilder &svalBuilder,
49     const ObjCForCollectionStmt *S, const Stmt *elem, SVal elementV,
50     SymbolManager &SymMgr, const NodeBuilderContext *currBldrCtx,
51     StmtNodeBuilder &Bldr, bool hasElements) {
52
53   for (ExplodedNode *Pred : dstLocation) {
54     ProgramStateRef state = Pred->getState();
55     const LocationContext *LCtx = Pred->getLocationContext();
56
57     SVal hasElementsV = svalBuilder.makeTruthVal(hasElements);
58
59     // FIXME: S is not an expression. We should not be binding values to it.
60     ProgramStateRef nextState = state->BindExpr(S, LCtx, hasElementsV);
61
62     if (auto MV = elementV.getAs<loc::MemRegionVal>())
63       if (const auto *R = dyn_cast<TypedValueRegion>(MV->getRegion())) {
64         // FIXME: The proper thing to do is to really iterate over the
65         //  container.  We will do this with dispatch logic to the store.
66         //  For now, just 'conjure' up a symbolic value.
67         QualType T = R->getValueType();
68         assert(Loc::isLocType(T));
69
70         SVal V;
71         if (hasElements) {
72           SymbolRef Sym = SymMgr.conjureSymbol(elem, LCtx, T,
73                                                currBldrCtx->blockCount());
74           V = svalBuilder.makeLoc(Sym);
75         } else {
76           V = svalBuilder.makeIntVal(0, T);
77         }
78
79         nextState = nextState->bindLoc(elementV, V, LCtx);
80       }
81
82     Bldr.generateNode(S, Pred, nextState);
83   }
84 }
85
86 void ExprEngine::VisitObjCForCollectionStmt(const ObjCForCollectionStmt *S,
87                                             ExplodedNode *Pred,
88                                             ExplodedNodeSet &Dst) {
89
90   // ObjCForCollectionStmts are processed in two places.  This method
91   // handles the case where an ObjCForCollectionStmt* occurs as one of the
92   // statements within a basic block.  This transfer function does two things:
93   //
94   //  (1) binds the next container value to 'element'.  This creates a new
95   //      node in the ExplodedGraph.
96   //
97   //  (2) binds the value 0/1 to the ObjCForCollectionStmt* itself, indicating
98   //      whether or not the container has any more elements.  This value
99   //      will be tested in ProcessBranch.  We need to explicitly bind
100   //      this value because a container can contain nil elements.
101   //
102   // FIXME: Eventually this logic should actually do dispatches to
103   //   'countByEnumeratingWithState:objects:count:' (NSFastEnumeration).
104   //   This will require simulating a temporary NSFastEnumerationState, either
105   //   through an SVal or through the use of MemRegions.  This value can
106   //   be affixed to the ObjCForCollectionStmt* instead of 0/1; when the loop
107   //   terminates we reclaim the temporary (it goes out of scope) and we
108   //   we can test if the SVal is 0 or if the MemRegion is null (depending
109   //   on what approach we take).
110   //
111   //  For now: simulate (1) by assigning either a symbol or nil if the
112   //    container is empty.  Thus this transfer function will by default
113   //    result in state splitting.
114
115   const Stmt *elem = S->getElement();
116   const Stmt *collection = S->getCollection();
117   ProgramStateRef state = Pred->getState();
118   SVal collectionV = state->getSVal(collection, Pred->getLocationContext());
119
120   SVal elementV;
121   if (const auto *DS = dyn_cast<DeclStmt>(elem)) {
122     const VarDecl *elemD = cast<VarDecl>(DS->getSingleDecl());
123     assert(elemD->getInit() == nullptr);
124     elementV = state->getLValue(elemD, Pred->getLocationContext());
125   } else {
126     elementV = state->getSVal(elem, Pred->getLocationContext());
127   }
128
129   bool isContainerNull = state->isNull(collectionV).isConstrainedTrue();
130
131   ExplodedNodeSet dstLocation;
132   evalLocation(dstLocation, S, elem, Pred, state, elementV, nullptr, false);
133
134   ExplodedNodeSet Tmp;
135   StmtNodeBuilder Bldr(Pred, Tmp, *currBldrCtx);
136
137   if (!isContainerNull)
138     populateObjCForDestinationSet(dstLocation, svalBuilder, S, elem, elementV,
139                                   SymMgr, currBldrCtx, Bldr,
140                                   /*hasElements=*/true);
141
142   populateObjCForDestinationSet(dstLocation, svalBuilder, S, elem, elementV,
143                                 SymMgr, currBldrCtx, Bldr,
144                                 /*hasElements=*/false);
145
146   // Finally, run any custom checkers.
147   // FIXME: Eventually all pre- and post-checks should live in VisitStmt.
148   getCheckerManager().runCheckersForPostStmt(Dst, Tmp, S, *this);
149 }
150
151 void ExprEngine::VisitObjCMessage(const ObjCMessageExpr *ME,
152                                   ExplodedNode *Pred,
153                                   ExplodedNodeSet &Dst) {
154   CallEventManager &CEMgr = getStateManager().getCallEventManager();
155   CallEventRef<ObjCMethodCall> Msg =
156     CEMgr.getObjCMethodCall(ME, Pred->getState(), Pred->getLocationContext());
157
158   // There are three cases for the receiver:
159   //   (1) it is definitely nil,
160   //   (2) it is definitely non-nil, and
161   //   (3) we don't know.
162   //
163   // If the receiver is definitely nil, we skip the pre/post callbacks and
164   // instead call the ObjCMessageNil callbacks and return.
165   //
166   // If the receiver is definitely non-nil, we call the pre- callbacks,
167   // evaluate the call, and call the post- callbacks.
168   //
169   // If we don't know, we drop the potential nil flow and instead
170   // continue from the assumed non-nil state as in (2). This approach
171   // intentionally drops coverage in order to prevent false alarms
172   // in the following scenario:
173   //
174   // id result = [o someMethod]
175   // if (result) {
176   //   if (!o) {
177   //     // <-- This program point should be unreachable because if o is nil
178   //     // it must the case that result is nil as well.
179   //   }
180   // }
181   //
182   // We could avoid dropping coverage by performing an explicit case split
183   // on each method call -- but this would get very expensive. An alternative
184   // would be to introduce lazy constraints.
185   // FIXME: This ignores many potential bugs (<rdar://problem/11733396>).
186   // Revisit once we have lazier constraints.
187   if (Msg->isInstanceMessage()) {
188     SVal recVal = Msg->getReceiverSVal();
189     if (!recVal.isUndef()) {
190       // Bifurcate the state into nil and non-nil ones.
191       DefinedOrUnknownSVal receiverVal =
192           recVal.castAs<DefinedOrUnknownSVal>();
193       ProgramStateRef State = Pred->getState();
194
195       ProgramStateRef notNilState, nilState;
196       std::tie(notNilState, nilState) = State->assume(receiverVal);
197
198       // Receiver is definitely nil, so run ObjCMessageNil callbacks and return.
199       if (nilState && !notNilState) {
200         StmtNodeBuilder Bldr(Pred, Dst, *currBldrCtx);
201         bool HasTag = Pred->getLocation().getTag();
202         Pred = Bldr.generateNode(ME, Pred, nilState, nullptr,
203                                  ProgramPoint::PreStmtKind);
204         assert((Pred || HasTag) && "Should have cached out already!");
205         (void)HasTag;
206         if (!Pred)
207           return;
208         getCheckerManager().runCheckersForObjCMessageNil(Dst, Pred,
209                                                          *Msg, *this);
210         return;
211       }
212
213       ExplodedNodeSet dstNonNil;
214       StmtNodeBuilder Bldr(Pred, dstNonNil, *currBldrCtx);
215       // Generate a transition to the non-nil state, dropping any potential
216       // nil flow.
217       if (notNilState != State) {
218         bool HasTag = Pred->getLocation().getTag();
219         Pred = Bldr.generateNode(ME, Pred, notNilState);
220         assert((Pred || HasTag) && "Should have cached out already!");
221         (void)HasTag;
222         if (!Pred)
223           return;
224       }
225     }
226   }
227
228   // Handle the previsits checks.
229   ExplodedNodeSet dstPrevisit;
230   getCheckerManager().runCheckersForPreObjCMessage(dstPrevisit, Pred,
231                                                    *Msg, *this);
232   ExplodedNodeSet dstGenericPrevisit;
233   getCheckerManager().runCheckersForPreCall(dstGenericPrevisit, dstPrevisit,
234                                             *Msg, *this);
235
236   // Proceed with evaluate the message expression.
237   ExplodedNodeSet dstEval;
238   StmtNodeBuilder Bldr(dstGenericPrevisit, dstEval, *currBldrCtx);
239
240   for (ExplodedNodeSet::iterator DI = dstGenericPrevisit.begin(),
241        DE = dstGenericPrevisit.end(); DI != DE; ++DI) {
242     ExplodedNode *Pred = *DI;
243     ProgramStateRef State = Pred->getState();
244     CallEventRef<ObjCMethodCall> UpdatedMsg = Msg.cloneWithState(State);
245
246     if (UpdatedMsg->isInstanceMessage()) {
247       SVal recVal = UpdatedMsg->getReceiverSVal();
248       if (!recVal.isUndef()) {
249         if (ObjCNoRet.isImplicitNoReturn(ME)) {
250           // If we raise an exception, for now treat it as a sink.
251           // Eventually we will want to handle exceptions properly.
252           Bldr.generateSink(ME, Pred, State);
253           continue;
254         }
255       }
256     } else {
257       // Check for special class methods that are known to not return
258       // and that we should treat as a sink.
259       if (ObjCNoRet.isImplicitNoReturn(ME)) {
260         // If we raise an exception, for now treat it as a sink.
261         // Eventually we will want to handle exceptions properly.
262         Bldr.generateSink(ME, Pred, Pred->getState());
263         continue;
264       }
265     }
266
267     defaultEvalCall(Bldr, Pred, *UpdatedMsg);
268   }
269
270   ExplodedNodeSet dstPostvisit;
271   getCheckerManager().runCheckersForPostCall(dstPostvisit, dstEval,
272                                              *Msg, *this);
273
274   // Finally, perform the post-condition check of the ObjCMessageExpr and store
275   // the created nodes in 'Dst'.
276   getCheckerManager().runCheckersForPostObjCMessage(Dst, dstPostvisit,
277                                                     *Msg, *this);
278 }