xref: /freebsd-src/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/IteratorModeling.cpp (revision 06c3fb2749bda94cb5201f81ffdb8fa6c3161b2e)
1480093f4SDimitry Andric //===-- IteratorModeling.cpp --------------------------------------*- C++ -*--//
2480093f4SDimitry Andric //
3480093f4SDimitry Andric // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4480093f4SDimitry Andric // See https://llvm.org/LICENSE.txt for license information.
5480093f4SDimitry Andric // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6480093f4SDimitry Andric //
7480093f4SDimitry Andric //===----------------------------------------------------------------------===//
8480093f4SDimitry Andric //
95ffd83dbSDimitry Andric // Defines a modeling-checker for modeling STL iterator-like iterators.
10480093f4SDimitry Andric //
11480093f4SDimitry Andric //===----------------------------------------------------------------------===//
12480093f4SDimitry Andric //
13480093f4SDimitry Andric // In the code, iterator can be represented as a:
14480093f4SDimitry Andric // * type-I: typedef-ed pointer. Operations over such iterator, such as
15480093f4SDimitry Andric //           comparisons or increments, are modeled straightforwardly by the
16480093f4SDimitry Andric //           analyzer.
17480093f4SDimitry Andric // * type-II: structure with its method bodies available.  Operations over such
18480093f4SDimitry Andric //            iterator are inlined by the analyzer, and results of modeling
19480093f4SDimitry Andric //            these operations are exposing implementation details of the
20480093f4SDimitry Andric //            iterators, which is not necessarily helping.
21480093f4SDimitry Andric // * type-III: completely opaque structure. Operations over such iterator are
22480093f4SDimitry Andric //             modeled conservatively, producing conjured symbols everywhere.
23480093f4SDimitry Andric //
24480093f4SDimitry Andric // To handle all these types in a common way we introduce a structure called
25480093f4SDimitry Andric // IteratorPosition which is an abstraction of the position the iterator
26480093f4SDimitry Andric // represents using symbolic expressions. The checker handles all the
27480093f4SDimitry Andric // operations on this structure.
28480093f4SDimitry Andric //
29480093f4SDimitry Andric // Additionally, depending on the circumstances, operators of types II and III
30480093f4SDimitry Andric // can be represented as:
31480093f4SDimitry Andric // * type-IIa, type-IIIa: conjured structure symbols - when returned by value
32480093f4SDimitry Andric //                        from conservatively evaluated methods such as
33480093f4SDimitry Andric //                        `.begin()`.
34480093f4SDimitry Andric // * type-IIb, type-IIIb: memory regions of iterator-typed objects, such as
35480093f4SDimitry Andric //                        variables or temporaries, when the iterator object is
36480093f4SDimitry Andric //                        currently treated as an lvalue.
37480093f4SDimitry Andric // * type-IIc, type-IIIc: compound values of iterator-typed objects, when the
38480093f4SDimitry Andric //                        iterator object is treated as an rvalue taken of a
39480093f4SDimitry Andric //                        particular lvalue, eg. a copy of "type-a" iterator
40480093f4SDimitry Andric //                        object, or an iterator that existed before the
41480093f4SDimitry Andric //                        analysis has started.
42480093f4SDimitry Andric //
43480093f4SDimitry Andric // To handle any of these three different representations stored in an SVal we
44480093f4SDimitry Andric // use setter and getters functions which separate the three cases. To store
45480093f4SDimitry Andric // them we use a pointer union of symbol and memory region.
46480093f4SDimitry Andric //
47480093f4SDimitry Andric // The checker works the following way: We record the begin and the
48480093f4SDimitry Andric // past-end iterator for all containers whenever their `.begin()` and `.end()`
49480093f4SDimitry Andric // are called. Since the Constraint Manager cannot handle such SVals we need
50480093f4SDimitry Andric // to take over its role. We post-check equality and non-equality comparisons
51480093f4SDimitry Andric // and record that the two sides are equal if we are in the 'equal' branch
52480093f4SDimitry Andric // (true-branch for `==` and false-branch for `!=`).
53480093f4SDimitry Andric //
54480093f4SDimitry Andric // In case of type-I or type-II iterators we get a concrete integer as a result
55480093f4SDimitry Andric // of the comparison (1 or 0) but in case of type-III we only get a Symbol. In
56480093f4SDimitry Andric // this latter case we record the symbol and reload it in evalAssume() and do
57480093f4SDimitry Andric // the propagation there. We also handle (maybe double) negated comparisons
58480093f4SDimitry Andric // which are represented in the form of (x == 0 or x != 0) where x is the
59480093f4SDimitry Andric // comparison itself.
60480093f4SDimitry Andric //
61480093f4SDimitry Andric // Since `SimpleConstraintManager` cannot handle complex symbolic expressions
62480093f4SDimitry Andric // we only use expressions of the format S, S+n or S-n for iterator positions
63480093f4SDimitry Andric // where S is a conjured symbol and n is an unsigned concrete integer. When
64480093f4SDimitry Andric // making an assumption e.g. `S1 + n == S2 + m` we store `S1 - S2 == m - n` as
65480093f4SDimitry Andric // a constraint which we later retrieve when doing an actual comparison.
66480093f4SDimitry Andric 
67480093f4SDimitry Andric #include "clang/AST/DeclTemplate.h"
68349cc55cSDimitry Andric #include "clang/StaticAnalyzer/Checkers/BuiltinCheckerRegistration.h"
69480093f4SDimitry Andric #include "clang/StaticAnalyzer/Core/BugReporter/BugType.h"
70480093f4SDimitry Andric #include "clang/StaticAnalyzer/Core/Checker.h"
71349cc55cSDimitry Andric #include "clang/StaticAnalyzer/Core/PathSensitive/CallDescription.h"
72480093f4SDimitry Andric #include "clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h"
73480093f4SDimitry Andric #include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h"
74480093f4SDimitry Andric #include "clang/StaticAnalyzer/Core/PathSensitive/DynamicType.h"
75*06c3fb27SDimitry Andric #include "llvm/ADT/STLExtras.h"
76480093f4SDimitry Andric 
77480093f4SDimitry Andric #include "Iterator.h"
78480093f4SDimitry Andric 
79480093f4SDimitry Andric #include <utility>
80480093f4SDimitry Andric 
81480093f4SDimitry Andric using namespace clang;
82480093f4SDimitry Andric using namespace ento;
83480093f4SDimitry Andric using namespace iterator;
84480093f4SDimitry Andric 
85480093f4SDimitry Andric namespace {
86480093f4SDimitry Andric 
87480093f4SDimitry Andric class IteratorModeling
885ffd83dbSDimitry Andric     : public Checker<check::PostCall, check::PostStmt<UnaryOperator>,
895ffd83dbSDimitry Andric                      check::PostStmt<BinaryOperator>,
905ffd83dbSDimitry Andric                      check::PostStmt<MaterializeTemporaryExpr>,
91480093f4SDimitry Andric                      check::Bind, check::LiveSymbols, check::DeadSymbols> {
92480093f4SDimitry Andric 
935ffd83dbSDimitry Andric   using AdvanceFn = void (IteratorModeling::*)(CheckerContext &, const Expr *,
945ffd83dbSDimitry Andric                                                SVal, SVal, SVal) const;
955ffd83dbSDimitry Andric 
965ffd83dbSDimitry Andric   void handleOverloadedOperator(CheckerContext &C, const CallEvent &Call,
975ffd83dbSDimitry Andric                                 OverloadedOperatorKind Op) const;
985ffd83dbSDimitry Andric   void handleAdvanceLikeFunction(CheckerContext &C, const CallEvent &Call,
995ffd83dbSDimitry Andric                                  const Expr *OrigExpr,
1005ffd83dbSDimitry Andric                                  const AdvanceFn *Handler) const;
1015ffd83dbSDimitry Andric 
102480093f4SDimitry Andric   void handleComparison(CheckerContext &C, const Expr *CE, SVal RetVal,
103480093f4SDimitry Andric                         const SVal &LVal, const SVal &RVal,
104480093f4SDimitry Andric                         OverloadedOperatorKind Op) const;
105480093f4SDimitry Andric   void processComparison(CheckerContext &C, ProgramStateRef State,
106480093f4SDimitry Andric                          SymbolRef Sym1, SymbolRef Sym2, const SVal &RetVal,
107480093f4SDimitry Andric                          OverloadedOperatorKind Op) const;
108480093f4SDimitry Andric   void handleIncrement(CheckerContext &C, const SVal &RetVal, const SVal &Iter,
109480093f4SDimitry Andric                        bool Postfix) const;
110480093f4SDimitry Andric   void handleDecrement(CheckerContext &C, const SVal &RetVal, const SVal &Iter,
111480093f4SDimitry Andric                        bool Postfix) const;
112480093f4SDimitry Andric   void handleRandomIncrOrDecr(CheckerContext &C, const Expr *CE,
113480093f4SDimitry Andric                               OverloadedOperatorKind Op, const SVal &RetVal,
114e8d8bef9SDimitry Andric                               const SVal &Iterator, const SVal &Amount) const;
1155ffd83dbSDimitry Andric   void handlePtrIncrOrDecr(CheckerContext &C, const Expr *Iterator,
1165ffd83dbSDimitry Andric                            OverloadedOperatorKind OK, SVal Offset) const;
1175ffd83dbSDimitry Andric   void handleAdvance(CheckerContext &C, const Expr *CE, SVal RetVal, SVal Iter,
1185ffd83dbSDimitry Andric                      SVal Amount) const;
1195ffd83dbSDimitry Andric   void handlePrev(CheckerContext &C, const Expr *CE, SVal RetVal, SVal Iter,
1205ffd83dbSDimitry Andric                   SVal Amount) const;
1215ffd83dbSDimitry Andric   void handleNext(CheckerContext &C, const Expr *CE, SVal RetVal, SVal Iter,
1225ffd83dbSDimitry Andric                   SVal Amount) const;
123480093f4SDimitry Andric   void assignToContainer(CheckerContext &C, const Expr *CE, const SVal &RetVal,
124480093f4SDimitry Andric                          const MemRegion *Cont) const;
1255ffd83dbSDimitry Andric   bool noChangeInAdvance(CheckerContext &C, SVal Iter, const Expr *CE) const;
126480093f4SDimitry Andric   void printState(raw_ostream &Out, ProgramStateRef State, const char *NL,
127480093f4SDimitry Andric                   const char *Sep) const override;
128480093f4SDimitry Andric 
1295ffd83dbSDimitry Andric   // std::advance, std::prev & std::next
1305ffd83dbSDimitry Andric   CallDescriptionMap<AdvanceFn> AdvanceLikeFunctions = {
1315ffd83dbSDimitry Andric       // template<class InputIt, class Distance>
1325ffd83dbSDimitry Andric       // void advance(InputIt& it, Distance n);
1335ffd83dbSDimitry Andric       {{{"std", "advance"}, 2}, &IteratorModeling::handleAdvance},
1345ffd83dbSDimitry Andric 
1355ffd83dbSDimitry Andric       // template<class BidirIt>
1365ffd83dbSDimitry Andric       // BidirIt prev(
1375ffd83dbSDimitry Andric       //   BidirIt it,
1385ffd83dbSDimitry Andric       //   typename std::iterator_traits<BidirIt>::difference_type n = 1);
1395ffd83dbSDimitry Andric       {{{"std", "prev"}, 2}, &IteratorModeling::handlePrev},
1405ffd83dbSDimitry Andric 
1415ffd83dbSDimitry Andric       // template<class ForwardIt>
1425ffd83dbSDimitry Andric       // ForwardIt next(
1435ffd83dbSDimitry Andric       //   ForwardIt it,
1445ffd83dbSDimitry Andric       //   typename std::iterator_traits<ForwardIt>::difference_type n = 1);
1455ffd83dbSDimitry Andric       {{{"std", "next"}, 2}, &IteratorModeling::handleNext},
1465ffd83dbSDimitry Andric   };
1475ffd83dbSDimitry Andric 
148480093f4SDimitry Andric public:
1495ffd83dbSDimitry Andric   IteratorModeling() = default;
150480093f4SDimitry Andric 
151480093f4SDimitry Andric   void checkPostCall(const CallEvent &Call, CheckerContext &C) const;
152480093f4SDimitry Andric   void checkBind(SVal Loc, SVal Val, const Stmt *S, CheckerContext &C) const;
1535ffd83dbSDimitry Andric   void checkPostStmt(const UnaryOperator *UO, CheckerContext &C) const;
1545ffd83dbSDimitry Andric   void checkPostStmt(const BinaryOperator *BO, CheckerContext &C) const;
155480093f4SDimitry Andric   void checkPostStmt(const MaterializeTemporaryExpr *MTE,
156480093f4SDimitry Andric                      CheckerContext &C) const;
157480093f4SDimitry Andric   void checkLiveSymbols(ProgramStateRef State, SymbolReaper &SR) const;
158480093f4SDimitry Andric   void checkDeadSymbols(SymbolReaper &SR, CheckerContext &C) const;
159480093f4SDimitry Andric };
160480093f4SDimitry Andric 
161480093f4SDimitry Andric bool isSimpleComparisonOperator(OverloadedOperatorKind OK);
1625ffd83dbSDimitry Andric bool isSimpleComparisonOperator(BinaryOperatorKind OK);
163480093f4SDimitry Andric ProgramStateRef removeIteratorPosition(ProgramStateRef State, const SVal &Val);
164480093f4SDimitry Andric ProgramStateRef relateSymbols(ProgramStateRef State, SymbolRef Sym1,
165480093f4SDimitry Andric                               SymbolRef Sym2, bool Equal);
166480093f4SDimitry Andric bool isBoundThroughLazyCompoundVal(const Environment &Env,
167480093f4SDimitry Andric                                    const MemRegion *Reg);
1685ffd83dbSDimitry Andric const ExplodedNode *findCallEnter(const ExplodedNode *Node, const Expr *Call);
169480093f4SDimitry Andric 
170480093f4SDimitry Andric } // namespace
171480093f4SDimitry Andric 
172480093f4SDimitry Andric void IteratorModeling::checkPostCall(const CallEvent &Call,
173480093f4SDimitry Andric                                      CheckerContext &C) const {
174480093f4SDimitry Andric   // Record new iterator positions and iterator position changes
175480093f4SDimitry Andric   const auto *Func = dyn_cast_or_null<FunctionDecl>(Call.getDecl());
176480093f4SDimitry Andric   if (!Func)
177480093f4SDimitry Andric     return;
178480093f4SDimitry Andric 
179480093f4SDimitry Andric   if (Func->isOverloadedOperator()) {
180480093f4SDimitry Andric     const auto Op = Func->getOverloadedOperator();
1815ffd83dbSDimitry Andric     handleOverloadedOperator(C, Call, Op);
182480093f4SDimitry Andric     return;
183480093f4SDimitry Andric   }
184480093f4SDimitry Andric 
185480093f4SDimitry Andric   const auto *OrigExpr = Call.getOriginExpr();
186480093f4SDimitry Andric   if (!OrigExpr)
187480093f4SDimitry Andric     return;
188480093f4SDimitry Andric 
1895ffd83dbSDimitry Andric   const AdvanceFn *Handler = AdvanceLikeFunctions.lookup(Call);
1905ffd83dbSDimitry Andric   if (Handler) {
1915ffd83dbSDimitry Andric     handleAdvanceLikeFunction(C, Call, OrigExpr, Handler);
1925ffd83dbSDimitry Andric     return;
1935ffd83dbSDimitry Andric   }
1945ffd83dbSDimitry Andric 
195480093f4SDimitry Andric   if (!isIteratorType(Call.getResultType()))
196480093f4SDimitry Andric     return;
197480093f4SDimitry Andric 
198480093f4SDimitry Andric   auto State = C.getState();
199480093f4SDimitry Andric 
200480093f4SDimitry Andric   // Already bound to container?
201480093f4SDimitry Andric   if (getIteratorPosition(State, Call.getReturnValue()))
202480093f4SDimitry Andric     return;
203480093f4SDimitry Andric 
204480093f4SDimitry Andric   // Copy-like and move constructors
205480093f4SDimitry Andric   if (isa<CXXConstructorCall>(&Call) && Call.getNumArgs() == 1) {
206480093f4SDimitry Andric     if (const auto *Pos = getIteratorPosition(State, Call.getArgSVal(0))) {
207480093f4SDimitry Andric       State = setIteratorPosition(State, Call.getReturnValue(), *Pos);
208480093f4SDimitry Andric       if (cast<CXXConstructorDecl>(Func)->isMoveConstructor()) {
209480093f4SDimitry Andric         State = removeIteratorPosition(State, Call.getArgSVal(0));
210480093f4SDimitry Andric       }
211480093f4SDimitry Andric       C.addTransition(State);
212480093f4SDimitry Andric       return;
213480093f4SDimitry Andric     }
214480093f4SDimitry Andric   }
215480093f4SDimitry Andric 
216480093f4SDimitry Andric   // Assumption: if return value is an iterator which is not yet bound to a
2175ffd83dbSDimitry Andric   //             container, then look for the first iterator argument of the
2185ffd83dbSDimitry Andric   //             same type as the return value and bind the return value to
2195ffd83dbSDimitry Andric   //             the same container. This approach works for STL algorithms.
220480093f4SDimitry Andric   // FIXME: Add a more conservative mode
221480093f4SDimitry Andric   for (unsigned i = 0; i < Call.getNumArgs(); ++i) {
2225ffd83dbSDimitry Andric     if (isIteratorType(Call.getArgExpr(i)->getType()) &&
2235ffd83dbSDimitry Andric         Call.getArgExpr(i)->getType().getNonReferenceType().getDesugaredType(
2245ffd83dbSDimitry Andric             C.getASTContext()).getTypePtr() ==
2255ffd83dbSDimitry Andric         Call.getResultType().getDesugaredType(C.getASTContext()).getTypePtr()) {
226480093f4SDimitry Andric       if (const auto *Pos = getIteratorPosition(State, Call.getArgSVal(i))) {
227480093f4SDimitry Andric         assignToContainer(C, OrigExpr, Call.getReturnValue(),
228480093f4SDimitry Andric                           Pos->getContainer());
229480093f4SDimitry Andric         return;
230480093f4SDimitry Andric       }
231480093f4SDimitry Andric     }
232480093f4SDimitry Andric   }
233480093f4SDimitry Andric }
234480093f4SDimitry Andric 
235480093f4SDimitry Andric void IteratorModeling::checkBind(SVal Loc, SVal Val, const Stmt *S,
236480093f4SDimitry Andric                                  CheckerContext &C) const {
237480093f4SDimitry Andric   auto State = C.getState();
238480093f4SDimitry Andric   const auto *Pos = getIteratorPosition(State, Val);
239480093f4SDimitry Andric   if (Pos) {
240480093f4SDimitry Andric     State = setIteratorPosition(State, Loc, *Pos);
241480093f4SDimitry Andric     C.addTransition(State);
242480093f4SDimitry Andric   } else {
243480093f4SDimitry Andric     const auto *OldPos = getIteratorPosition(State, Loc);
244480093f4SDimitry Andric     if (OldPos) {
245480093f4SDimitry Andric       State = removeIteratorPosition(State, Loc);
246480093f4SDimitry Andric       C.addTransition(State);
247480093f4SDimitry Andric     }
248480093f4SDimitry Andric   }
249480093f4SDimitry Andric }
250480093f4SDimitry Andric 
2515ffd83dbSDimitry Andric void IteratorModeling::checkPostStmt(const UnaryOperator *UO,
2525ffd83dbSDimitry Andric                                      CheckerContext &C) const {
2535ffd83dbSDimitry Andric   UnaryOperatorKind OK = UO->getOpcode();
2545ffd83dbSDimitry Andric   if (!isIncrementOperator(OK) && !isDecrementOperator(OK))
2555ffd83dbSDimitry Andric     return;
2565ffd83dbSDimitry Andric 
2575ffd83dbSDimitry Andric   auto &SVB = C.getSValBuilder();
2585ffd83dbSDimitry Andric   handlePtrIncrOrDecr(C, UO->getSubExpr(),
2595ffd83dbSDimitry Andric                       isIncrementOperator(OK) ? OO_Plus : OO_Minus,
2605ffd83dbSDimitry Andric                       SVB.makeArrayIndex(1));
2615ffd83dbSDimitry Andric }
2625ffd83dbSDimitry Andric 
2635ffd83dbSDimitry Andric void IteratorModeling::checkPostStmt(const BinaryOperator *BO,
2645ffd83dbSDimitry Andric                                      CheckerContext &C) const {
265e8d8bef9SDimitry Andric   const ProgramStateRef State = C.getState();
266e8d8bef9SDimitry Andric   const BinaryOperatorKind OK = BO->getOpcode();
267e8d8bef9SDimitry Andric   const Expr *const LHS = BO->getLHS();
268e8d8bef9SDimitry Andric   const Expr *const RHS = BO->getRHS();
269e8d8bef9SDimitry Andric   const SVal LVal = State->getSVal(LHS, C.getLocationContext());
270e8d8bef9SDimitry Andric   const SVal RVal = State->getSVal(RHS, C.getLocationContext());
2715ffd83dbSDimitry Andric 
2725ffd83dbSDimitry Andric   if (isSimpleComparisonOperator(BO->getOpcode())) {
2735ffd83dbSDimitry Andric     SVal Result = State->getSVal(BO, C.getLocationContext());
2745ffd83dbSDimitry Andric     handleComparison(C, BO, Result, LVal, RVal,
2755ffd83dbSDimitry Andric                      BinaryOperator::getOverloadedOperator(OK));
2765ffd83dbSDimitry Andric   } else if (isRandomIncrOrDecrOperator(OK)) {
277e8d8bef9SDimitry Andric     // In case of operator+ the iterator can be either on the LHS (eg.: it + 1),
278e8d8bef9SDimitry Andric     // or on the RHS (eg.: 1 + it). Both cases are modeled.
279e8d8bef9SDimitry Andric     const bool IsIterOnLHS = BO->getLHS()->getType()->isPointerType();
280e8d8bef9SDimitry Andric     const Expr *const &IterExpr = IsIterOnLHS ? LHS : RHS;
281e8d8bef9SDimitry Andric     const Expr *const &AmountExpr = IsIterOnLHS ? RHS : LHS;
282e8d8bef9SDimitry Andric 
283e8d8bef9SDimitry Andric     // The non-iterator side must have an integral or enumeration type.
284e8d8bef9SDimitry Andric     if (!AmountExpr->getType()->isIntegralOrEnumerationType())
285e8d8bef9SDimitry Andric       return;
286e8d8bef9SDimitry Andric     const SVal &AmountVal = IsIterOnLHS ? RVal : LVal;
287e8d8bef9SDimitry Andric     handlePtrIncrOrDecr(C, IterExpr, BinaryOperator::getOverloadedOperator(OK),
288e8d8bef9SDimitry Andric                         AmountVal);
2895ffd83dbSDimitry Andric   }
2905ffd83dbSDimitry Andric }
2915ffd83dbSDimitry Andric 
292480093f4SDimitry Andric void IteratorModeling::checkPostStmt(const MaterializeTemporaryExpr *MTE,
293480093f4SDimitry Andric                                      CheckerContext &C) const {
294480093f4SDimitry Andric   /* Transfer iterator state to temporary objects */
295480093f4SDimitry Andric   auto State = C.getState();
296480093f4SDimitry Andric   const auto *Pos = getIteratorPosition(State, C.getSVal(MTE->getSubExpr()));
297480093f4SDimitry Andric   if (!Pos)
298480093f4SDimitry Andric     return;
299480093f4SDimitry Andric   State = setIteratorPosition(State, C.getSVal(MTE), *Pos);
300480093f4SDimitry Andric   C.addTransition(State);
301480093f4SDimitry Andric }
302480093f4SDimitry Andric 
303480093f4SDimitry Andric void IteratorModeling::checkLiveSymbols(ProgramStateRef State,
304480093f4SDimitry Andric                                         SymbolReaper &SR) const {
3055ffd83dbSDimitry Andric   // Keep symbolic expressions of iterator positions alive
306480093f4SDimitry Andric   auto RegionMap = State->get<IteratorRegionMap>();
307*06c3fb27SDimitry Andric   for (const IteratorPosition &Pos : llvm::make_second_range(RegionMap)) {
308*06c3fb27SDimitry Andric     for (SymbolRef Sym : Pos.getOffset()->symbols())
309*06c3fb27SDimitry Andric       if (isa<SymbolData>(Sym))
310*06c3fb27SDimitry Andric         SR.markLive(Sym);
311480093f4SDimitry Andric   }
312480093f4SDimitry Andric 
313480093f4SDimitry Andric   auto SymbolMap = State->get<IteratorSymbolMap>();
314*06c3fb27SDimitry Andric   for (const IteratorPosition &Pos : llvm::make_second_range(SymbolMap)) {
315*06c3fb27SDimitry Andric     for (SymbolRef Sym : Pos.getOffset()->symbols())
316*06c3fb27SDimitry Andric       if (isa<SymbolData>(Sym))
317*06c3fb27SDimitry Andric         SR.markLive(Sym);
318480093f4SDimitry Andric   }
319480093f4SDimitry Andric }
320480093f4SDimitry Andric 
321480093f4SDimitry Andric void IteratorModeling::checkDeadSymbols(SymbolReaper &SR,
322480093f4SDimitry Andric                                         CheckerContext &C) const {
323480093f4SDimitry Andric   // Cleanup
324480093f4SDimitry Andric   auto State = C.getState();
325480093f4SDimitry Andric 
326480093f4SDimitry Andric   auto RegionMap = State->get<IteratorRegionMap>();
327480093f4SDimitry Andric   for (const auto &Reg : RegionMap) {
328480093f4SDimitry Andric     if (!SR.isLiveRegion(Reg.first)) {
329480093f4SDimitry Andric       // The region behind the `LazyCompoundVal` is often cleaned up before
330480093f4SDimitry Andric       // the `LazyCompoundVal` itself. If there are iterator positions keyed
331480093f4SDimitry Andric       // by these regions their cleanup must be deferred.
332480093f4SDimitry Andric       if (!isBoundThroughLazyCompoundVal(State->getEnvironment(), Reg.first)) {
333480093f4SDimitry Andric         State = State->remove<IteratorRegionMap>(Reg.first);
334480093f4SDimitry Andric       }
335480093f4SDimitry Andric     }
336480093f4SDimitry Andric   }
337480093f4SDimitry Andric 
338480093f4SDimitry Andric   auto SymbolMap = State->get<IteratorSymbolMap>();
339480093f4SDimitry Andric   for (const auto &Sym : SymbolMap) {
340480093f4SDimitry Andric     if (!SR.isLive(Sym.first)) {
341480093f4SDimitry Andric       State = State->remove<IteratorSymbolMap>(Sym.first);
342480093f4SDimitry Andric     }
343480093f4SDimitry Andric   }
344480093f4SDimitry Andric 
3455ffd83dbSDimitry Andric   C.addTransition(State);
346480093f4SDimitry Andric }
3475ffd83dbSDimitry Andric 
3485ffd83dbSDimitry Andric void
3495ffd83dbSDimitry Andric IteratorModeling::handleOverloadedOperator(CheckerContext &C,
3505ffd83dbSDimitry Andric                                            const CallEvent &Call,
3515ffd83dbSDimitry Andric                                            OverloadedOperatorKind Op) const {
3525ffd83dbSDimitry Andric     if (isSimpleComparisonOperator(Op)) {
3535ffd83dbSDimitry Andric       const auto *OrigExpr = Call.getOriginExpr();
3545ffd83dbSDimitry Andric       if (!OrigExpr)
3555ffd83dbSDimitry Andric         return;
3565ffd83dbSDimitry Andric 
3575ffd83dbSDimitry Andric       if (const auto *InstCall = dyn_cast<CXXInstanceCall>(&Call)) {
3585ffd83dbSDimitry Andric         handleComparison(C, OrigExpr, Call.getReturnValue(),
3595ffd83dbSDimitry Andric                          InstCall->getCXXThisVal(), Call.getArgSVal(0), Op);
3605ffd83dbSDimitry Andric         return;
3615ffd83dbSDimitry Andric       }
3625ffd83dbSDimitry Andric 
3635ffd83dbSDimitry Andric       handleComparison(C, OrigExpr, Call.getReturnValue(), Call.getArgSVal(0),
3645ffd83dbSDimitry Andric                          Call.getArgSVal(1), Op);
3655ffd83dbSDimitry Andric       return;
3665ffd83dbSDimitry Andric     } else if (isRandomIncrOrDecrOperator(Op)) {
3675ffd83dbSDimitry Andric       const auto *OrigExpr = Call.getOriginExpr();
3685ffd83dbSDimitry Andric       if (!OrigExpr)
3695ffd83dbSDimitry Andric         return;
3705ffd83dbSDimitry Andric 
3715ffd83dbSDimitry Andric       if (const auto *InstCall = dyn_cast<CXXInstanceCall>(&Call)) {
3725ffd83dbSDimitry Andric         if (Call.getNumArgs() >= 1 &&
3735ffd83dbSDimitry Andric               Call.getArgExpr(0)->getType()->isIntegralOrEnumerationType()) {
3745ffd83dbSDimitry Andric           handleRandomIncrOrDecr(C, OrigExpr, Op, Call.getReturnValue(),
3755ffd83dbSDimitry Andric                                  InstCall->getCXXThisVal(), Call.getArgSVal(0));
3765ffd83dbSDimitry Andric           return;
3775ffd83dbSDimitry Andric         }
378e8d8bef9SDimitry Andric       } else if (Call.getNumArgs() >= 2) {
379e8d8bef9SDimitry Andric         const Expr *FirstArg = Call.getArgExpr(0);
380e8d8bef9SDimitry Andric         const Expr *SecondArg = Call.getArgExpr(1);
381e8d8bef9SDimitry Andric         const QualType FirstType = FirstArg->getType();
382e8d8bef9SDimitry Andric         const QualType SecondType = SecondArg->getType();
383e8d8bef9SDimitry Andric 
384e8d8bef9SDimitry Andric         if (FirstType->isIntegralOrEnumerationType() ||
385e8d8bef9SDimitry Andric             SecondType->isIntegralOrEnumerationType()) {
386e8d8bef9SDimitry Andric           // In case of operator+ the iterator can be either on the LHS (eg.:
387e8d8bef9SDimitry Andric           // it + 1), or on the RHS (eg.: 1 + it). Both cases are modeled.
388e8d8bef9SDimitry Andric           const bool IsIterFirst = FirstType->isStructureOrClassType();
389e8d8bef9SDimitry Andric           const SVal FirstArg = Call.getArgSVal(0);
390e8d8bef9SDimitry Andric           const SVal SecondArg = Call.getArgSVal(1);
391e8d8bef9SDimitry Andric           const SVal &Iterator = IsIterFirst ? FirstArg : SecondArg;
392e8d8bef9SDimitry Andric           const SVal &Amount = IsIterFirst ? SecondArg : FirstArg;
393e8d8bef9SDimitry Andric 
3945ffd83dbSDimitry Andric           handleRandomIncrOrDecr(C, OrigExpr, Op, Call.getReturnValue(),
395e8d8bef9SDimitry Andric                                  Iterator, Amount);
3965ffd83dbSDimitry Andric           return;
3975ffd83dbSDimitry Andric         }
3985ffd83dbSDimitry Andric       }
3995ffd83dbSDimitry Andric     } else if (isIncrementOperator(Op)) {
4005ffd83dbSDimitry Andric       if (const auto *InstCall = dyn_cast<CXXInstanceCall>(&Call)) {
4015ffd83dbSDimitry Andric         handleIncrement(C, Call.getReturnValue(), InstCall->getCXXThisVal(),
4025ffd83dbSDimitry Andric                         Call.getNumArgs());
4035ffd83dbSDimitry Andric         return;
4045ffd83dbSDimitry Andric       }
4055ffd83dbSDimitry Andric 
4065ffd83dbSDimitry Andric       handleIncrement(C, Call.getReturnValue(), Call.getArgSVal(0),
4075ffd83dbSDimitry Andric                       Call.getNumArgs());
4085ffd83dbSDimitry Andric       return;
4095ffd83dbSDimitry Andric     } else if (isDecrementOperator(Op)) {
4105ffd83dbSDimitry Andric       if (const auto *InstCall = dyn_cast<CXXInstanceCall>(&Call)) {
4115ffd83dbSDimitry Andric         handleDecrement(C, Call.getReturnValue(), InstCall->getCXXThisVal(),
4125ffd83dbSDimitry Andric                         Call.getNumArgs());
4135ffd83dbSDimitry Andric         return;
4145ffd83dbSDimitry Andric       }
4155ffd83dbSDimitry Andric 
4165ffd83dbSDimitry Andric       handleDecrement(C, Call.getReturnValue(), Call.getArgSVal(0),
4175ffd83dbSDimitry Andric                         Call.getNumArgs());
4185ffd83dbSDimitry Andric       return;
419480093f4SDimitry Andric     }
420480093f4SDimitry Andric }
421480093f4SDimitry Andric 
4225ffd83dbSDimitry Andric void
4235ffd83dbSDimitry Andric IteratorModeling::handleAdvanceLikeFunction(CheckerContext &C,
4245ffd83dbSDimitry Andric                                             const CallEvent &Call,
4255ffd83dbSDimitry Andric                                             const Expr *OrigExpr,
4265ffd83dbSDimitry Andric                                             const AdvanceFn *Handler) const {
4275ffd83dbSDimitry Andric   if (!C.wasInlined) {
4285ffd83dbSDimitry Andric     (this->**Handler)(C, OrigExpr, Call.getReturnValue(),
4295ffd83dbSDimitry Andric                       Call.getArgSVal(0), Call.getArgSVal(1));
4305ffd83dbSDimitry Andric     return;
4315ffd83dbSDimitry Andric   }
4325ffd83dbSDimitry Andric 
4335ffd83dbSDimitry Andric   // If std::advance() was inlined, but a non-standard function it calls inside
4345ffd83dbSDimitry Andric   // was not, then we have to model it explicitly
4355ffd83dbSDimitry Andric   const auto *IdInfo = cast<FunctionDecl>(Call.getDecl())->getIdentifier();
4365ffd83dbSDimitry Andric   if (IdInfo) {
4375ffd83dbSDimitry Andric     if (IdInfo->getName() == "advance") {
4385ffd83dbSDimitry Andric       if (noChangeInAdvance(C, Call.getArgSVal(0), OrigExpr)) {
4395ffd83dbSDimitry Andric         (this->**Handler)(C, OrigExpr, Call.getReturnValue(),
4405ffd83dbSDimitry Andric                           Call.getArgSVal(0), Call.getArgSVal(1));
4415ffd83dbSDimitry Andric       }
4425ffd83dbSDimitry Andric     }
4435ffd83dbSDimitry Andric   }
444480093f4SDimitry Andric }
445480093f4SDimitry Andric 
446480093f4SDimitry Andric void IteratorModeling::handleComparison(CheckerContext &C, const Expr *CE,
447480093f4SDimitry Andric                                        SVal RetVal, const SVal &LVal,
448480093f4SDimitry Andric                                        const SVal &RVal,
449480093f4SDimitry Andric                                        OverloadedOperatorKind Op) const {
450480093f4SDimitry Andric   // Record the operands and the operator of the comparison for the next
451480093f4SDimitry Andric   // evalAssume, if the result is a symbolic expression. If it is a concrete
452480093f4SDimitry Andric   // value (only one branch is possible), then transfer the state between
453480093f4SDimitry Andric   // the operands according to the operator and the result
454480093f4SDimitry Andric    auto State = C.getState();
455480093f4SDimitry Andric   const auto *LPos = getIteratorPosition(State, LVal);
456480093f4SDimitry Andric   const auto *RPos = getIteratorPosition(State, RVal);
457480093f4SDimitry Andric   const MemRegion *Cont = nullptr;
458480093f4SDimitry Andric   if (LPos) {
459480093f4SDimitry Andric     Cont = LPos->getContainer();
460480093f4SDimitry Andric   } else if (RPos) {
461480093f4SDimitry Andric     Cont = RPos->getContainer();
462480093f4SDimitry Andric   }
463480093f4SDimitry Andric   if (!Cont)
464480093f4SDimitry Andric     return;
465480093f4SDimitry Andric 
4665ffd83dbSDimitry Andric   // At least one of the iterators has recorded positions. If one of them does
467480093f4SDimitry Andric   // not then create a new symbol for the offset.
468480093f4SDimitry Andric   SymbolRef Sym;
469480093f4SDimitry Andric   if (!LPos || !RPos) {
470480093f4SDimitry Andric     auto &SymMgr = C.getSymbolManager();
471480093f4SDimitry Andric     Sym = SymMgr.conjureSymbol(CE, C.getLocationContext(),
472480093f4SDimitry Andric                                C.getASTContext().LongTy, C.blockCount());
473480093f4SDimitry Andric     State = assumeNoOverflow(State, Sym, 4);
474480093f4SDimitry Andric   }
475480093f4SDimitry Andric 
476480093f4SDimitry Andric   if (!LPos) {
477480093f4SDimitry Andric     State = setIteratorPosition(State, LVal,
478480093f4SDimitry Andric                                 IteratorPosition::getPosition(Cont, Sym));
479480093f4SDimitry Andric     LPos = getIteratorPosition(State, LVal);
480480093f4SDimitry Andric   } else if (!RPos) {
481480093f4SDimitry Andric     State = setIteratorPosition(State, RVal,
482480093f4SDimitry Andric                                 IteratorPosition::getPosition(Cont, Sym));
483480093f4SDimitry Andric     RPos = getIteratorPosition(State, RVal);
484480093f4SDimitry Andric   }
485480093f4SDimitry Andric 
486e8d8bef9SDimitry Andric   // If the value for which we just tried to set a new iterator position is
487e8d8bef9SDimitry Andric   // an `SVal`for which no iterator position can be set then the setting was
488e8d8bef9SDimitry Andric   // unsuccessful. We cannot handle the comparison in this case.
489e8d8bef9SDimitry Andric   if (!LPos || !RPos)
490e8d8bef9SDimitry Andric     return;
491e8d8bef9SDimitry Andric 
4925ffd83dbSDimitry Andric   // We cannot make assumptions on `UnknownVal`. Let us conjure a symbol
493480093f4SDimitry Andric   // instead.
494480093f4SDimitry Andric   if (RetVal.isUnknown()) {
495480093f4SDimitry Andric     auto &SymMgr = C.getSymbolManager();
496480093f4SDimitry Andric     auto *LCtx = C.getLocationContext();
497480093f4SDimitry Andric     RetVal = nonloc::SymbolVal(SymMgr.conjureSymbol(
498480093f4SDimitry Andric         CE, LCtx, C.getASTContext().BoolTy, C.blockCount()));
499480093f4SDimitry Andric     State = State->BindExpr(CE, LCtx, RetVal);
500480093f4SDimitry Andric   }
501480093f4SDimitry Andric 
502480093f4SDimitry Andric   processComparison(C, State, LPos->getOffset(), RPos->getOffset(), RetVal, Op);
503480093f4SDimitry Andric }
504480093f4SDimitry Andric 
505480093f4SDimitry Andric void IteratorModeling::processComparison(CheckerContext &C,
506480093f4SDimitry Andric                                          ProgramStateRef State, SymbolRef Sym1,
507480093f4SDimitry Andric                                          SymbolRef Sym2, const SVal &RetVal,
508480093f4SDimitry Andric                                          OverloadedOperatorKind Op) const {
509480093f4SDimitry Andric   if (const auto TruthVal = RetVal.getAs<nonloc::ConcreteInt>()) {
510480093f4SDimitry Andric     if ((State = relateSymbols(State, Sym1, Sym2,
511480093f4SDimitry Andric                               (Op == OO_EqualEqual) ==
512480093f4SDimitry Andric                                (TruthVal->getValue() != 0)))) {
513480093f4SDimitry Andric       C.addTransition(State);
514480093f4SDimitry Andric     } else {
515480093f4SDimitry Andric       C.generateSink(State, C.getPredecessor());
516480093f4SDimitry Andric     }
517480093f4SDimitry Andric     return;
518480093f4SDimitry Andric   }
519480093f4SDimitry Andric 
520480093f4SDimitry Andric   const auto ConditionVal = RetVal.getAs<DefinedSVal>();
521480093f4SDimitry Andric   if (!ConditionVal)
522480093f4SDimitry Andric     return;
523480093f4SDimitry Andric 
524480093f4SDimitry Andric   if (auto StateTrue = relateSymbols(State, Sym1, Sym2, Op == OO_EqualEqual)) {
525480093f4SDimitry Andric     StateTrue = StateTrue->assume(*ConditionVal, true);
526480093f4SDimitry Andric     C.addTransition(StateTrue);
527480093f4SDimitry Andric   }
528480093f4SDimitry Andric 
529480093f4SDimitry Andric   if (auto StateFalse = relateSymbols(State, Sym1, Sym2, Op != OO_EqualEqual)) {
530480093f4SDimitry Andric     StateFalse = StateFalse->assume(*ConditionVal, false);
531480093f4SDimitry Andric     C.addTransition(StateFalse);
532480093f4SDimitry Andric   }
533480093f4SDimitry Andric }
534480093f4SDimitry Andric 
535480093f4SDimitry Andric void IteratorModeling::handleIncrement(CheckerContext &C, const SVal &RetVal,
536480093f4SDimitry Andric                                        const SVal &Iter, bool Postfix) const {
537480093f4SDimitry Andric   // Increment the symbolic expressions which represents the position of the
538480093f4SDimitry Andric   // iterator
539480093f4SDimitry Andric   auto State = C.getState();
540480093f4SDimitry Andric   auto &BVF = C.getSymbolManager().getBasicVals();
541480093f4SDimitry Andric 
542480093f4SDimitry Andric   const auto *Pos = getIteratorPosition(State, Iter);
543480093f4SDimitry Andric   if (!Pos)
544480093f4SDimitry Andric     return;
545480093f4SDimitry Andric 
546480093f4SDimitry Andric   auto NewState =
547480093f4SDimitry Andric     advancePosition(State, Iter, OO_Plus,
548480093f4SDimitry Andric                     nonloc::ConcreteInt(BVF.getValue(llvm::APSInt::get(1))));
549480093f4SDimitry Andric   assert(NewState &&
550480093f4SDimitry Andric          "Advancing position by concrete int should always be successful");
551480093f4SDimitry Andric 
552480093f4SDimitry Andric   const auto *NewPos = getIteratorPosition(NewState, Iter);
553480093f4SDimitry Andric   assert(NewPos &&
554480093f4SDimitry Andric          "Iterator should have position after successful advancement");
555480093f4SDimitry Andric 
556480093f4SDimitry Andric   State = setIteratorPosition(State, Iter, *NewPos);
557480093f4SDimitry Andric   State = setIteratorPosition(State, RetVal, Postfix ? *Pos : *NewPos);
558480093f4SDimitry Andric   C.addTransition(State);
559480093f4SDimitry Andric }
560480093f4SDimitry Andric 
561480093f4SDimitry Andric void IteratorModeling::handleDecrement(CheckerContext &C, const SVal &RetVal,
562480093f4SDimitry Andric                                        const SVal &Iter, bool Postfix) const {
563480093f4SDimitry Andric   // Decrement the symbolic expressions which represents the position of the
564480093f4SDimitry Andric   // iterator
565480093f4SDimitry Andric   auto State = C.getState();
566480093f4SDimitry Andric   auto &BVF = C.getSymbolManager().getBasicVals();
567480093f4SDimitry Andric 
568480093f4SDimitry Andric   const auto *Pos = getIteratorPosition(State, Iter);
569480093f4SDimitry Andric   if (!Pos)
570480093f4SDimitry Andric     return;
571480093f4SDimitry Andric 
572480093f4SDimitry Andric   auto NewState =
573480093f4SDimitry Andric     advancePosition(State, Iter, OO_Minus,
574480093f4SDimitry Andric                     nonloc::ConcreteInt(BVF.getValue(llvm::APSInt::get(1))));
575480093f4SDimitry Andric   assert(NewState &&
576480093f4SDimitry Andric          "Advancing position by concrete int should always be successful");
577480093f4SDimitry Andric 
578480093f4SDimitry Andric   const auto *NewPos = getIteratorPosition(NewState, Iter);
579480093f4SDimitry Andric   assert(NewPos &&
580480093f4SDimitry Andric          "Iterator should have position after successful advancement");
581480093f4SDimitry Andric 
582480093f4SDimitry Andric   State = setIteratorPosition(State, Iter, *NewPos);
583480093f4SDimitry Andric   State = setIteratorPosition(State, RetVal, Postfix ? *Pos : *NewPos);
584480093f4SDimitry Andric   C.addTransition(State);
585480093f4SDimitry Andric }
586480093f4SDimitry Andric 
587e8d8bef9SDimitry Andric void IteratorModeling::handleRandomIncrOrDecr(CheckerContext &C, const Expr *CE,
588480093f4SDimitry Andric                                               OverloadedOperatorKind Op,
589480093f4SDimitry Andric                                               const SVal &RetVal,
590e8d8bef9SDimitry Andric                                               const SVal &Iterator,
591e8d8bef9SDimitry Andric                                               const SVal &Amount) const {
592480093f4SDimitry Andric   // Increment or decrement the symbolic expressions which represents the
593480093f4SDimitry Andric   // position of the iterator
594480093f4SDimitry Andric   auto State = C.getState();
595480093f4SDimitry Andric 
596e8d8bef9SDimitry Andric   const auto *Pos = getIteratorPosition(State, Iterator);
597480093f4SDimitry Andric   if (!Pos)
598480093f4SDimitry Andric     return;
599480093f4SDimitry Andric 
600e8d8bef9SDimitry Andric   const auto *Value = &Amount;
601e8d8bef9SDimitry Andric   SVal Val;
602e8d8bef9SDimitry Andric   if (auto LocAmount = Amount.getAs<Loc>()) {
603e8d8bef9SDimitry Andric     Val = State->getRawSVal(*LocAmount);
604e8d8bef9SDimitry Andric     Value = &Val;
605480093f4SDimitry Andric   }
606480093f4SDimitry Andric 
607e8d8bef9SDimitry Andric   const auto &TgtVal =
608e8d8bef9SDimitry Andric       (Op == OO_PlusEqual || Op == OO_MinusEqual) ? Iterator : RetVal;
609480093f4SDimitry Andric 
6105ffd83dbSDimitry Andric   // `AdvancedState` is a state where the position of `LHS` is advanced. We
6115ffd83dbSDimitry Andric   // only need this state to retrieve the new position, but we do not want
6125ffd83dbSDimitry Andric   // to change the position of `LHS` (in every case).
613e8d8bef9SDimitry Andric   auto AdvancedState = advancePosition(State, Iterator, Op, *Value);
6145ffd83dbSDimitry Andric   if (AdvancedState) {
615e8d8bef9SDimitry Andric     const auto *NewPos = getIteratorPosition(AdvancedState, Iterator);
616480093f4SDimitry Andric     assert(NewPos &&
617480093f4SDimitry Andric            "Iterator should have position after successful advancement");
618480093f4SDimitry Andric 
6195ffd83dbSDimitry Andric     State = setIteratorPosition(State, TgtVal, *NewPos);
620480093f4SDimitry Andric     C.addTransition(State);
621480093f4SDimitry Andric   } else {
622480093f4SDimitry Andric     assignToContainer(C, CE, TgtVal, Pos->getContainer());
623480093f4SDimitry Andric   }
624480093f4SDimitry Andric }
625480093f4SDimitry Andric 
6265ffd83dbSDimitry Andric void IteratorModeling::handlePtrIncrOrDecr(CheckerContext &C,
6275ffd83dbSDimitry Andric                                            const Expr *Iterator,
6285ffd83dbSDimitry Andric                                            OverloadedOperatorKind OK,
6295ffd83dbSDimitry Andric                                            SVal Offset) const {
63081ad6265SDimitry Andric   if (!isa<DefinedSVal>(Offset))
631e8d8bef9SDimitry Andric     return;
632e8d8bef9SDimitry Andric 
6335ffd83dbSDimitry Andric   QualType PtrType = Iterator->getType();
6345ffd83dbSDimitry Andric   if (!PtrType->isPointerType())
6355ffd83dbSDimitry Andric     return;
6365ffd83dbSDimitry Andric   QualType ElementType = PtrType->getPointeeType();
6375ffd83dbSDimitry Andric 
6385ffd83dbSDimitry Andric   ProgramStateRef State = C.getState();
6395ffd83dbSDimitry Andric   SVal OldVal = State->getSVal(Iterator, C.getLocationContext());
6405ffd83dbSDimitry Andric 
6415ffd83dbSDimitry Andric   const IteratorPosition *OldPos = getIteratorPosition(State, OldVal);
6425ffd83dbSDimitry Andric   if (!OldPos)
643480093f4SDimitry Andric     return;
644480093f4SDimitry Andric 
6455ffd83dbSDimitry Andric   SVal NewVal;
646e8d8bef9SDimitry Andric   if (OK == OO_Plus || OK == OO_PlusEqual) {
6475ffd83dbSDimitry Andric     NewVal = State->getLValue(ElementType, Offset, OldVal);
648e8d8bef9SDimitry Andric   } else {
649e8d8bef9SDimitry Andric     auto &SVB = C.getSValBuilder();
650e8d8bef9SDimitry Andric     SVal NegatedOffset = SVB.evalMinus(Offset.castAs<NonLoc>());
6515ffd83dbSDimitry Andric     NewVal = State->getLValue(ElementType, NegatedOffset, OldVal);
652480093f4SDimitry Andric   }
653480093f4SDimitry Andric 
6545ffd83dbSDimitry Andric   // `AdvancedState` is a state where the position of `Old` is advanced. We
6555ffd83dbSDimitry Andric   // only need this state to retrieve the new position, but we do not want
6565ffd83dbSDimitry Andric   // ever to change the position of `OldVal`.
6575ffd83dbSDimitry Andric   auto AdvancedState = advancePosition(State, OldVal, OK, Offset);
6585ffd83dbSDimitry Andric   if (AdvancedState) {
6595ffd83dbSDimitry Andric     const IteratorPosition *NewPos = getIteratorPosition(AdvancedState, OldVal);
6605ffd83dbSDimitry Andric     assert(NewPos &&
6615ffd83dbSDimitry Andric            "Iterator should have position after successful advancement");
662480093f4SDimitry Andric 
6635ffd83dbSDimitry Andric     ProgramStateRef NewState = setIteratorPosition(State, NewVal, *NewPos);
6645ffd83dbSDimitry Andric     C.addTransition(NewState);
6655ffd83dbSDimitry Andric   } else {
6665ffd83dbSDimitry Andric     assignToContainer(C, Iterator, NewVal, OldPos->getContainer());
667480093f4SDimitry Andric   }
6685ffd83dbSDimitry Andric }
6695ffd83dbSDimitry Andric 
6705ffd83dbSDimitry Andric void IteratorModeling::handleAdvance(CheckerContext &C, const Expr *CE,
6715ffd83dbSDimitry Andric                                      SVal RetVal, SVal Iter,
6725ffd83dbSDimitry Andric                                      SVal Amount) const {
6735ffd83dbSDimitry Andric   handleRandomIncrOrDecr(C, CE, OO_PlusEqual, RetVal, Iter, Amount);
6745ffd83dbSDimitry Andric }
6755ffd83dbSDimitry Andric 
6765ffd83dbSDimitry Andric void IteratorModeling::handlePrev(CheckerContext &C, const Expr *CE,
6775ffd83dbSDimitry Andric                                   SVal RetVal, SVal Iter, SVal Amount) const {
6785ffd83dbSDimitry Andric   handleRandomIncrOrDecr(C, CE, OO_Minus, RetVal, Iter, Amount);
6795ffd83dbSDimitry Andric }
6805ffd83dbSDimitry Andric 
6815ffd83dbSDimitry Andric void IteratorModeling::handleNext(CheckerContext &C, const Expr *CE,
6825ffd83dbSDimitry Andric                                   SVal RetVal, SVal Iter, SVal Amount) const {
6835ffd83dbSDimitry Andric   handleRandomIncrOrDecr(C, CE, OO_Plus, RetVal, Iter, Amount);
684480093f4SDimitry Andric }
685480093f4SDimitry Andric 
686480093f4SDimitry Andric void IteratorModeling::assignToContainer(CheckerContext &C, const Expr *CE,
687480093f4SDimitry Andric                                          const SVal &RetVal,
688480093f4SDimitry Andric                                          const MemRegion *Cont) const {
689480093f4SDimitry Andric   Cont = Cont->getMostDerivedObjectRegion();
690480093f4SDimitry Andric 
691480093f4SDimitry Andric   auto State = C.getState();
6925ffd83dbSDimitry Andric   const auto *LCtx = C.getLocationContext();
6935ffd83dbSDimitry Andric   State = createIteratorPosition(State, RetVal, Cont, CE, LCtx, C.blockCount());
6945ffd83dbSDimitry Andric 
695480093f4SDimitry Andric   C.addTransition(State);
696480093f4SDimitry Andric }
697480093f4SDimitry Andric 
6985ffd83dbSDimitry Andric bool IteratorModeling::noChangeInAdvance(CheckerContext &C, SVal Iter,
6995ffd83dbSDimitry Andric                                          const Expr *CE) const {
7005ffd83dbSDimitry Andric   // Compare the iterator position before and after the call. (To be called
7015ffd83dbSDimitry Andric   // from `checkPostCall()`.)
7025ffd83dbSDimitry Andric   const auto StateAfter = C.getState();
703480093f4SDimitry Andric 
7045ffd83dbSDimitry Andric   const auto *PosAfter = getIteratorPosition(StateAfter, Iter);
7055ffd83dbSDimitry Andric   // If we have no position after the call of `std::advance`, then we are not
7065ffd83dbSDimitry Andric   // interested. (Modeling of an inlined `std::advance()` should not remove the
7075ffd83dbSDimitry Andric   // position in any case.)
7085ffd83dbSDimitry Andric   if (!PosAfter)
7095ffd83dbSDimitry Andric     return false;
710480093f4SDimitry Andric 
7115ffd83dbSDimitry Andric   const ExplodedNode *N = findCallEnter(C.getPredecessor(), CE);
7125ffd83dbSDimitry Andric   assert(N && "Any call should have a `CallEnter` node.");
713480093f4SDimitry Andric 
7145ffd83dbSDimitry Andric   const auto StateBefore = N->getState();
7155ffd83dbSDimitry Andric   const auto *PosBefore = getIteratorPosition(StateBefore, Iter);
716e8d8bef9SDimitry Andric   // FIXME: `std::advance()` should not create a new iterator position but
717e8d8bef9SDimitry Andric   //        change existing ones. However, in case of iterators implemented as
718e8d8bef9SDimitry Andric   //        pointers the handling of parameters in `std::advance()`-like
719e8d8bef9SDimitry Andric   //        functions is still incomplete which may result in cases where
720e8d8bef9SDimitry Andric   //        the new position is assigned to the wrong pointer. This causes
721e8d8bef9SDimitry Andric   //        crash if we use an assertion here.
722e8d8bef9SDimitry Andric   if (!PosBefore)
723e8d8bef9SDimitry Andric     return false;
724480093f4SDimitry Andric 
7255ffd83dbSDimitry Andric   return PosBefore->getOffset() == PosAfter->getOffset();
726480093f4SDimitry Andric }
727480093f4SDimitry Andric 
728480093f4SDimitry Andric void IteratorModeling::printState(raw_ostream &Out, ProgramStateRef State,
729480093f4SDimitry Andric                                   const char *NL, const char *Sep) const {
730480093f4SDimitry Andric   auto SymbolMap = State->get<IteratorSymbolMap>();
731480093f4SDimitry Andric   auto RegionMap = State->get<IteratorRegionMap>();
7325ffd83dbSDimitry Andric   // Use a counter to add newlines before every line except the first one.
7335ffd83dbSDimitry Andric   unsigned Count = 0;
734480093f4SDimitry Andric 
735480093f4SDimitry Andric   if (!SymbolMap.isEmpty() || !RegionMap.isEmpty()) {
736480093f4SDimitry Andric     Out << Sep << "Iterator Positions :" << NL;
737480093f4SDimitry Andric     for (const auto &Sym : SymbolMap) {
7385ffd83dbSDimitry Andric       if (Count++)
7395ffd83dbSDimitry Andric         Out << NL;
7405ffd83dbSDimitry Andric 
741480093f4SDimitry Andric       Sym.first->dumpToStream(Out);
742480093f4SDimitry Andric       Out << " : ";
743480093f4SDimitry Andric       const auto Pos = Sym.second;
744480093f4SDimitry Andric       Out << (Pos.isValid() ? "Valid" : "Invalid") << " ; Container == ";
745480093f4SDimitry Andric       Pos.getContainer()->dumpToStream(Out);
746480093f4SDimitry Andric       Out<<" ; Offset == ";
747480093f4SDimitry Andric       Pos.getOffset()->dumpToStream(Out);
748480093f4SDimitry Andric     }
749480093f4SDimitry Andric 
750480093f4SDimitry Andric     for (const auto &Reg : RegionMap) {
7515ffd83dbSDimitry Andric       if (Count++)
7525ffd83dbSDimitry Andric         Out << NL;
7535ffd83dbSDimitry Andric 
754480093f4SDimitry Andric       Reg.first->dumpToStream(Out);
755480093f4SDimitry Andric       Out << " : ";
756480093f4SDimitry Andric       const auto Pos = Reg.second;
757480093f4SDimitry Andric       Out << (Pos.isValid() ? "Valid" : "Invalid") << " ; Container == ";
758480093f4SDimitry Andric       Pos.getContainer()->dumpToStream(Out);
759480093f4SDimitry Andric       Out<<" ; Offset == ";
760480093f4SDimitry Andric       Pos.getOffset()->dumpToStream(Out);
761480093f4SDimitry Andric     }
762480093f4SDimitry Andric   }
763480093f4SDimitry Andric }
764480093f4SDimitry Andric 
765480093f4SDimitry Andric namespace {
766480093f4SDimitry Andric 
767480093f4SDimitry Andric bool isSimpleComparisonOperator(OverloadedOperatorKind OK) {
768480093f4SDimitry Andric   return OK == OO_EqualEqual || OK == OO_ExclaimEqual;
769480093f4SDimitry Andric }
770480093f4SDimitry Andric 
7715ffd83dbSDimitry Andric bool isSimpleComparisonOperator(BinaryOperatorKind OK) {
7725ffd83dbSDimitry Andric   return OK == BO_EQ || OK == BO_NE;
773480093f4SDimitry Andric }
774480093f4SDimitry Andric 
775480093f4SDimitry Andric ProgramStateRef removeIteratorPosition(ProgramStateRef State, const SVal &Val) {
776480093f4SDimitry Andric   if (auto Reg = Val.getAsRegion()) {
777480093f4SDimitry Andric     Reg = Reg->getMostDerivedObjectRegion();
778480093f4SDimitry Andric     return State->remove<IteratorRegionMap>(Reg);
779480093f4SDimitry Andric   } else if (const auto Sym = Val.getAsSymbol()) {
780480093f4SDimitry Andric     return State->remove<IteratorSymbolMap>(Sym);
781480093f4SDimitry Andric   } else if (const auto LCVal = Val.getAs<nonloc::LazyCompoundVal>()) {
782480093f4SDimitry Andric     return State->remove<IteratorRegionMap>(LCVal->getRegion());
783480093f4SDimitry Andric   }
784480093f4SDimitry Andric   return nullptr;
785480093f4SDimitry Andric }
786480093f4SDimitry Andric 
787480093f4SDimitry Andric ProgramStateRef relateSymbols(ProgramStateRef State, SymbolRef Sym1,
788480093f4SDimitry Andric                               SymbolRef Sym2, bool Equal) {
789480093f4SDimitry Andric   auto &SVB = State->getStateManager().getSValBuilder();
790480093f4SDimitry Andric 
791480093f4SDimitry Andric   // FIXME: This code should be reworked as follows:
792480093f4SDimitry Andric   // 1. Subtract the operands using evalBinOp().
793480093f4SDimitry Andric   // 2. Assume that the result doesn't overflow.
794480093f4SDimitry Andric   // 3. Compare the result to 0.
795480093f4SDimitry Andric   // 4. Assume the result of the comparison.
796480093f4SDimitry Andric   const auto comparison =
797480093f4SDimitry Andric     SVB.evalBinOp(State, BO_EQ, nonloc::SymbolVal(Sym1),
798480093f4SDimitry Andric                   nonloc::SymbolVal(Sym2), SVB.getConditionType());
799480093f4SDimitry Andric 
80081ad6265SDimitry Andric   assert(isa<DefinedSVal>(comparison) &&
801480093f4SDimitry Andric          "Symbol comparison must be a `DefinedSVal`");
802480093f4SDimitry Andric 
803480093f4SDimitry Andric   auto NewState = State->assume(comparison.castAs<DefinedSVal>(), Equal);
804480093f4SDimitry Andric   if (!NewState)
805480093f4SDimitry Andric     return nullptr;
806480093f4SDimitry Andric 
807480093f4SDimitry Andric   if (const auto CompSym = comparison.getAsSymbol()) {
808480093f4SDimitry Andric     assert(isa<SymIntExpr>(CompSym) &&
809480093f4SDimitry Andric            "Symbol comparison must be a `SymIntExpr`");
810480093f4SDimitry Andric     assert(BinaryOperator::isComparisonOp(
811480093f4SDimitry Andric                cast<SymIntExpr>(CompSym)->getOpcode()) &&
812480093f4SDimitry Andric            "Symbol comparison must be a comparison");
813480093f4SDimitry Andric     return assumeNoOverflow(NewState, cast<SymIntExpr>(CompSym)->getLHS(), 2);
814480093f4SDimitry Andric   }
815480093f4SDimitry Andric 
816480093f4SDimitry Andric   return NewState;
817480093f4SDimitry Andric }
818480093f4SDimitry Andric 
819480093f4SDimitry Andric bool isBoundThroughLazyCompoundVal(const Environment &Env,
820480093f4SDimitry Andric                                    const MemRegion *Reg) {
821480093f4SDimitry Andric   for (const auto &Binding : Env) {
822480093f4SDimitry Andric     if (const auto LCVal = Binding.second.getAs<nonloc::LazyCompoundVal>()) {
823480093f4SDimitry Andric       if (LCVal->getRegion() == Reg)
824480093f4SDimitry Andric         return true;
825480093f4SDimitry Andric     }
826480093f4SDimitry Andric   }
827480093f4SDimitry Andric 
828480093f4SDimitry Andric   return false;
829480093f4SDimitry Andric }
830480093f4SDimitry Andric 
8315ffd83dbSDimitry Andric const ExplodedNode *findCallEnter(const ExplodedNode *Node, const Expr *Call) {
8325ffd83dbSDimitry Andric   while (Node) {
8335ffd83dbSDimitry Andric     ProgramPoint PP = Node->getLocation();
8345ffd83dbSDimitry Andric     if (auto Enter = PP.getAs<CallEnter>()) {
8355ffd83dbSDimitry Andric       if (Enter->getCallExpr() == Call)
8365ffd83dbSDimitry Andric         break;
837480093f4SDimitry Andric     }
838480093f4SDimitry Andric 
8395ffd83dbSDimitry Andric     Node = Node->getFirstPred();
840480093f4SDimitry Andric   }
841480093f4SDimitry Andric 
8425ffd83dbSDimitry Andric   return Node;
843480093f4SDimitry Andric }
844480093f4SDimitry Andric 
845480093f4SDimitry Andric } // namespace
846480093f4SDimitry Andric 
847480093f4SDimitry Andric void ento::registerIteratorModeling(CheckerManager &mgr) {
848480093f4SDimitry Andric   mgr.registerChecker<IteratorModeling>();
849480093f4SDimitry Andric }
850480093f4SDimitry Andric 
8515ffd83dbSDimitry Andric bool ento::shouldRegisterIteratorModeling(const CheckerManager &mgr) {
852480093f4SDimitry Andric   return true;
853480093f4SDimitry Andric }
854