xref: /freebsd/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/IteratorModeling.cpp (revision 0fca6ea1d4eea4c934cfff25ac9ee8ad6fe95583)
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"
7506c3fb27SDimitry 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,
103647cbc5dSDimitry Andric                         SVal LVal, SVal RVal, OverloadedOperatorKind Op) const;
104480093f4SDimitry Andric   void processComparison(CheckerContext &C, ProgramStateRef State,
105647cbc5dSDimitry Andric                          SymbolRef Sym1, SymbolRef Sym2, SVal RetVal,
106480093f4SDimitry Andric                          OverloadedOperatorKind Op) const;
107647cbc5dSDimitry Andric   void handleIncrement(CheckerContext &C, SVal RetVal, SVal Iter,
108480093f4SDimitry Andric                        bool Postfix) const;
109647cbc5dSDimitry Andric   void handleDecrement(CheckerContext &C, SVal RetVal, SVal Iter,
110480093f4SDimitry Andric                        bool Postfix) const;
111480093f4SDimitry Andric   void handleRandomIncrOrDecr(CheckerContext &C, const Expr *CE,
112647cbc5dSDimitry Andric                               OverloadedOperatorKind Op, SVal RetVal,
113647cbc5dSDimitry Andric                               SVal Iterator, SVal Amount) const;
1145ffd83dbSDimitry Andric   void handlePtrIncrOrDecr(CheckerContext &C, const Expr *Iterator,
1155ffd83dbSDimitry Andric                            OverloadedOperatorKind OK, SVal Offset) const;
1165ffd83dbSDimitry Andric   void handleAdvance(CheckerContext &C, const Expr *CE, SVal RetVal, SVal Iter,
1175ffd83dbSDimitry Andric                      SVal Amount) const;
1185ffd83dbSDimitry Andric   void handlePrev(CheckerContext &C, const Expr *CE, SVal RetVal, SVal Iter,
1195ffd83dbSDimitry Andric                   SVal Amount) const;
1205ffd83dbSDimitry Andric   void handleNext(CheckerContext &C, const Expr *CE, SVal RetVal, SVal Iter,
1215ffd83dbSDimitry Andric                   SVal Amount) const;
122647cbc5dSDimitry Andric   void assignToContainer(CheckerContext &C, const Expr *CE, SVal RetVal,
123480093f4SDimitry Andric                          const MemRegion *Cont) const;
1245ffd83dbSDimitry Andric   bool noChangeInAdvance(CheckerContext &C, SVal Iter, const Expr *CE) const;
125480093f4SDimitry Andric   void printState(raw_ostream &Out, ProgramStateRef State, const char *NL,
126480093f4SDimitry Andric                   const char *Sep) const override;
127480093f4SDimitry Andric 
1285ffd83dbSDimitry Andric   // std::advance, std::prev & std::next
1295ffd83dbSDimitry Andric   CallDescriptionMap<AdvanceFn> AdvanceLikeFunctions = {
1305ffd83dbSDimitry Andric       // template<class InputIt, class Distance>
1315ffd83dbSDimitry Andric       // void advance(InputIt& it, Distance n);
132*0fca6ea1SDimitry Andric       {{CDM::SimpleFunc, {"std", "advance"}, 2},
133*0fca6ea1SDimitry Andric        &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);
139*0fca6ea1SDimitry Andric       {{CDM::SimpleFunc, {"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);
145*0fca6ea1SDimitry Andric       {{CDM::SimpleFunc, {"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);
163647cbc5dSDimitry Andric ProgramStateRef removeIteratorPosition(ProgramStateRef State, 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 
checkPostCall(const CallEvent & Call,CheckerContext & C) const172480093f4SDimitry 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 
checkBind(SVal Loc,SVal Val,const Stmt * S,CheckerContext & C) const235480093f4SDimitry 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 
checkPostStmt(const UnaryOperator * UO,CheckerContext & C) const2515ffd83dbSDimitry 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 
checkPostStmt(const BinaryOperator * BO,CheckerContext & C) const2635ffd83dbSDimitry 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;
286647cbc5dSDimitry Andric     SVal AmountVal = IsIterOnLHS ? RVal : LVal;
287e8d8bef9SDimitry Andric     handlePtrIncrOrDecr(C, IterExpr, BinaryOperator::getOverloadedOperator(OK),
288e8d8bef9SDimitry Andric                         AmountVal);
2895ffd83dbSDimitry Andric   }
2905ffd83dbSDimitry Andric }
2915ffd83dbSDimitry Andric 
checkPostStmt(const MaterializeTemporaryExpr * MTE,CheckerContext & C) const292480093f4SDimitry 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 
checkLiveSymbols(ProgramStateRef State,SymbolReaper & SR) const303480093f4SDimitry 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>();
30706c3fb27SDimitry Andric   for (const IteratorPosition &Pos : llvm::make_second_range(RegionMap)) {
30806c3fb27SDimitry Andric     for (SymbolRef Sym : Pos.getOffset()->symbols())
30906c3fb27SDimitry Andric       if (isa<SymbolData>(Sym))
31006c3fb27SDimitry Andric         SR.markLive(Sym);
311480093f4SDimitry Andric   }
312480093f4SDimitry Andric 
313480093f4SDimitry Andric   auto SymbolMap = State->get<IteratorSymbolMap>();
31406c3fb27SDimitry Andric   for (const IteratorPosition &Pos : llvm::make_second_range(SymbolMap)) {
31506c3fb27SDimitry Andric     for (SymbolRef Sym : Pos.getOffset()->symbols())
31606c3fb27SDimitry Andric       if (isa<SymbolData>(Sym))
31706c3fb27SDimitry Andric         SR.markLive(Sym);
318480093f4SDimitry Andric   }
319480093f4SDimitry Andric }
320480093f4SDimitry Andric 
checkDeadSymbols(SymbolReaper & SR,CheckerContext & C) const321480093f4SDimitry 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
handleOverloadedOperator(CheckerContext & C,const CallEvent & Call,OverloadedOperatorKind Op) const3495ffd83dbSDimitry 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);
391647cbc5dSDimitry Andric           SVal Iterator = IsIterFirst ? FirstArg : SecondArg;
392647cbc5dSDimitry Andric           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
handleAdvanceLikeFunction(CheckerContext & C,const CallEvent & Call,const Expr * OrigExpr,const AdvanceFn * Handler) const4235ffd83dbSDimitry 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 
handleComparison(CheckerContext & C,const Expr * CE,SVal RetVal,SVal LVal,SVal RVal,OverloadedOperatorKind Op) const446480093f4SDimitry Andric void IteratorModeling::handleComparison(CheckerContext &C, const Expr *CE,
447647cbc5dSDimitry Andric                                         SVal RetVal, SVal LVal, SVal RVal,
448480093f4SDimitry Andric                                         OverloadedOperatorKind Op) const {
449480093f4SDimitry Andric   // Record the operands and the operator of the comparison for the next
450480093f4SDimitry Andric   // evalAssume, if the result is a symbolic expression. If it is a concrete
451480093f4SDimitry Andric   // value (only one branch is possible), then transfer the state between
452480093f4SDimitry Andric   // the operands according to the operator and the result
453480093f4SDimitry Andric   auto State = C.getState();
454480093f4SDimitry Andric   const auto *LPos = getIteratorPosition(State, LVal);
455480093f4SDimitry Andric   const auto *RPos = getIteratorPosition(State, RVal);
456480093f4SDimitry Andric   const MemRegion *Cont = nullptr;
457480093f4SDimitry Andric   if (LPos) {
458480093f4SDimitry Andric     Cont = LPos->getContainer();
459480093f4SDimitry Andric   } else if (RPos) {
460480093f4SDimitry Andric     Cont = RPos->getContainer();
461480093f4SDimitry Andric   }
462480093f4SDimitry Andric   if (!Cont)
463480093f4SDimitry Andric     return;
464480093f4SDimitry Andric 
4655ffd83dbSDimitry Andric   // At least one of the iterators has recorded positions. If one of them does
466480093f4SDimitry Andric   // not then create a new symbol for the offset.
467480093f4SDimitry Andric   SymbolRef Sym;
468480093f4SDimitry Andric   if (!LPos || !RPos) {
469480093f4SDimitry Andric     auto &SymMgr = C.getSymbolManager();
470480093f4SDimitry Andric     Sym = SymMgr.conjureSymbol(CE, C.getLocationContext(),
471480093f4SDimitry Andric                                C.getASTContext().LongTy, C.blockCount());
472480093f4SDimitry Andric     State = assumeNoOverflow(State, Sym, 4);
473480093f4SDimitry Andric   }
474480093f4SDimitry Andric 
475480093f4SDimitry Andric   if (!LPos) {
476480093f4SDimitry Andric     State = setIteratorPosition(State, LVal,
477480093f4SDimitry Andric                                 IteratorPosition::getPosition(Cont, Sym));
478480093f4SDimitry Andric     LPos = getIteratorPosition(State, LVal);
479480093f4SDimitry Andric   } else if (!RPos) {
480480093f4SDimitry Andric     State = setIteratorPosition(State, RVal,
481480093f4SDimitry Andric                                 IteratorPosition::getPosition(Cont, Sym));
482480093f4SDimitry Andric     RPos = getIteratorPosition(State, RVal);
483480093f4SDimitry Andric   }
484480093f4SDimitry Andric 
485e8d8bef9SDimitry Andric   // If the value for which we just tried to set a new iterator position is
486e8d8bef9SDimitry Andric   // an `SVal`for which no iterator position can be set then the setting was
487e8d8bef9SDimitry Andric   // unsuccessful. We cannot handle the comparison in this case.
488e8d8bef9SDimitry Andric   if (!LPos || !RPos)
489e8d8bef9SDimitry Andric     return;
490e8d8bef9SDimitry Andric 
4915ffd83dbSDimitry Andric   // We cannot make assumptions on `UnknownVal`. Let us conjure a symbol
492480093f4SDimitry Andric   // instead.
493480093f4SDimitry Andric   if (RetVal.isUnknown()) {
494480093f4SDimitry Andric     auto &SymMgr = C.getSymbolManager();
495480093f4SDimitry Andric     auto *LCtx = C.getLocationContext();
496480093f4SDimitry Andric     RetVal = nonloc::SymbolVal(SymMgr.conjureSymbol(
497480093f4SDimitry Andric         CE, LCtx, C.getASTContext().BoolTy, C.blockCount()));
498480093f4SDimitry Andric     State = State->BindExpr(CE, LCtx, RetVal);
499480093f4SDimitry Andric   }
500480093f4SDimitry Andric 
501480093f4SDimitry Andric   processComparison(C, State, LPos->getOffset(), RPos->getOffset(), RetVal, Op);
502480093f4SDimitry Andric }
503480093f4SDimitry Andric 
processComparison(CheckerContext & C,ProgramStateRef State,SymbolRef Sym1,SymbolRef Sym2,SVal RetVal,OverloadedOperatorKind Op) const504480093f4SDimitry Andric void IteratorModeling::processComparison(CheckerContext &C,
505480093f4SDimitry Andric                                          ProgramStateRef State, SymbolRef Sym1,
506647cbc5dSDimitry Andric                                          SymbolRef Sym2, SVal RetVal,
507480093f4SDimitry Andric                                          OverloadedOperatorKind Op) const {
508480093f4SDimitry Andric   if (const auto TruthVal = RetVal.getAs<nonloc::ConcreteInt>()) {
509480093f4SDimitry Andric     if ((State = relateSymbols(State, Sym1, Sym2,
510480093f4SDimitry Andric                               (Op == OO_EqualEqual) ==
511480093f4SDimitry Andric                                (TruthVal->getValue() != 0)))) {
512480093f4SDimitry Andric       C.addTransition(State);
513480093f4SDimitry Andric     } else {
514480093f4SDimitry Andric       C.generateSink(State, C.getPredecessor());
515480093f4SDimitry Andric     }
516480093f4SDimitry Andric     return;
517480093f4SDimitry Andric   }
518480093f4SDimitry Andric 
519480093f4SDimitry Andric   const auto ConditionVal = RetVal.getAs<DefinedSVal>();
520480093f4SDimitry Andric   if (!ConditionVal)
521480093f4SDimitry Andric     return;
522480093f4SDimitry Andric 
523480093f4SDimitry Andric   if (auto StateTrue = relateSymbols(State, Sym1, Sym2, Op == OO_EqualEqual)) {
524480093f4SDimitry Andric     StateTrue = StateTrue->assume(*ConditionVal, true);
525480093f4SDimitry Andric     C.addTransition(StateTrue);
526480093f4SDimitry Andric   }
527480093f4SDimitry Andric 
528480093f4SDimitry Andric   if (auto StateFalse = relateSymbols(State, Sym1, Sym2, Op != OO_EqualEqual)) {
529480093f4SDimitry Andric     StateFalse = StateFalse->assume(*ConditionVal, false);
530480093f4SDimitry Andric     C.addTransition(StateFalse);
531480093f4SDimitry Andric   }
532480093f4SDimitry Andric }
533480093f4SDimitry Andric 
handleIncrement(CheckerContext & C,SVal RetVal,SVal Iter,bool Postfix) const534647cbc5dSDimitry Andric void IteratorModeling::handleIncrement(CheckerContext &C, SVal RetVal,
535647cbc5dSDimitry Andric                                        SVal Iter, bool Postfix) const {
536480093f4SDimitry Andric   // Increment the symbolic expressions which represents the position of the
537480093f4SDimitry Andric   // iterator
538480093f4SDimitry Andric   auto State = C.getState();
539480093f4SDimitry Andric   auto &BVF = C.getSymbolManager().getBasicVals();
540480093f4SDimitry Andric 
541480093f4SDimitry Andric   const auto *Pos = getIteratorPosition(State, Iter);
542480093f4SDimitry Andric   if (!Pos)
543480093f4SDimitry Andric     return;
544480093f4SDimitry Andric 
545480093f4SDimitry Andric   auto NewState =
546480093f4SDimitry Andric     advancePosition(State, Iter, OO_Plus,
547480093f4SDimitry Andric                     nonloc::ConcreteInt(BVF.getValue(llvm::APSInt::get(1))));
548480093f4SDimitry Andric   assert(NewState &&
549480093f4SDimitry Andric          "Advancing position by concrete int should always be successful");
550480093f4SDimitry Andric 
551480093f4SDimitry Andric   const auto *NewPos = getIteratorPosition(NewState, Iter);
552480093f4SDimitry Andric   assert(NewPos &&
553480093f4SDimitry Andric          "Iterator should have position after successful advancement");
554480093f4SDimitry Andric 
555480093f4SDimitry Andric   State = setIteratorPosition(State, Iter, *NewPos);
556480093f4SDimitry Andric   State = setIteratorPosition(State, RetVal, Postfix ? *Pos : *NewPos);
557480093f4SDimitry Andric   C.addTransition(State);
558480093f4SDimitry Andric }
559480093f4SDimitry Andric 
handleDecrement(CheckerContext & C,SVal RetVal,SVal Iter,bool Postfix) const560647cbc5dSDimitry Andric void IteratorModeling::handleDecrement(CheckerContext &C, SVal RetVal,
561647cbc5dSDimitry Andric                                        SVal Iter, bool Postfix) const {
562480093f4SDimitry Andric   // Decrement the symbolic expressions which represents the position of the
563480093f4SDimitry Andric   // iterator
564480093f4SDimitry Andric   auto State = C.getState();
565480093f4SDimitry Andric   auto &BVF = C.getSymbolManager().getBasicVals();
566480093f4SDimitry Andric 
567480093f4SDimitry Andric   const auto *Pos = getIteratorPosition(State, Iter);
568480093f4SDimitry Andric   if (!Pos)
569480093f4SDimitry Andric     return;
570480093f4SDimitry Andric 
571480093f4SDimitry Andric   auto NewState =
572480093f4SDimitry Andric     advancePosition(State, Iter, OO_Minus,
573480093f4SDimitry Andric                     nonloc::ConcreteInt(BVF.getValue(llvm::APSInt::get(1))));
574480093f4SDimitry Andric   assert(NewState &&
575480093f4SDimitry Andric          "Advancing position by concrete int should always be successful");
576480093f4SDimitry Andric 
577480093f4SDimitry Andric   const auto *NewPos = getIteratorPosition(NewState, Iter);
578480093f4SDimitry Andric   assert(NewPos &&
579480093f4SDimitry Andric          "Iterator should have position after successful advancement");
580480093f4SDimitry Andric 
581480093f4SDimitry Andric   State = setIteratorPosition(State, Iter, *NewPos);
582480093f4SDimitry Andric   State = setIteratorPosition(State, RetVal, Postfix ? *Pos : *NewPos);
583480093f4SDimitry Andric   C.addTransition(State);
584480093f4SDimitry Andric }
585480093f4SDimitry Andric 
handleRandomIncrOrDecr(CheckerContext & C,const Expr * CE,OverloadedOperatorKind Op,SVal RetVal,SVal Iterator,SVal Amount) const586e8d8bef9SDimitry Andric void IteratorModeling::handleRandomIncrOrDecr(CheckerContext &C, const Expr *CE,
587480093f4SDimitry Andric                                               OverloadedOperatorKind Op,
588647cbc5dSDimitry Andric                                               SVal RetVal, SVal Iterator,
589647cbc5dSDimitry Andric                                               SVal Amount) const {
590480093f4SDimitry Andric   // Increment or decrement the symbolic expressions which represents the
591480093f4SDimitry Andric   // position of the iterator
592480093f4SDimitry Andric   auto State = C.getState();
593480093f4SDimitry Andric 
594e8d8bef9SDimitry Andric   const auto *Pos = getIteratorPosition(State, Iterator);
595480093f4SDimitry Andric   if (!Pos)
596480093f4SDimitry Andric     return;
597480093f4SDimitry Andric 
598e8d8bef9SDimitry Andric   const auto *Value = &Amount;
599e8d8bef9SDimitry Andric   SVal Val;
600e8d8bef9SDimitry Andric   if (auto LocAmount = Amount.getAs<Loc>()) {
601e8d8bef9SDimitry Andric     Val = State->getRawSVal(*LocAmount);
602e8d8bef9SDimitry Andric     Value = &Val;
603480093f4SDimitry Andric   }
604480093f4SDimitry Andric 
605e8d8bef9SDimitry Andric   const auto &TgtVal =
606e8d8bef9SDimitry Andric       (Op == OO_PlusEqual || Op == OO_MinusEqual) ? Iterator : RetVal;
607480093f4SDimitry Andric 
6085ffd83dbSDimitry Andric   // `AdvancedState` is a state where the position of `LHS` is advanced. We
6095ffd83dbSDimitry Andric   // only need this state to retrieve the new position, but we do not want
6105ffd83dbSDimitry Andric   // to change the position of `LHS` (in every case).
611e8d8bef9SDimitry Andric   auto AdvancedState = advancePosition(State, Iterator, Op, *Value);
6125ffd83dbSDimitry Andric   if (AdvancedState) {
613e8d8bef9SDimitry Andric     const auto *NewPos = getIteratorPosition(AdvancedState, Iterator);
614480093f4SDimitry Andric     assert(NewPos &&
615480093f4SDimitry Andric            "Iterator should have position after successful advancement");
616480093f4SDimitry Andric 
6175ffd83dbSDimitry Andric     State = setIteratorPosition(State, TgtVal, *NewPos);
618480093f4SDimitry Andric     C.addTransition(State);
619480093f4SDimitry Andric   } else {
620480093f4SDimitry Andric     assignToContainer(C, CE, TgtVal, Pos->getContainer());
621480093f4SDimitry Andric   }
622480093f4SDimitry Andric }
623480093f4SDimitry Andric 
handlePtrIncrOrDecr(CheckerContext & C,const Expr * Iterator,OverloadedOperatorKind OK,SVal Offset) const6245ffd83dbSDimitry Andric void IteratorModeling::handlePtrIncrOrDecr(CheckerContext &C,
6255ffd83dbSDimitry Andric                                            const Expr *Iterator,
6265ffd83dbSDimitry Andric                                            OverloadedOperatorKind OK,
6275ffd83dbSDimitry Andric                                            SVal Offset) const {
62881ad6265SDimitry Andric   if (!isa<DefinedSVal>(Offset))
629e8d8bef9SDimitry Andric     return;
630e8d8bef9SDimitry Andric 
6315ffd83dbSDimitry Andric   QualType PtrType = Iterator->getType();
6325ffd83dbSDimitry Andric   if (!PtrType->isPointerType())
6335ffd83dbSDimitry Andric     return;
6345ffd83dbSDimitry Andric   QualType ElementType = PtrType->getPointeeType();
6355ffd83dbSDimitry Andric 
6365ffd83dbSDimitry Andric   ProgramStateRef State = C.getState();
6375ffd83dbSDimitry Andric   SVal OldVal = State->getSVal(Iterator, C.getLocationContext());
6385ffd83dbSDimitry Andric 
6395ffd83dbSDimitry Andric   const IteratorPosition *OldPos = getIteratorPosition(State, OldVal);
6405ffd83dbSDimitry Andric   if (!OldPos)
641480093f4SDimitry Andric     return;
642480093f4SDimitry Andric 
6435ffd83dbSDimitry Andric   SVal NewVal;
644e8d8bef9SDimitry Andric   if (OK == OO_Plus || OK == OO_PlusEqual) {
6455ffd83dbSDimitry Andric     NewVal = State->getLValue(ElementType, Offset, OldVal);
646e8d8bef9SDimitry Andric   } else {
647e8d8bef9SDimitry Andric     auto &SVB = C.getSValBuilder();
648e8d8bef9SDimitry Andric     SVal NegatedOffset = SVB.evalMinus(Offset.castAs<NonLoc>());
6495ffd83dbSDimitry Andric     NewVal = State->getLValue(ElementType, NegatedOffset, OldVal);
650480093f4SDimitry Andric   }
651480093f4SDimitry Andric 
6525ffd83dbSDimitry Andric   // `AdvancedState` is a state where the position of `Old` is advanced. We
6535ffd83dbSDimitry Andric   // only need this state to retrieve the new position, but we do not want
6545ffd83dbSDimitry Andric   // ever to change the position of `OldVal`.
6555ffd83dbSDimitry Andric   auto AdvancedState = advancePosition(State, OldVal, OK, Offset);
6565ffd83dbSDimitry Andric   if (AdvancedState) {
6575ffd83dbSDimitry Andric     const IteratorPosition *NewPos = getIteratorPosition(AdvancedState, OldVal);
6585ffd83dbSDimitry Andric     assert(NewPos &&
6595ffd83dbSDimitry Andric            "Iterator should have position after successful advancement");
660480093f4SDimitry Andric 
6615ffd83dbSDimitry Andric     ProgramStateRef NewState = setIteratorPosition(State, NewVal, *NewPos);
6625ffd83dbSDimitry Andric     C.addTransition(NewState);
6635ffd83dbSDimitry Andric   } else {
6645ffd83dbSDimitry Andric     assignToContainer(C, Iterator, NewVal, OldPos->getContainer());
665480093f4SDimitry Andric   }
6665ffd83dbSDimitry Andric }
6675ffd83dbSDimitry Andric 
handleAdvance(CheckerContext & C,const Expr * CE,SVal RetVal,SVal Iter,SVal Amount) const6685ffd83dbSDimitry Andric void IteratorModeling::handleAdvance(CheckerContext &C, const Expr *CE,
6695ffd83dbSDimitry Andric                                      SVal RetVal, SVal Iter,
6705ffd83dbSDimitry Andric                                      SVal Amount) const {
6715ffd83dbSDimitry Andric   handleRandomIncrOrDecr(C, CE, OO_PlusEqual, RetVal, Iter, Amount);
6725ffd83dbSDimitry Andric }
6735ffd83dbSDimitry Andric 
handlePrev(CheckerContext & C,const Expr * CE,SVal RetVal,SVal Iter,SVal Amount) const6745ffd83dbSDimitry Andric void IteratorModeling::handlePrev(CheckerContext &C, const Expr *CE,
6755ffd83dbSDimitry Andric                                   SVal RetVal, SVal Iter, SVal Amount) const {
6765ffd83dbSDimitry Andric   handleRandomIncrOrDecr(C, CE, OO_Minus, RetVal, Iter, Amount);
6775ffd83dbSDimitry Andric }
6785ffd83dbSDimitry Andric 
handleNext(CheckerContext & C,const Expr * CE,SVal RetVal,SVal Iter,SVal Amount) const6795ffd83dbSDimitry Andric void IteratorModeling::handleNext(CheckerContext &C, const Expr *CE,
6805ffd83dbSDimitry Andric                                   SVal RetVal, SVal Iter, SVal Amount) const {
6815ffd83dbSDimitry Andric   handleRandomIncrOrDecr(C, CE, OO_Plus, RetVal, Iter, Amount);
682480093f4SDimitry Andric }
683480093f4SDimitry Andric 
assignToContainer(CheckerContext & C,const Expr * CE,SVal RetVal,const MemRegion * Cont) const684480093f4SDimitry Andric void IteratorModeling::assignToContainer(CheckerContext &C, const Expr *CE,
685647cbc5dSDimitry Andric                                          SVal RetVal,
686480093f4SDimitry Andric                                          const MemRegion *Cont) const {
687480093f4SDimitry Andric   Cont = Cont->getMostDerivedObjectRegion();
688480093f4SDimitry Andric 
689480093f4SDimitry Andric   auto State = C.getState();
6905ffd83dbSDimitry Andric   const auto *LCtx = C.getLocationContext();
6915ffd83dbSDimitry Andric   State = createIteratorPosition(State, RetVal, Cont, CE, LCtx, C.blockCount());
6925ffd83dbSDimitry Andric 
693480093f4SDimitry Andric   C.addTransition(State);
694480093f4SDimitry Andric }
695480093f4SDimitry Andric 
noChangeInAdvance(CheckerContext & C,SVal Iter,const Expr * CE) const6965ffd83dbSDimitry Andric bool IteratorModeling::noChangeInAdvance(CheckerContext &C, SVal Iter,
6975ffd83dbSDimitry Andric                                          const Expr *CE) const {
6985ffd83dbSDimitry Andric   // Compare the iterator position before and after the call. (To be called
6995ffd83dbSDimitry Andric   // from `checkPostCall()`.)
7005ffd83dbSDimitry Andric   const auto StateAfter = C.getState();
701480093f4SDimitry Andric 
7025ffd83dbSDimitry Andric   const auto *PosAfter = getIteratorPosition(StateAfter, Iter);
7035ffd83dbSDimitry Andric   // If we have no position after the call of `std::advance`, then we are not
7045ffd83dbSDimitry Andric   // interested. (Modeling of an inlined `std::advance()` should not remove the
7055ffd83dbSDimitry Andric   // position in any case.)
7065ffd83dbSDimitry Andric   if (!PosAfter)
7075ffd83dbSDimitry Andric     return false;
708480093f4SDimitry Andric 
7095ffd83dbSDimitry Andric   const ExplodedNode *N = findCallEnter(C.getPredecessor(), CE);
7105ffd83dbSDimitry Andric   assert(N && "Any call should have a `CallEnter` node.");
711480093f4SDimitry Andric 
7125ffd83dbSDimitry Andric   const auto StateBefore = N->getState();
7135ffd83dbSDimitry Andric   const auto *PosBefore = getIteratorPosition(StateBefore, Iter);
714e8d8bef9SDimitry Andric   // FIXME: `std::advance()` should not create a new iterator position but
715e8d8bef9SDimitry Andric   //        change existing ones. However, in case of iterators implemented as
716e8d8bef9SDimitry Andric   //        pointers the handling of parameters in `std::advance()`-like
717e8d8bef9SDimitry Andric   //        functions is still incomplete which may result in cases where
718e8d8bef9SDimitry Andric   //        the new position is assigned to the wrong pointer. This causes
719e8d8bef9SDimitry Andric   //        crash if we use an assertion here.
720e8d8bef9SDimitry Andric   if (!PosBefore)
721e8d8bef9SDimitry Andric     return false;
722480093f4SDimitry Andric 
7235ffd83dbSDimitry Andric   return PosBefore->getOffset() == PosAfter->getOffset();
724480093f4SDimitry Andric }
725480093f4SDimitry Andric 
printState(raw_ostream & Out,ProgramStateRef State,const char * NL,const char * Sep) const726480093f4SDimitry Andric void IteratorModeling::printState(raw_ostream &Out, ProgramStateRef State,
727480093f4SDimitry Andric                                   const char *NL, const char *Sep) const {
728480093f4SDimitry Andric   auto SymbolMap = State->get<IteratorSymbolMap>();
729480093f4SDimitry Andric   auto RegionMap = State->get<IteratorRegionMap>();
7305ffd83dbSDimitry Andric   // Use a counter to add newlines before every line except the first one.
7315ffd83dbSDimitry Andric   unsigned Count = 0;
732480093f4SDimitry Andric 
733480093f4SDimitry Andric   if (!SymbolMap.isEmpty() || !RegionMap.isEmpty()) {
734480093f4SDimitry Andric     Out << Sep << "Iterator Positions :" << NL;
735480093f4SDimitry Andric     for (const auto &Sym : SymbolMap) {
7365ffd83dbSDimitry Andric       if (Count++)
7375ffd83dbSDimitry Andric         Out << NL;
7385ffd83dbSDimitry Andric 
739480093f4SDimitry Andric       Sym.first->dumpToStream(Out);
740480093f4SDimitry Andric       Out << " : ";
741480093f4SDimitry Andric       const auto Pos = Sym.second;
742480093f4SDimitry Andric       Out << (Pos.isValid() ? "Valid" : "Invalid") << " ; Container == ";
743480093f4SDimitry Andric       Pos.getContainer()->dumpToStream(Out);
744480093f4SDimitry Andric       Out<<" ; Offset == ";
745480093f4SDimitry Andric       Pos.getOffset()->dumpToStream(Out);
746480093f4SDimitry Andric     }
747480093f4SDimitry Andric 
748480093f4SDimitry Andric     for (const auto &Reg : RegionMap) {
7495ffd83dbSDimitry Andric       if (Count++)
7505ffd83dbSDimitry Andric         Out << NL;
7515ffd83dbSDimitry Andric 
752480093f4SDimitry Andric       Reg.first->dumpToStream(Out);
753480093f4SDimitry Andric       Out << " : ";
754480093f4SDimitry Andric       const auto Pos = Reg.second;
755480093f4SDimitry Andric       Out << (Pos.isValid() ? "Valid" : "Invalid") << " ; Container == ";
756480093f4SDimitry Andric       Pos.getContainer()->dumpToStream(Out);
757480093f4SDimitry Andric       Out<<" ; Offset == ";
758480093f4SDimitry Andric       Pos.getOffset()->dumpToStream(Out);
759480093f4SDimitry Andric     }
760480093f4SDimitry Andric   }
761480093f4SDimitry Andric }
762480093f4SDimitry Andric 
763480093f4SDimitry Andric namespace {
764480093f4SDimitry Andric 
isSimpleComparisonOperator(OverloadedOperatorKind OK)765480093f4SDimitry Andric bool isSimpleComparisonOperator(OverloadedOperatorKind OK) {
766480093f4SDimitry Andric   return OK == OO_EqualEqual || OK == OO_ExclaimEqual;
767480093f4SDimitry Andric }
768480093f4SDimitry Andric 
isSimpleComparisonOperator(BinaryOperatorKind OK)7695ffd83dbSDimitry Andric bool isSimpleComparisonOperator(BinaryOperatorKind OK) {
7705ffd83dbSDimitry Andric   return OK == BO_EQ || OK == BO_NE;
771480093f4SDimitry Andric }
772480093f4SDimitry Andric 
removeIteratorPosition(ProgramStateRef State,SVal Val)773647cbc5dSDimitry Andric ProgramStateRef removeIteratorPosition(ProgramStateRef State, SVal Val) {
774480093f4SDimitry Andric   if (auto Reg = Val.getAsRegion()) {
775480093f4SDimitry Andric     Reg = Reg->getMostDerivedObjectRegion();
776480093f4SDimitry Andric     return State->remove<IteratorRegionMap>(Reg);
777480093f4SDimitry Andric   } else if (const auto Sym = Val.getAsSymbol()) {
778480093f4SDimitry Andric     return State->remove<IteratorSymbolMap>(Sym);
779480093f4SDimitry Andric   } else if (const auto LCVal = Val.getAs<nonloc::LazyCompoundVal>()) {
780480093f4SDimitry Andric     return State->remove<IteratorRegionMap>(LCVal->getRegion());
781480093f4SDimitry Andric   }
782480093f4SDimitry Andric   return nullptr;
783480093f4SDimitry Andric }
784480093f4SDimitry Andric 
relateSymbols(ProgramStateRef State,SymbolRef Sym1,SymbolRef Sym2,bool Equal)785480093f4SDimitry Andric ProgramStateRef relateSymbols(ProgramStateRef State, SymbolRef Sym1,
786480093f4SDimitry Andric                               SymbolRef Sym2, bool Equal) {
787480093f4SDimitry Andric   auto &SVB = State->getStateManager().getSValBuilder();
788480093f4SDimitry Andric 
789480093f4SDimitry Andric   // FIXME: This code should be reworked as follows:
790480093f4SDimitry Andric   // 1. Subtract the operands using evalBinOp().
791480093f4SDimitry Andric   // 2. Assume that the result doesn't overflow.
792480093f4SDimitry Andric   // 3. Compare the result to 0.
793480093f4SDimitry Andric   // 4. Assume the result of the comparison.
794480093f4SDimitry Andric   const auto comparison =
795480093f4SDimitry Andric     SVB.evalBinOp(State, BO_EQ, nonloc::SymbolVal(Sym1),
796480093f4SDimitry Andric                   nonloc::SymbolVal(Sym2), SVB.getConditionType());
797480093f4SDimitry Andric 
79881ad6265SDimitry Andric   assert(isa<DefinedSVal>(comparison) &&
799480093f4SDimitry Andric          "Symbol comparison must be a `DefinedSVal`");
800480093f4SDimitry Andric 
801480093f4SDimitry Andric   auto NewState = State->assume(comparison.castAs<DefinedSVal>(), Equal);
802480093f4SDimitry Andric   if (!NewState)
803480093f4SDimitry Andric     return nullptr;
804480093f4SDimitry Andric 
805480093f4SDimitry Andric   if (const auto CompSym = comparison.getAsSymbol()) {
806480093f4SDimitry Andric     assert(isa<SymIntExpr>(CompSym) &&
807480093f4SDimitry Andric            "Symbol comparison must be a `SymIntExpr`");
808480093f4SDimitry Andric     assert(BinaryOperator::isComparisonOp(
809480093f4SDimitry Andric                cast<SymIntExpr>(CompSym)->getOpcode()) &&
810480093f4SDimitry Andric            "Symbol comparison must be a comparison");
811480093f4SDimitry Andric     return assumeNoOverflow(NewState, cast<SymIntExpr>(CompSym)->getLHS(), 2);
812480093f4SDimitry Andric   }
813480093f4SDimitry Andric 
814480093f4SDimitry Andric   return NewState;
815480093f4SDimitry Andric }
816480093f4SDimitry Andric 
isBoundThroughLazyCompoundVal(const Environment & Env,const MemRegion * Reg)817480093f4SDimitry Andric bool isBoundThroughLazyCompoundVal(const Environment &Env,
818480093f4SDimitry Andric                                    const MemRegion *Reg) {
819480093f4SDimitry Andric   for (const auto &Binding : Env) {
820480093f4SDimitry Andric     if (const auto LCVal = Binding.second.getAs<nonloc::LazyCompoundVal>()) {
821480093f4SDimitry Andric       if (LCVal->getRegion() == Reg)
822480093f4SDimitry Andric         return true;
823480093f4SDimitry Andric     }
824480093f4SDimitry Andric   }
825480093f4SDimitry Andric 
826480093f4SDimitry Andric   return false;
827480093f4SDimitry Andric }
828480093f4SDimitry Andric 
findCallEnter(const ExplodedNode * Node,const Expr * Call)8295ffd83dbSDimitry Andric const ExplodedNode *findCallEnter(const ExplodedNode *Node, const Expr *Call) {
8305ffd83dbSDimitry Andric   while (Node) {
8315ffd83dbSDimitry Andric     ProgramPoint PP = Node->getLocation();
8325ffd83dbSDimitry Andric     if (auto Enter = PP.getAs<CallEnter>()) {
8335ffd83dbSDimitry Andric       if (Enter->getCallExpr() == Call)
8345ffd83dbSDimitry Andric         break;
835480093f4SDimitry Andric     }
836480093f4SDimitry Andric 
8375ffd83dbSDimitry Andric     Node = Node->getFirstPred();
838480093f4SDimitry Andric   }
839480093f4SDimitry Andric 
8405ffd83dbSDimitry Andric   return Node;
841480093f4SDimitry Andric }
842480093f4SDimitry Andric 
843480093f4SDimitry Andric } // namespace
844480093f4SDimitry Andric 
registerIteratorModeling(CheckerManager & mgr)845480093f4SDimitry Andric void ento::registerIteratorModeling(CheckerManager &mgr) {
846480093f4SDimitry Andric   mgr.registerChecker<IteratorModeling>();
847480093f4SDimitry Andric }
848480093f4SDimitry Andric 
shouldRegisterIteratorModeling(const CheckerManager & mgr)8495ffd83dbSDimitry Andric bool ento::shouldRegisterIteratorModeling(const CheckerManager &mgr) {
850480093f4SDimitry Andric   return true;
851480093f4SDimitry Andric }
852