xref: /openbsd-src/gnu/llvm/clang/lib/StaticAnalyzer/Checkers/IteratorModeling.cpp (revision ec727ea710c91afd8ce4f788c5aaa8482b7b69b2)
1e5dd7070Spatrick //===-- IteratorModeling.cpp --------------------------------------*- C++ -*--//
2e5dd7070Spatrick //
3e5dd7070Spatrick // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4e5dd7070Spatrick // See https://llvm.org/LICENSE.txt for license information.
5e5dd7070Spatrick // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6e5dd7070Spatrick //
7e5dd7070Spatrick //===----------------------------------------------------------------------===//
8e5dd7070Spatrick //
9*ec727ea7Spatrick // Defines a modeling-checker for modeling STL iterator-like iterators.
10e5dd7070Spatrick //
11e5dd7070Spatrick //===----------------------------------------------------------------------===//
12e5dd7070Spatrick //
13e5dd7070Spatrick // In the code, iterator can be represented as a:
14e5dd7070Spatrick // * type-I: typedef-ed pointer. Operations over such iterator, such as
15e5dd7070Spatrick //           comparisons or increments, are modeled straightforwardly by the
16e5dd7070Spatrick //           analyzer.
17e5dd7070Spatrick // * type-II: structure with its method bodies available.  Operations over such
18e5dd7070Spatrick //            iterator are inlined by the analyzer, and results of modeling
19e5dd7070Spatrick //            these operations are exposing implementation details of the
20e5dd7070Spatrick //            iterators, which is not necessarily helping.
21e5dd7070Spatrick // * type-III: completely opaque structure. Operations over such iterator are
22e5dd7070Spatrick //             modeled conservatively, producing conjured symbols everywhere.
23e5dd7070Spatrick //
24e5dd7070Spatrick // To handle all these types in a common way we introduce a structure called
25e5dd7070Spatrick // IteratorPosition which is an abstraction of the position the iterator
26e5dd7070Spatrick // represents using symbolic expressions. The checker handles all the
27e5dd7070Spatrick // operations on this structure.
28e5dd7070Spatrick //
29e5dd7070Spatrick // Additionally, depending on the circumstances, operators of types II and III
30e5dd7070Spatrick // can be represented as:
31e5dd7070Spatrick // * type-IIa, type-IIIa: conjured structure symbols - when returned by value
32e5dd7070Spatrick //                        from conservatively evaluated methods such as
33e5dd7070Spatrick //                        `.begin()`.
34e5dd7070Spatrick // * type-IIb, type-IIIb: memory regions of iterator-typed objects, such as
35e5dd7070Spatrick //                        variables or temporaries, when the iterator object is
36e5dd7070Spatrick //                        currently treated as an lvalue.
37e5dd7070Spatrick // * type-IIc, type-IIIc: compound values of iterator-typed objects, when the
38e5dd7070Spatrick //                        iterator object is treated as an rvalue taken of a
39e5dd7070Spatrick //                        particular lvalue, eg. a copy of "type-a" iterator
40e5dd7070Spatrick //                        object, or an iterator that existed before the
41e5dd7070Spatrick //                        analysis has started.
42e5dd7070Spatrick //
43e5dd7070Spatrick // To handle any of these three different representations stored in an SVal we
44e5dd7070Spatrick // use setter and getters functions which separate the three cases. To store
45e5dd7070Spatrick // them we use a pointer union of symbol and memory region.
46e5dd7070Spatrick //
47e5dd7070Spatrick // The checker works the following way: We record the begin and the
48e5dd7070Spatrick // past-end iterator for all containers whenever their `.begin()` and `.end()`
49e5dd7070Spatrick // are called. Since the Constraint Manager cannot handle such SVals we need
50e5dd7070Spatrick // to take over its role. We post-check equality and non-equality comparisons
51e5dd7070Spatrick // and record that the two sides are equal if we are in the 'equal' branch
52e5dd7070Spatrick // (true-branch for `==` and false-branch for `!=`).
53e5dd7070Spatrick //
54e5dd7070Spatrick // In case of type-I or type-II iterators we get a concrete integer as a result
55e5dd7070Spatrick // of the comparison (1 or 0) but in case of type-III we only get a Symbol. In
56e5dd7070Spatrick // this latter case we record the symbol and reload it in evalAssume() and do
57e5dd7070Spatrick // the propagation there. We also handle (maybe double) negated comparisons
58e5dd7070Spatrick // which are represented in the form of (x == 0 or x != 0) where x is the
59e5dd7070Spatrick // comparison itself.
60e5dd7070Spatrick //
61e5dd7070Spatrick // Since `SimpleConstraintManager` cannot handle complex symbolic expressions
62e5dd7070Spatrick // we only use expressions of the format S, S+n or S-n for iterator positions
63e5dd7070Spatrick // where S is a conjured symbol and n is an unsigned concrete integer. When
64e5dd7070Spatrick // making an assumption e.g. `S1 + n == S2 + m` we store `S1 - S2 == m - n` as
65e5dd7070Spatrick // a constraint which we later retrieve when doing an actual comparison.
66e5dd7070Spatrick 
67e5dd7070Spatrick #include "clang/StaticAnalyzer/Checkers/BuiltinCheckerRegistration.h"
68e5dd7070Spatrick #include "clang/AST/DeclTemplate.h"
69e5dd7070Spatrick #include "clang/StaticAnalyzer/Core/BugReporter/BugType.h"
70e5dd7070Spatrick #include "clang/StaticAnalyzer/Core/Checker.h"
71e5dd7070Spatrick #include "clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h"
72e5dd7070Spatrick #include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h"
73e5dd7070Spatrick #include "clang/StaticAnalyzer/Core/PathSensitive/DynamicType.h"
74e5dd7070Spatrick 
75e5dd7070Spatrick #include "Iterator.h"
76e5dd7070Spatrick 
77e5dd7070Spatrick #include <utility>
78e5dd7070Spatrick 
79e5dd7070Spatrick using namespace clang;
80e5dd7070Spatrick using namespace ento;
81e5dd7070Spatrick using namespace iterator;
82e5dd7070Spatrick 
83e5dd7070Spatrick namespace {
84e5dd7070Spatrick 
85e5dd7070Spatrick class IteratorModeling
86*ec727ea7Spatrick     : public Checker<check::PostCall, check::PostStmt<UnaryOperator>,
87*ec727ea7Spatrick                      check::PostStmt<BinaryOperator>,
88*ec727ea7Spatrick                      check::PostStmt<MaterializeTemporaryExpr>,
89e5dd7070Spatrick                      check::Bind, check::LiveSymbols, check::DeadSymbols> {
90e5dd7070Spatrick 
91*ec727ea7Spatrick   using AdvanceFn = void (IteratorModeling::*)(CheckerContext &, const Expr *,
92*ec727ea7Spatrick                                                SVal, SVal, SVal) const;
93*ec727ea7Spatrick 
94*ec727ea7Spatrick   void handleOverloadedOperator(CheckerContext &C, const CallEvent &Call,
95*ec727ea7Spatrick                                 OverloadedOperatorKind Op) const;
96*ec727ea7Spatrick   void handleAdvanceLikeFunction(CheckerContext &C, const CallEvent &Call,
97*ec727ea7Spatrick                                  const Expr *OrigExpr,
98*ec727ea7Spatrick                                  const AdvanceFn *Handler) const;
99*ec727ea7Spatrick 
100e5dd7070Spatrick   void handleComparison(CheckerContext &C, const Expr *CE, SVal RetVal,
101e5dd7070Spatrick                         const SVal &LVal, const SVal &RVal,
102e5dd7070Spatrick                         OverloadedOperatorKind Op) const;
103e5dd7070Spatrick   void processComparison(CheckerContext &C, ProgramStateRef State,
104e5dd7070Spatrick                          SymbolRef Sym1, SymbolRef Sym2, const SVal &RetVal,
105e5dd7070Spatrick                          OverloadedOperatorKind Op) const;
106e5dd7070Spatrick   void handleIncrement(CheckerContext &C, const SVal &RetVal, const SVal &Iter,
107e5dd7070Spatrick                        bool Postfix) const;
108e5dd7070Spatrick   void handleDecrement(CheckerContext &C, const SVal &RetVal, const SVal &Iter,
109e5dd7070Spatrick                        bool Postfix) const;
110e5dd7070Spatrick   void handleRandomIncrOrDecr(CheckerContext &C, const Expr *CE,
111e5dd7070Spatrick                               OverloadedOperatorKind Op, const SVal &RetVal,
112e5dd7070Spatrick                               const SVal &LHS, const SVal &RHS) const;
113*ec727ea7Spatrick   void handlePtrIncrOrDecr(CheckerContext &C, const Expr *Iterator,
114*ec727ea7Spatrick                            OverloadedOperatorKind OK, SVal Offset) const;
115*ec727ea7Spatrick   void handleAdvance(CheckerContext &C, const Expr *CE, SVal RetVal, SVal Iter,
116*ec727ea7Spatrick                      SVal Amount) const;
117*ec727ea7Spatrick   void handlePrev(CheckerContext &C, const Expr *CE, SVal RetVal, SVal Iter,
118*ec727ea7Spatrick                   SVal Amount) const;
119*ec727ea7Spatrick   void handleNext(CheckerContext &C, const Expr *CE, SVal RetVal, SVal Iter,
120*ec727ea7Spatrick                   SVal Amount) const;
121e5dd7070Spatrick   void assignToContainer(CheckerContext &C, const Expr *CE, const SVal &RetVal,
122e5dd7070Spatrick                          const MemRegion *Cont) const;
123*ec727ea7Spatrick   bool noChangeInAdvance(CheckerContext &C, SVal Iter, const Expr *CE) const;
124e5dd7070Spatrick   void printState(raw_ostream &Out, ProgramStateRef State, const char *NL,
125e5dd7070Spatrick                   const char *Sep) const override;
126e5dd7070Spatrick 
127*ec727ea7Spatrick   // std::advance, std::prev & std::next
128*ec727ea7Spatrick   CallDescriptionMap<AdvanceFn> AdvanceLikeFunctions = {
129*ec727ea7Spatrick       // template<class InputIt, class Distance>
130*ec727ea7Spatrick       // void advance(InputIt& it, Distance n);
131*ec727ea7Spatrick       {{{"std", "advance"}, 2}, &IteratorModeling::handleAdvance},
132*ec727ea7Spatrick 
133*ec727ea7Spatrick       // template<class BidirIt>
134*ec727ea7Spatrick       // BidirIt prev(
135*ec727ea7Spatrick       //   BidirIt it,
136*ec727ea7Spatrick       //   typename std::iterator_traits<BidirIt>::difference_type n = 1);
137*ec727ea7Spatrick       {{{"std", "prev"}, 2}, &IteratorModeling::handlePrev},
138*ec727ea7Spatrick 
139*ec727ea7Spatrick       // template<class ForwardIt>
140*ec727ea7Spatrick       // ForwardIt next(
141*ec727ea7Spatrick       //   ForwardIt it,
142*ec727ea7Spatrick       //   typename std::iterator_traits<ForwardIt>::difference_type n = 1);
143*ec727ea7Spatrick       {{{"std", "next"}, 2}, &IteratorModeling::handleNext},
144*ec727ea7Spatrick   };
145*ec727ea7Spatrick 
146e5dd7070Spatrick public:
147*ec727ea7Spatrick   IteratorModeling() = default;
148e5dd7070Spatrick 
149e5dd7070Spatrick   void checkPostCall(const CallEvent &Call, CheckerContext &C) const;
150e5dd7070Spatrick   void checkBind(SVal Loc, SVal Val, const Stmt *S, CheckerContext &C) const;
151*ec727ea7Spatrick   void checkPostStmt(const UnaryOperator *UO, CheckerContext &C) const;
152*ec727ea7Spatrick   void checkPostStmt(const BinaryOperator *BO, CheckerContext &C) const;
153e5dd7070Spatrick   void checkPostStmt(const CXXConstructExpr *CCE, CheckerContext &C) const;
154e5dd7070Spatrick   void checkPostStmt(const DeclStmt *DS, CheckerContext &C) const;
155e5dd7070Spatrick   void checkPostStmt(const MaterializeTemporaryExpr *MTE,
156e5dd7070Spatrick                      CheckerContext &C) const;
157e5dd7070Spatrick   void checkLiveSymbols(ProgramStateRef State, SymbolReaper &SR) const;
158e5dd7070Spatrick   void checkDeadSymbols(SymbolReaper &SR, CheckerContext &C) const;
159e5dd7070Spatrick };
160e5dd7070Spatrick 
161e5dd7070Spatrick bool isSimpleComparisonOperator(OverloadedOperatorKind OK);
162*ec727ea7Spatrick bool isSimpleComparisonOperator(BinaryOperatorKind OK);
163e5dd7070Spatrick ProgramStateRef removeIteratorPosition(ProgramStateRef State, const SVal &Val);
164e5dd7070Spatrick ProgramStateRef relateSymbols(ProgramStateRef State, SymbolRef Sym1,
165e5dd7070Spatrick                               SymbolRef Sym2, bool Equal);
166e5dd7070Spatrick bool isBoundThroughLazyCompoundVal(const Environment &Env,
167e5dd7070Spatrick                                    const MemRegion *Reg);
168*ec727ea7Spatrick const ExplodedNode *findCallEnter(const ExplodedNode *Node, const Expr *Call);
169e5dd7070Spatrick 
170e5dd7070Spatrick } // namespace
171e5dd7070Spatrick 
172e5dd7070Spatrick void IteratorModeling::checkPostCall(const CallEvent &Call,
173e5dd7070Spatrick                                      CheckerContext &C) const {
174e5dd7070Spatrick   // Record new iterator positions and iterator position changes
175e5dd7070Spatrick   const auto *Func = dyn_cast_or_null<FunctionDecl>(Call.getDecl());
176e5dd7070Spatrick   if (!Func)
177e5dd7070Spatrick     return;
178e5dd7070Spatrick 
179e5dd7070Spatrick   if (Func->isOverloadedOperator()) {
180e5dd7070Spatrick     const auto Op = Func->getOverloadedOperator();
181*ec727ea7Spatrick     handleOverloadedOperator(C, Call, Op);
182e5dd7070Spatrick     return;
183e5dd7070Spatrick   }
184e5dd7070Spatrick 
185e5dd7070Spatrick   const auto *OrigExpr = Call.getOriginExpr();
186e5dd7070Spatrick   if (!OrigExpr)
187e5dd7070Spatrick     return;
188e5dd7070Spatrick 
189*ec727ea7Spatrick   const AdvanceFn *Handler = AdvanceLikeFunctions.lookup(Call);
190*ec727ea7Spatrick   if (Handler) {
191*ec727ea7Spatrick     handleAdvanceLikeFunction(C, Call, OrigExpr, Handler);
192*ec727ea7Spatrick     return;
193*ec727ea7Spatrick   }
194*ec727ea7Spatrick 
195e5dd7070Spatrick   if (!isIteratorType(Call.getResultType()))
196e5dd7070Spatrick     return;
197e5dd7070Spatrick 
198e5dd7070Spatrick   auto State = C.getState();
199e5dd7070Spatrick 
200e5dd7070Spatrick   // Already bound to container?
201e5dd7070Spatrick   if (getIteratorPosition(State, Call.getReturnValue()))
202e5dd7070Spatrick     return;
203e5dd7070Spatrick 
204e5dd7070Spatrick   // Copy-like and move constructors
205e5dd7070Spatrick   if (isa<CXXConstructorCall>(&Call) && Call.getNumArgs() == 1) {
206e5dd7070Spatrick     if (const auto *Pos = getIteratorPosition(State, Call.getArgSVal(0))) {
207e5dd7070Spatrick       State = setIteratorPosition(State, Call.getReturnValue(), *Pos);
208e5dd7070Spatrick       if (cast<CXXConstructorDecl>(Func)->isMoveConstructor()) {
209e5dd7070Spatrick         State = removeIteratorPosition(State, Call.getArgSVal(0));
210e5dd7070Spatrick       }
211e5dd7070Spatrick       C.addTransition(State);
212e5dd7070Spatrick       return;
213e5dd7070Spatrick     }
214e5dd7070Spatrick   }
215e5dd7070Spatrick 
216e5dd7070Spatrick   // Assumption: if return value is an iterator which is not yet bound to a
217*ec727ea7Spatrick   //             container, then look for the first iterator argument of the
218*ec727ea7Spatrick   //             same type as the return value and bind the return value to
219*ec727ea7Spatrick   //             the same container. This approach works for STL algorithms.
220e5dd7070Spatrick   // FIXME: Add a more conservative mode
221e5dd7070Spatrick   for (unsigned i = 0; i < Call.getNumArgs(); ++i) {
222*ec727ea7Spatrick     if (isIteratorType(Call.getArgExpr(i)->getType()) &&
223*ec727ea7Spatrick         Call.getArgExpr(i)->getType().getNonReferenceType().getDesugaredType(
224*ec727ea7Spatrick             C.getASTContext()).getTypePtr() ==
225*ec727ea7Spatrick         Call.getResultType().getDesugaredType(C.getASTContext()).getTypePtr()) {
226e5dd7070Spatrick       if (const auto *Pos = getIteratorPosition(State, Call.getArgSVal(i))) {
227e5dd7070Spatrick         assignToContainer(C, OrigExpr, Call.getReturnValue(),
228e5dd7070Spatrick                           Pos->getContainer());
229e5dd7070Spatrick         return;
230e5dd7070Spatrick       }
231e5dd7070Spatrick     }
232e5dd7070Spatrick   }
233e5dd7070Spatrick }
234e5dd7070Spatrick 
235e5dd7070Spatrick void IteratorModeling::checkBind(SVal Loc, SVal Val, const Stmt *S,
236e5dd7070Spatrick                                  CheckerContext &C) const {
237e5dd7070Spatrick   auto State = C.getState();
238e5dd7070Spatrick   const auto *Pos = getIteratorPosition(State, Val);
239e5dd7070Spatrick   if (Pos) {
240e5dd7070Spatrick     State = setIteratorPosition(State, Loc, *Pos);
241e5dd7070Spatrick     C.addTransition(State);
242e5dd7070Spatrick   } else {
243e5dd7070Spatrick     const auto *OldPos = getIteratorPosition(State, Loc);
244e5dd7070Spatrick     if (OldPos) {
245e5dd7070Spatrick       State = removeIteratorPosition(State, Loc);
246e5dd7070Spatrick       C.addTransition(State);
247e5dd7070Spatrick     }
248e5dd7070Spatrick   }
249e5dd7070Spatrick }
250e5dd7070Spatrick 
251*ec727ea7Spatrick void IteratorModeling::checkPostStmt(const UnaryOperator *UO,
252*ec727ea7Spatrick                                      CheckerContext &C) const {
253*ec727ea7Spatrick   UnaryOperatorKind OK = UO->getOpcode();
254*ec727ea7Spatrick   if (!isIncrementOperator(OK) && !isDecrementOperator(OK))
255*ec727ea7Spatrick     return;
256*ec727ea7Spatrick 
257*ec727ea7Spatrick   auto &SVB = C.getSValBuilder();
258*ec727ea7Spatrick   handlePtrIncrOrDecr(C, UO->getSubExpr(),
259*ec727ea7Spatrick                       isIncrementOperator(OK) ? OO_Plus : OO_Minus,
260*ec727ea7Spatrick                       SVB.makeArrayIndex(1));
261*ec727ea7Spatrick }
262*ec727ea7Spatrick 
263*ec727ea7Spatrick void IteratorModeling::checkPostStmt(const BinaryOperator *BO,
264*ec727ea7Spatrick                                      CheckerContext &C) const {
265*ec727ea7Spatrick   ProgramStateRef State = C.getState();
266*ec727ea7Spatrick   BinaryOperatorKind OK = BO->getOpcode();
267*ec727ea7Spatrick   SVal RVal = State->getSVal(BO->getRHS(), C.getLocationContext());
268*ec727ea7Spatrick 
269*ec727ea7Spatrick   if (isSimpleComparisonOperator(BO->getOpcode())) {
270*ec727ea7Spatrick     SVal LVal = State->getSVal(BO->getLHS(), C.getLocationContext());
271*ec727ea7Spatrick     SVal Result = State->getSVal(BO, C.getLocationContext());
272*ec727ea7Spatrick     handleComparison(C, BO, Result, LVal, RVal,
273*ec727ea7Spatrick                      BinaryOperator::getOverloadedOperator(OK));
274*ec727ea7Spatrick   } else if (isRandomIncrOrDecrOperator(OK)) {
275*ec727ea7Spatrick     handlePtrIncrOrDecr(C, BO->getLHS(),
276*ec727ea7Spatrick                         BinaryOperator::getOverloadedOperator(OK), RVal);
277*ec727ea7Spatrick   }
278*ec727ea7Spatrick }
279*ec727ea7Spatrick 
280e5dd7070Spatrick void IteratorModeling::checkPostStmt(const MaterializeTemporaryExpr *MTE,
281e5dd7070Spatrick                                      CheckerContext &C) const {
282e5dd7070Spatrick   /* Transfer iterator state to temporary objects */
283e5dd7070Spatrick   auto State = C.getState();
284e5dd7070Spatrick   const auto *Pos = getIteratorPosition(State, C.getSVal(MTE->getSubExpr()));
285e5dd7070Spatrick   if (!Pos)
286e5dd7070Spatrick     return;
287e5dd7070Spatrick   State = setIteratorPosition(State, C.getSVal(MTE), *Pos);
288e5dd7070Spatrick   C.addTransition(State);
289e5dd7070Spatrick }
290e5dd7070Spatrick 
291e5dd7070Spatrick void IteratorModeling::checkLiveSymbols(ProgramStateRef State,
292e5dd7070Spatrick                                         SymbolReaper &SR) const {
293*ec727ea7Spatrick   // Keep symbolic expressions of iterator positions alive
294e5dd7070Spatrick   auto RegionMap = State->get<IteratorRegionMap>();
295e5dd7070Spatrick   for (const auto &Reg : RegionMap) {
296e5dd7070Spatrick     const auto Offset = Reg.second.getOffset();
297e5dd7070Spatrick     for (auto i = Offset->symbol_begin(); i != Offset->symbol_end(); ++i)
298e5dd7070Spatrick       if (isa<SymbolData>(*i))
299e5dd7070Spatrick         SR.markLive(*i);
300e5dd7070Spatrick   }
301e5dd7070Spatrick 
302e5dd7070Spatrick   auto SymbolMap = State->get<IteratorSymbolMap>();
303e5dd7070Spatrick   for (const auto &Sym : SymbolMap) {
304e5dd7070Spatrick     const auto Offset = Sym.second.getOffset();
305e5dd7070Spatrick     for (auto i = Offset->symbol_begin(); i != Offset->symbol_end(); ++i)
306e5dd7070Spatrick       if (isa<SymbolData>(*i))
307e5dd7070Spatrick         SR.markLive(*i);
308e5dd7070Spatrick   }
309e5dd7070Spatrick 
310e5dd7070Spatrick }
311e5dd7070Spatrick 
312e5dd7070Spatrick void IteratorModeling::checkDeadSymbols(SymbolReaper &SR,
313e5dd7070Spatrick                                         CheckerContext &C) const {
314e5dd7070Spatrick   // Cleanup
315e5dd7070Spatrick   auto State = C.getState();
316e5dd7070Spatrick 
317e5dd7070Spatrick   auto RegionMap = State->get<IteratorRegionMap>();
318e5dd7070Spatrick   for (const auto &Reg : RegionMap) {
319e5dd7070Spatrick     if (!SR.isLiveRegion(Reg.first)) {
320e5dd7070Spatrick       // The region behind the `LazyCompoundVal` is often cleaned up before
321e5dd7070Spatrick       // the `LazyCompoundVal` itself. If there are iterator positions keyed
322e5dd7070Spatrick       // by these regions their cleanup must be deferred.
323e5dd7070Spatrick       if (!isBoundThroughLazyCompoundVal(State->getEnvironment(), Reg.first)) {
324e5dd7070Spatrick         State = State->remove<IteratorRegionMap>(Reg.first);
325e5dd7070Spatrick       }
326e5dd7070Spatrick     }
327e5dd7070Spatrick   }
328e5dd7070Spatrick 
329e5dd7070Spatrick   auto SymbolMap = State->get<IteratorSymbolMap>();
330e5dd7070Spatrick   for (const auto &Sym : SymbolMap) {
331e5dd7070Spatrick     if (!SR.isLive(Sym.first)) {
332e5dd7070Spatrick       State = State->remove<IteratorSymbolMap>(Sym.first);
333e5dd7070Spatrick     }
334e5dd7070Spatrick   }
335e5dd7070Spatrick 
336*ec727ea7Spatrick   C.addTransition(State);
337e5dd7070Spatrick }
338*ec727ea7Spatrick 
339*ec727ea7Spatrick void
340*ec727ea7Spatrick IteratorModeling::handleOverloadedOperator(CheckerContext &C,
341*ec727ea7Spatrick                                            const CallEvent &Call,
342*ec727ea7Spatrick                                            OverloadedOperatorKind Op) const {
343*ec727ea7Spatrick     if (isSimpleComparisonOperator(Op)) {
344*ec727ea7Spatrick       const auto *OrigExpr = Call.getOriginExpr();
345*ec727ea7Spatrick       if (!OrigExpr)
346*ec727ea7Spatrick         return;
347*ec727ea7Spatrick 
348*ec727ea7Spatrick       if (const auto *InstCall = dyn_cast<CXXInstanceCall>(&Call)) {
349*ec727ea7Spatrick         handleComparison(C, OrigExpr, Call.getReturnValue(),
350*ec727ea7Spatrick                          InstCall->getCXXThisVal(), Call.getArgSVal(0), Op);
351*ec727ea7Spatrick         return;
352*ec727ea7Spatrick       }
353*ec727ea7Spatrick 
354*ec727ea7Spatrick       handleComparison(C, OrigExpr, Call.getReturnValue(), Call.getArgSVal(0),
355*ec727ea7Spatrick                          Call.getArgSVal(1), Op);
356*ec727ea7Spatrick       return;
357*ec727ea7Spatrick     } else if (isRandomIncrOrDecrOperator(Op)) {
358*ec727ea7Spatrick       const auto *OrigExpr = Call.getOriginExpr();
359*ec727ea7Spatrick       if (!OrigExpr)
360*ec727ea7Spatrick         return;
361*ec727ea7Spatrick 
362*ec727ea7Spatrick       if (const auto *InstCall = dyn_cast<CXXInstanceCall>(&Call)) {
363*ec727ea7Spatrick         if (Call.getNumArgs() >= 1 &&
364*ec727ea7Spatrick               Call.getArgExpr(0)->getType()->isIntegralOrEnumerationType()) {
365*ec727ea7Spatrick           handleRandomIncrOrDecr(C, OrigExpr, Op, Call.getReturnValue(),
366*ec727ea7Spatrick                                  InstCall->getCXXThisVal(), Call.getArgSVal(0));
367*ec727ea7Spatrick           return;
368*ec727ea7Spatrick         }
369*ec727ea7Spatrick       } else {
370*ec727ea7Spatrick         if (Call.getNumArgs() >= 2 &&
371*ec727ea7Spatrick               Call.getArgExpr(1)->getType()->isIntegralOrEnumerationType()) {
372*ec727ea7Spatrick           handleRandomIncrOrDecr(C, OrigExpr, Op, Call.getReturnValue(),
373*ec727ea7Spatrick                                  Call.getArgSVal(0), Call.getArgSVal(1));
374*ec727ea7Spatrick           return;
375*ec727ea7Spatrick         }
376*ec727ea7Spatrick       }
377*ec727ea7Spatrick     } else if (isIncrementOperator(Op)) {
378*ec727ea7Spatrick       if (const auto *InstCall = dyn_cast<CXXInstanceCall>(&Call)) {
379*ec727ea7Spatrick         handleIncrement(C, Call.getReturnValue(), InstCall->getCXXThisVal(),
380*ec727ea7Spatrick                         Call.getNumArgs());
381*ec727ea7Spatrick         return;
382*ec727ea7Spatrick       }
383*ec727ea7Spatrick 
384*ec727ea7Spatrick       handleIncrement(C, Call.getReturnValue(), Call.getArgSVal(0),
385*ec727ea7Spatrick                       Call.getNumArgs());
386*ec727ea7Spatrick       return;
387*ec727ea7Spatrick     } else if (isDecrementOperator(Op)) {
388*ec727ea7Spatrick       if (const auto *InstCall = dyn_cast<CXXInstanceCall>(&Call)) {
389*ec727ea7Spatrick         handleDecrement(C, Call.getReturnValue(), InstCall->getCXXThisVal(),
390*ec727ea7Spatrick                         Call.getNumArgs());
391*ec727ea7Spatrick         return;
392*ec727ea7Spatrick       }
393*ec727ea7Spatrick 
394*ec727ea7Spatrick       handleDecrement(C, Call.getReturnValue(), Call.getArgSVal(0),
395*ec727ea7Spatrick                         Call.getNumArgs());
396*ec727ea7Spatrick       return;
397e5dd7070Spatrick     }
398e5dd7070Spatrick }
399e5dd7070Spatrick 
400*ec727ea7Spatrick void
401*ec727ea7Spatrick IteratorModeling::handleAdvanceLikeFunction(CheckerContext &C,
402*ec727ea7Spatrick                                             const CallEvent &Call,
403*ec727ea7Spatrick                                             const Expr *OrigExpr,
404*ec727ea7Spatrick                                             const AdvanceFn *Handler) const {
405*ec727ea7Spatrick   if (!C.wasInlined) {
406*ec727ea7Spatrick     (this->**Handler)(C, OrigExpr, Call.getReturnValue(),
407*ec727ea7Spatrick                       Call.getArgSVal(0), Call.getArgSVal(1));
408*ec727ea7Spatrick     return;
409*ec727ea7Spatrick   }
410*ec727ea7Spatrick 
411*ec727ea7Spatrick   // If std::advance() was inlined, but a non-standard function it calls inside
412*ec727ea7Spatrick   // was not, then we have to model it explicitly
413*ec727ea7Spatrick   const auto *IdInfo = cast<FunctionDecl>(Call.getDecl())->getIdentifier();
414*ec727ea7Spatrick   if (IdInfo) {
415*ec727ea7Spatrick     if (IdInfo->getName() == "advance") {
416*ec727ea7Spatrick       if (noChangeInAdvance(C, Call.getArgSVal(0), OrigExpr)) {
417*ec727ea7Spatrick         (this->**Handler)(C, OrigExpr, Call.getReturnValue(),
418*ec727ea7Spatrick                           Call.getArgSVal(0), Call.getArgSVal(1));
419*ec727ea7Spatrick       }
420*ec727ea7Spatrick     }
421*ec727ea7Spatrick   }
422e5dd7070Spatrick }
423e5dd7070Spatrick 
424e5dd7070Spatrick void IteratorModeling::handleComparison(CheckerContext &C, const Expr *CE,
425e5dd7070Spatrick                                        SVal RetVal, const SVal &LVal,
426e5dd7070Spatrick                                        const SVal &RVal,
427e5dd7070Spatrick                                        OverloadedOperatorKind Op) const {
428e5dd7070Spatrick   // Record the operands and the operator of the comparison for the next
429e5dd7070Spatrick   // evalAssume, if the result is a symbolic expression. If it is a concrete
430e5dd7070Spatrick   // value (only one branch is possible), then transfer the state between
431e5dd7070Spatrick   // the operands according to the operator and the result
432e5dd7070Spatrick    auto State = C.getState();
433e5dd7070Spatrick   const auto *LPos = getIteratorPosition(State, LVal);
434e5dd7070Spatrick   const auto *RPos = getIteratorPosition(State, RVal);
435e5dd7070Spatrick   const MemRegion *Cont = nullptr;
436e5dd7070Spatrick   if (LPos) {
437e5dd7070Spatrick     Cont = LPos->getContainer();
438e5dd7070Spatrick   } else if (RPos) {
439e5dd7070Spatrick     Cont = RPos->getContainer();
440e5dd7070Spatrick   }
441e5dd7070Spatrick   if (!Cont)
442e5dd7070Spatrick     return;
443e5dd7070Spatrick 
444*ec727ea7Spatrick   // At least one of the iterators has recorded positions. If one of them does
445e5dd7070Spatrick   // not then create a new symbol for the offset.
446e5dd7070Spatrick   SymbolRef Sym;
447e5dd7070Spatrick   if (!LPos || !RPos) {
448e5dd7070Spatrick     auto &SymMgr = C.getSymbolManager();
449e5dd7070Spatrick     Sym = SymMgr.conjureSymbol(CE, C.getLocationContext(),
450e5dd7070Spatrick                                C.getASTContext().LongTy, C.blockCount());
451e5dd7070Spatrick     State = assumeNoOverflow(State, Sym, 4);
452e5dd7070Spatrick   }
453e5dd7070Spatrick 
454e5dd7070Spatrick   if (!LPos) {
455e5dd7070Spatrick     State = setIteratorPosition(State, LVal,
456e5dd7070Spatrick                                 IteratorPosition::getPosition(Cont, Sym));
457e5dd7070Spatrick     LPos = getIteratorPosition(State, LVal);
458e5dd7070Spatrick   } else if (!RPos) {
459e5dd7070Spatrick     State = setIteratorPosition(State, RVal,
460e5dd7070Spatrick                                 IteratorPosition::getPosition(Cont, Sym));
461e5dd7070Spatrick     RPos = getIteratorPosition(State, RVal);
462e5dd7070Spatrick   }
463e5dd7070Spatrick 
464*ec727ea7Spatrick   // We cannot make assumptions on `UnknownVal`. Let us conjure a symbol
465e5dd7070Spatrick   // instead.
466e5dd7070Spatrick   if (RetVal.isUnknown()) {
467e5dd7070Spatrick     auto &SymMgr = C.getSymbolManager();
468e5dd7070Spatrick     auto *LCtx = C.getLocationContext();
469e5dd7070Spatrick     RetVal = nonloc::SymbolVal(SymMgr.conjureSymbol(
470e5dd7070Spatrick         CE, LCtx, C.getASTContext().BoolTy, C.blockCount()));
471e5dd7070Spatrick     State = State->BindExpr(CE, LCtx, RetVal);
472e5dd7070Spatrick   }
473e5dd7070Spatrick 
474e5dd7070Spatrick   processComparison(C, State, LPos->getOffset(), RPos->getOffset(), RetVal, Op);
475e5dd7070Spatrick }
476e5dd7070Spatrick 
477e5dd7070Spatrick void IteratorModeling::processComparison(CheckerContext &C,
478e5dd7070Spatrick                                          ProgramStateRef State, SymbolRef Sym1,
479e5dd7070Spatrick                                          SymbolRef Sym2, const SVal &RetVal,
480e5dd7070Spatrick                                          OverloadedOperatorKind Op) const {
481e5dd7070Spatrick   if (const auto TruthVal = RetVal.getAs<nonloc::ConcreteInt>()) {
482e5dd7070Spatrick     if ((State = relateSymbols(State, Sym1, Sym2,
483e5dd7070Spatrick                               (Op == OO_EqualEqual) ==
484e5dd7070Spatrick                                (TruthVal->getValue() != 0)))) {
485e5dd7070Spatrick       C.addTransition(State);
486e5dd7070Spatrick     } else {
487e5dd7070Spatrick       C.generateSink(State, C.getPredecessor());
488e5dd7070Spatrick     }
489e5dd7070Spatrick     return;
490e5dd7070Spatrick   }
491e5dd7070Spatrick 
492e5dd7070Spatrick   const auto ConditionVal = RetVal.getAs<DefinedSVal>();
493e5dd7070Spatrick   if (!ConditionVal)
494e5dd7070Spatrick     return;
495e5dd7070Spatrick 
496e5dd7070Spatrick   if (auto StateTrue = relateSymbols(State, Sym1, Sym2, Op == OO_EqualEqual)) {
497e5dd7070Spatrick     StateTrue = StateTrue->assume(*ConditionVal, true);
498e5dd7070Spatrick     C.addTransition(StateTrue);
499e5dd7070Spatrick   }
500e5dd7070Spatrick 
501e5dd7070Spatrick   if (auto StateFalse = relateSymbols(State, Sym1, Sym2, Op != OO_EqualEqual)) {
502e5dd7070Spatrick     StateFalse = StateFalse->assume(*ConditionVal, false);
503e5dd7070Spatrick     C.addTransition(StateFalse);
504e5dd7070Spatrick   }
505e5dd7070Spatrick }
506e5dd7070Spatrick 
507e5dd7070Spatrick void IteratorModeling::handleIncrement(CheckerContext &C, const SVal &RetVal,
508e5dd7070Spatrick                                        const SVal &Iter, bool Postfix) const {
509e5dd7070Spatrick   // Increment the symbolic expressions which represents the position of the
510e5dd7070Spatrick   // iterator
511e5dd7070Spatrick   auto State = C.getState();
512e5dd7070Spatrick   auto &BVF = C.getSymbolManager().getBasicVals();
513e5dd7070Spatrick 
514e5dd7070Spatrick   const auto *Pos = getIteratorPosition(State, Iter);
515e5dd7070Spatrick   if (!Pos)
516e5dd7070Spatrick     return;
517e5dd7070Spatrick 
518e5dd7070Spatrick   auto NewState =
519e5dd7070Spatrick     advancePosition(State, Iter, OO_Plus,
520e5dd7070Spatrick                     nonloc::ConcreteInt(BVF.getValue(llvm::APSInt::get(1))));
521e5dd7070Spatrick   assert(NewState &&
522e5dd7070Spatrick          "Advancing position by concrete int should always be successful");
523e5dd7070Spatrick 
524e5dd7070Spatrick   const auto *NewPos = getIteratorPosition(NewState, Iter);
525e5dd7070Spatrick   assert(NewPos &&
526e5dd7070Spatrick          "Iterator should have position after successful advancement");
527e5dd7070Spatrick 
528e5dd7070Spatrick   State = setIteratorPosition(State, Iter, *NewPos);
529e5dd7070Spatrick   State = setIteratorPosition(State, RetVal, Postfix ? *Pos : *NewPos);
530e5dd7070Spatrick   C.addTransition(State);
531e5dd7070Spatrick }
532e5dd7070Spatrick 
533e5dd7070Spatrick void IteratorModeling::handleDecrement(CheckerContext &C, const SVal &RetVal,
534e5dd7070Spatrick                                        const SVal &Iter, bool Postfix) const {
535e5dd7070Spatrick   // Decrement the symbolic expressions which represents the position of the
536e5dd7070Spatrick   // iterator
537e5dd7070Spatrick   auto State = C.getState();
538e5dd7070Spatrick   auto &BVF = C.getSymbolManager().getBasicVals();
539e5dd7070Spatrick 
540e5dd7070Spatrick   const auto *Pos = getIteratorPosition(State, Iter);
541e5dd7070Spatrick   if (!Pos)
542e5dd7070Spatrick     return;
543e5dd7070Spatrick 
544e5dd7070Spatrick   auto NewState =
545e5dd7070Spatrick     advancePosition(State, Iter, OO_Minus,
546e5dd7070Spatrick                     nonloc::ConcreteInt(BVF.getValue(llvm::APSInt::get(1))));
547e5dd7070Spatrick   assert(NewState &&
548e5dd7070Spatrick          "Advancing position by concrete int should always be successful");
549e5dd7070Spatrick 
550e5dd7070Spatrick   const auto *NewPos = getIteratorPosition(NewState, Iter);
551e5dd7070Spatrick   assert(NewPos &&
552e5dd7070Spatrick          "Iterator should have position after successful advancement");
553e5dd7070Spatrick 
554e5dd7070Spatrick   State = setIteratorPosition(State, Iter, *NewPos);
555e5dd7070Spatrick   State = setIteratorPosition(State, RetVal, Postfix ? *Pos : *NewPos);
556e5dd7070Spatrick   C.addTransition(State);
557e5dd7070Spatrick }
558e5dd7070Spatrick 
559e5dd7070Spatrick void IteratorModeling::handleRandomIncrOrDecr(CheckerContext &C,
560e5dd7070Spatrick                                               const Expr *CE,
561e5dd7070Spatrick                                               OverloadedOperatorKind Op,
562e5dd7070Spatrick                                               const SVal &RetVal,
563e5dd7070Spatrick                                               const SVal &LHS,
564e5dd7070Spatrick                                               const SVal &RHS) const {
565e5dd7070Spatrick   // Increment or decrement the symbolic expressions which represents the
566e5dd7070Spatrick   // position of the iterator
567e5dd7070Spatrick   auto State = C.getState();
568e5dd7070Spatrick 
569e5dd7070Spatrick   const auto *Pos = getIteratorPosition(State, LHS);
570e5dd7070Spatrick   if (!Pos)
571e5dd7070Spatrick     return;
572e5dd7070Spatrick 
573e5dd7070Spatrick   const auto *value = &RHS;
574*ec727ea7Spatrick   SVal val;
575e5dd7070Spatrick   if (auto loc = RHS.getAs<Loc>()) {
576*ec727ea7Spatrick     val = State->getRawSVal(*loc);
577e5dd7070Spatrick     value = &val;
578e5dd7070Spatrick   }
579e5dd7070Spatrick 
580e5dd7070Spatrick   auto &TgtVal = (Op == OO_PlusEqual || Op == OO_MinusEqual) ? LHS : RetVal;
581e5dd7070Spatrick 
582*ec727ea7Spatrick   // `AdvancedState` is a state where the position of `LHS` is advanced. We
583*ec727ea7Spatrick   // only need this state to retrieve the new position, but we do not want
584*ec727ea7Spatrick   // to change the position of `LHS` (in every case).
585*ec727ea7Spatrick   auto AdvancedState = advancePosition(State, LHS, Op, *value);
586*ec727ea7Spatrick   if (AdvancedState) {
587*ec727ea7Spatrick     const auto *NewPos = getIteratorPosition(AdvancedState, LHS);
588e5dd7070Spatrick     assert(NewPos &&
589e5dd7070Spatrick            "Iterator should have position after successful advancement");
590e5dd7070Spatrick 
591*ec727ea7Spatrick     State = setIteratorPosition(State, TgtVal, *NewPos);
592e5dd7070Spatrick     C.addTransition(State);
593e5dd7070Spatrick   } else {
594e5dd7070Spatrick     assignToContainer(C, CE, TgtVal, Pos->getContainer());
595e5dd7070Spatrick   }
596e5dd7070Spatrick }
597e5dd7070Spatrick 
598*ec727ea7Spatrick void IteratorModeling::handlePtrIncrOrDecr(CheckerContext &C,
599*ec727ea7Spatrick                                            const Expr *Iterator,
600*ec727ea7Spatrick                                            OverloadedOperatorKind OK,
601*ec727ea7Spatrick                                            SVal Offset) const {
602*ec727ea7Spatrick   QualType PtrType = Iterator->getType();
603*ec727ea7Spatrick   if (!PtrType->isPointerType())
604*ec727ea7Spatrick     return;
605*ec727ea7Spatrick   QualType ElementType = PtrType->getPointeeType();
606*ec727ea7Spatrick 
607*ec727ea7Spatrick   ProgramStateRef State = C.getState();
608*ec727ea7Spatrick   SVal OldVal = State->getSVal(Iterator, C.getLocationContext());
609*ec727ea7Spatrick 
610*ec727ea7Spatrick   const IteratorPosition *OldPos = getIteratorPosition(State, OldVal);
611*ec727ea7Spatrick   if (!OldPos)
612e5dd7070Spatrick     return;
613e5dd7070Spatrick 
614*ec727ea7Spatrick   SVal NewVal;
615*ec727ea7Spatrick   if (OK == OO_Plus || OK == OO_PlusEqual)
616*ec727ea7Spatrick     NewVal = State->getLValue(ElementType, Offset, OldVal);
617*ec727ea7Spatrick   else {
618*ec727ea7Spatrick     const llvm::APSInt &OffsetInt =
619*ec727ea7Spatrick       Offset.castAs<nonloc::ConcreteInt>().getValue();
620*ec727ea7Spatrick     auto &BVF = C.getSymbolManager().getBasicVals();
621*ec727ea7Spatrick     SVal NegatedOffset = nonloc::ConcreteInt(BVF.getValue(-OffsetInt));
622*ec727ea7Spatrick     NewVal = State->getLValue(ElementType, NegatedOffset, OldVal);
623e5dd7070Spatrick   }
624e5dd7070Spatrick 
625*ec727ea7Spatrick   // `AdvancedState` is a state where the position of `Old` is advanced. We
626*ec727ea7Spatrick   // only need this state to retrieve the new position, but we do not want
627*ec727ea7Spatrick   // ever to change the position of `OldVal`.
628*ec727ea7Spatrick   auto AdvancedState = advancePosition(State, OldVal, OK, Offset);
629*ec727ea7Spatrick   if (AdvancedState) {
630*ec727ea7Spatrick     const IteratorPosition *NewPos = getIteratorPosition(AdvancedState, OldVal);
631*ec727ea7Spatrick     assert(NewPos &&
632*ec727ea7Spatrick            "Iterator should have position after successful advancement");
633e5dd7070Spatrick 
634*ec727ea7Spatrick     ProgramStateRef NewState = setIteratorPosition(State, NewVal, *NewPos);
635*ec727ea7Spatrick     C.addTransition(NewState);
636*ec727ea7Spatrick   } else {
637*ec727ea7Spatrick     assignToContainer(C, Iterator, NewVal, OldPos->getContainer());
638e5dd7070Spatrick   }
639*ec727ea7Spatrick }
640*ec727ea7Spatrick 
641*ec727ea7Spatrick void IteratorModeling::handleAdvance(CheckerContext &C, const Expr *CE,
642*ec727ea7Spatrick                                      SVal RetVal, SVal Iter,
643*ec727ea7Spatrick                                      SVal Amount) const {
644*ec727ea7Spatrick   handleRandomIncrOrDecr(C, CE, OO_PlusEqual, RetVal, Iter, Amount);
645*ec727ea7Spatrick }
646*ec727ea7Spatrick 
647*ec727ea7Spatrick void IteratorModeling::handlePrev(CheckerContext &C, const Expr *CE,
648*ec727ea7Spatrick                                   SVal RetVal, SVal Iter, SVal Amount) const {
649*ec727ea7Spatrick   handleRandomIncrOrDecr(C, CE, OO_Minus, RetVal, Iter, Amount);
650*ec727ea7Spatrick }
651*ec727ea7Spatrick 
652*ec727ea7Spatrick void IteratorModeling::handleNext(CheckerContext &C, const Expr *CE,
653*ec727ea7Spatrick                                   SVal RetVal, SVal Iter, SVal Amount) const {
654*ec727ea7Spatrick   handleRandomIncrOrDecr(C, CE, OO_Plus, RetVal, Iter, Amount);
655e5dd7070Spatrick }
656e5dd7070Spatrick 
657e5dd7070Spatrick void IteratorModeling::assignToContainer(CheckerContext &C, const Expr *CE,
658e5dd7070Spatrick                                          const SVal &RetVal,
659e5dd7070Spatrick                                          const MemRegion *Cont) const {
660e5dd7070Spatrick   Cont = Cont->getMostDerivedObjectRegion();
661e5dd7070Spatrick 
662e5dd7070Spatrick   auto State = C.getState();
663*ec727ea7Spatrick   const auto *LCtx = C.getLocationContext();
664*ec727ea7Spatrick   State = createIteratorPosition(State, RetVal, Cont, CE, LCtx, C.blockCount());
665*ec727ea7Spatrick 
666e5dd7070Spatrick   C.addTransition(State);
667e5dd7070Spatrick }
668e5dd7070Spatrick 
669*ec727ea7Spatrick bool IteratorModeling::noChangeInAdvance(CheckerContext &C, SVal Iter,
670*ec727ea7Spatrick                                          const Expr *CE) const {
671*ec727ea7Spatrick   // Compare the iterator position before and after the call. (To be called
672*ec727ea7Spatrick   // from `checkPostCall()`.)
673*ec727ea7Spatrick   const auto StateAfter = C.getState();
674e5dd7070Spatrick 
675*ec727ea7Spatrick   const auto *PosAfter = getIteratorPosition(StateAfter, Iter);
676*ec727ea7Spatrick   // If we have no position after the call of `std::advance`, then we are not
677*ec727ea7Spatrick   // interested. (Modeling of an inlined `std::advance()` should not remove the
678*ec727ea7Spatrick   // position in any case.)
679*ec727ea7Spatrick   if (!PosAfter)
680*ec727ea7Spatrick     return false;
681e5dd7070Spatrick 
682*ec727ea7Spatrick   const ExplodedNode *N = findCallEnter(C.getPredecessor(), CE);
683*ec727ea7Spatrick   assert(N && "Any call should have a `CallEnter` node.");
684e5dd7070Spatrick 
685*ec727ea7Spatrick   const auto StateBefore = N->getState();
686*ec727ea7Spatrick   const auto *PosBefore = getIteratorPosition(StateBefore, Iter);
687e5dd7070Spatrick 
688*ec727ea7Spatrick   assert(PosBefore && "`std::advance() should not create new iterator "
689*ec727ea7Spatrick          "position but change existing ones");
690e5dd7070Spatrick 
691*ec727ea7Spatrick   return PosBefore->getOffset() == PosAfter->getOffset();
692e5dd7070Spatrick }
693e5dd7070Spatrick 
694e5dd7070Spatrick void IteratorModeling::printState(raw_ostream &Out, ProgramStateRef State,
695e5dd7070Spatrick                                   const char *NL, const char *Sep) const {
696e5dd7070Spatrick   auto SymbolMap = State->get<IteratorSymbolMap>();
697e5dd7070Spatrick   auto RegionMap = State->get<IteratorRegionMap>();
698*ec727ea7Spatrick   // Use a counter to add newlines before every line except the first one.
699*ec727ea7Spatrick   unsigned Count = 0;
700e5dd7070Spatrick 
701e5dd7070Spatrick   if (!SymbolMap.isEmpty() || !RegionMap.isEmpty()) {
702e5dd7070Spatrick     Out << Sep << "Iterator Positions :" << NL;
703e5dd7070Spatrick     for (const auto &Sym : SymbolMap) {
704*ec727ea7Spatrick       if (Count++)
705*ec727ea7Spatrick         Out << NL;
706*ec727ea7Spatrick 
707e5dd7070Spatrick       Sym.first->dumpToStream(Out);
708e5dd7070Spatrick       Out << " : ";
709e5dd7070Spatrick       const auto Pos = Sym.second;
710e5dd7070Spatrick       Out << (Pos.isValid() ? "Valid" : "Invalid") << " ; Container == ";
711e5dd7070Spatrick       Pos.getContainer()->dumpToStream(Out);
712e5dd7070Spatrick       Out<<" ; Offset == ";
713e5dd7070Spatrick       Pos.getOffset()->dumpToStream(Out);
714e5dd7070Spatrick     }
715e5dd7070Spatrick 
716e5dd7070Spatrick     for (const auto &Reg : RegionMap) {
717*ec727ea7Spatrick       if (Count++)
718*ec727ea7Spatrick         Out << NL;
719*ec727ea7Spatrick 
720e5dd7070Spatrick       Reg.first->dumpToStream(Out);
721e5dd7070Spatrick       Out << " : ";
722e5dd7070Spatrick       const auto Pos = Reg.second;
723e5dd7070Spatrick       Out << (Pos.isValid() ? "Valid" : "Invalid") << " ; Container == ";
724e5dd7070Spatrick       Pos.getContainer()->dumpToStream(Out);
725e5dd7070Spatrick       Out<<" ; Offset == ";
726e5dd7070Spatrick       Pos.getOffset()->dumpToStream(Out);
727e5dd7070Spatrick     }
728e5dd7070Spatrick   }
729e5dd7070Spatrick }
730e5dd7070Spatrick 
731e5dd7070Spatrick namespace {
732e5dd7070Spatrick 
733e5dd7070Spatrick bool isSimpleComparisonOperator(OverloadedOperatorKind OK) {
734e5dd7070Spatrick   return OK == OO_EqualEqual || OK == OO_ExclaimEqual;
735e5dd7070Spatrick }
736e5dd7070Spatrick 
737*ec727ea7Spatrick bool isSimpleComparisonOperator(BinaryOperatorKind OK) {
738*ec727ea7Spatrick   return OK == BO_EQ || OK == BO_NE;
739e5dd7070Spatrick }
740e5dd7070Spatrick 
741e5dd7070Spatrick ProgramStateRef removeIteratorPosition(ProgramStateRef State, const SVal &Val) {
742e5dd7070Spatrick   if (auto Reg = Val.getAsRegion()) {
743e5dd7070Spatrick     Reg = Reg->getMostDerivedObjectRegion();
744e5dd7070Spatrick     return State->remove<IteratorRegionMap>(Reg);
745e5dd7070Spatrick   } else if (const auto Sym = Val.getAsSymbol()) {
746e5dd7070Spatrick     return State->remove<IteratorSymbolMap>(Sym);
747e5dd7070Spatrick   } else if (const auto LCVal = Val.getAs<nonloc::LazyCompoundVal>()) {
748e5dd7070Spatrick     return State->remove<IteratorRegionMap>(LCVal->getRegion());
749e5dd7070Spatrick   }
750e5dd7070Spatrick   return nullptr;
751e5dd7070Spatrick }
752e5dd7070Spatrick 
753e5dd7070Spatrick ProgramStateRef relateSymbols(ProgramStateRef State, SymbolRef Sym1,
754e5dd7070Spatrick                               SymbolRef Sym2, bool Equal) {
755e5dd7070Spatrick   auto &SVB = State->getStateManager().getSValBuilder();
756e5dd7070Spatrick 
757e5dd7070Spatrick   // FIXME: This code should be reworked as follows:
758e5dd7070Spatrick   // 1. Subtract the operands using evalBinOp().
759e5dd7070Spatrick   // 2. Assume that the result doesn't overflow.
760e5dd7070Spatrick   // 3. Compare the result to 0.
761e5dd7070Spatrick   // 4. Assume the result of the comparison.
762e5dd7070Spatrick   const auto comparison =
763e5dd7070Spatrick     SVB.evalBinOp(State, BO_EQ, nonloc::SymbolVal(Sym1),
764e5dd7070Spatrick                   nonloc::SymbolVal(Sym2), SVB.getConditionType());
765e5dd7070Spatrick 
766e5dd7070Spatrick   assert(comparison.getAs<DefinedSVal>() &&
767e5dd7070Spatrick     "Symbol comparison must be a `DefinedSVal`");
768e5dd7070Spatrick 
769e5dd7070Spatrick   auto NewState = State->assume(comparison.castAs<DefinedSVal>(), Equal);
770e5dd7070Spatrick   if (!NewState)
771e5dd7070Spatrick     return nullptr;
772e5dd7070Spatrick 
773e5dd7070Spatrick   if (const auto CompSym = comparison.getAsSymbol()) {
774e5dd7070Spatrick     assert(isa<SymIntExpr>(CompSym) &&
775e5dd7070Spatrick            "Symbol comparison must be a `SymIntExpr`");
776e5dd7070Spatrick     assert(BinaryOperator::isComparisonOp(
777e5dd7070Spatrick                cast<SymIntExpr>(CompSym)->getOpcode()) &&
778e5dd7070Spatrick            "Symbol comparison must be a comparison");
779e5dd7070Spatrick     return assumeNoOverflow(NewState, cast<SymIntExpr>(CompSym)->getLHS(), 2);
780e5dd7070Spatrick   }
781e5dd7070Spatrick 
782e5dd7070Spatrick   return NewState;
783e5dd7070Spatrick }
784e5dd7070Spatrick 
785e5dd7070Spatrick bool isBoundThroughLazyCompoundVal(const Environment &Env,
786e5dd7070Spatrick                                    const MemRegion *Reg) {
787e5dd7070Spatrick   for (const auto &Binding : Env) {
788e5dd7070Spatrick     if (const auto LCVal = Binding.second.getAs<nonloc::LazyCompoundVal>()) {
789e5dd7070Spatrick       if (LCVal->getRegion() == Reg)
790e5dd7070Spatrick         return true;
791e5dd7070Spatrick     }
792e5dd7070Spatrick   }
793e5dd7070Spatrick 
794e5dd7070Spatrick   return false;
795e5dd7070Spatrick }
796e5dd7070Spatrick 
797*ec727ea7Spatrick const ExplodedNode *findCallEnter(const ExplodedNode *Node, const Expr *Call) {
798*ec727ea7Spatrick   while (Node) {
799*ec727ea7Spatrick     ProgramPoint PP = Node->getLocation();
800*ec727ea7Spatrick     if (auto Enter = PP.getAs<CallEnter>()) {
801*ec727ea7Spatrick       if (Enter->getCallExpr() == Call)
802*ec727ea7Spatrick         break;
803e5dd7070Spatrick     }
804e5dd7070Spatrick 
805*ec727ea7Spatrick     Node = Node->getFirstPred();
806e5dd7070Spatrick   }
807e5dd7070Spatrick 
808*ec727ea7Spatrick   return Node;
809e5dd7070Spatrick }
810e5dd7070Spatrick 
811e5dd7070Spatrick } // namespace
812e5dd7070Spatrick 
813e5dd7070Spatrick void ento::registerIteratorModeling(CheckerManager &mgr) {
814e5dd7070Spatrick   mgr.registerChecker<IteratorModeling>();
815e5dd7070Spatrick }
816e5dd7070Spatrick 
817*ec727ea7Spatrick bool ento::shouldRegisterIteratorModeling(const CheckerManager &mgr) {
818e5dd7070Spatrick   return true;
819e5dd7070Spatrick }
820