19a08a3faSAdam Balogh //===-- ContainerModeling.cpp -------------------------------------*- C++ -*--//
29a08a3faSAdam Balogh //
39a08a3faSAdam Balogh // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
49a08a3faSAdam Balogh // See https://llvm.org/LICENSE.txt for license information.
59a08a3faSAdam Balogh // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
69a08a3faSAdam Balogh //
79a08a3faSAdam Balogh //===----------------------------------------------------------------------===//
89a08a3faSAdam Balogh //
99a08a3faSAdam Balogh // Defines a modeling-checker for modeling STL container-like containers.
109a08a3faSAdam Balogh //
119a08a3faSAdam Balogh //===----------------------------------------------------------------------===//
129a08a3faSAdam Balogh 
139a08a3faSAdam Balogh #include "clang/AST/DeclTemplate.h"
14afcb77ccSAdam Balogh #include "clang/Driver/DriverDiagnostic.h"
150b9d3a6eSBalazs Benics #include "clang/StaticAnalyzer/Checkers/BuiltinCheckerRegistration.h"
169a08a3faSAdam Balogh #include "clang/StaticAnalyzer/Core/BugReporter/BugType.h"
179a08a3faSAdam Balogh #include "clang/StaticAnalyzer/Core/Checker.h"
180b9d3a6eSBalazs Benics #include "clang/StaticAnalyzer/Core/PathSensitive/CallDescription.h"
199a08a3faSAdam Balogh #include "clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h"
209a08a3faSAdam Balogh #include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h"
219a08a3faSAdam Balogh #include "clang/StaticAnalyzer/Core/PathSensitive/DynamicType.h"
229a08a3faSAdam Balogh 
239a08a3faSAdam Balogh #include "Iterator.h"
249a08a3faSAdam Balogh 
259a08a3faSAdam Balogh #include <utility>
269a08a3faSAdam Balogh 
279a08a3faSAdam Balogh using namespace clang;
289a08a3faSAdam Balogh using namespace ento;
299a08a3faSAdam Balogh using namespace iterator;
309a08a3faSAdam Balogh 
319a08a3faSAdam Balogh namespace {
329a08a3faSAdam Balogh 
339a08a3faSAdam Balogh class ContainerModeling
349a08a3faSAdam Balogh   : public Checker<check::PostCall, check::LiveSymbols, check::DeadSymbols> {
359a08a3faSAdam Balogh 
36a3f4d17aSAdam Balogh   void handleBegin(CheckerContext &C, const Expr *CE, SVal RetVal,
37a3f4d17aSAdam Balogh                    SVal Cont) const;
38a3f4d17aSAdam Balogh   void handleEnd(CheckerContext &C, const Expr *CE, SVal RetVal,
39a3f4d17aSAdam Balogh                  SVal Cont) const;
40a3f4d17aSAdam Balogh   void handleAssignment(CheckerContext &C, SVal Cont, const Expr *CE = nullptr,
41a3f4d17aSAdam Balogh                         SVal OldCont = UndefinedVal()) const;
42a3f4d17aSAdam Balogh   void handleAssign(CheckerContext &C, SVal Cont, const Expr *ContE) const;
43a3f4d17aSAdam Balogh   void handleClear(CheckerContext &C, SVal Cont, const Expr *ContE) const;
44a3f4d17aSAdam Balogh   void handlePushBack(CheckerContext &C, SVal Cont, const Expr *ContE) const;
45a3f4d17aSAdam Balogh   void handlePopBack(CheckerContext &C, SVal Cont, const Expr *ContE) const;
46a3f4d17aSAdam Balogh   void handlePushFront(CheckerContext &C, SVal Cont, const Expr *ContE) const;
47a3f4d17aSAdam Balogh   void handlePopFront(CheckerContext &C, SVal Cont, const Expr *ContE) const;
48a3f4d17aSAdam Balogh   void handleInsert(CheckerContext &C, SVal Cont, SVal Iter) const;
49a3f4d17aSAdam Balogh   void handleErase(CheckerContext &C, SVal Cont, SVal Iter) const;
50a3f4d17aSAdam Balogh   void handleErase(CheckerContext &C, SVal Cont, SVal Iter1, SVal Iter2) const;
51a3f4d17aSAdam Balogh   void handleEraseAfter(CheckerContext &C, SVal Cont, SVal Iter) const;
52a3f4d17aSAdam Balogh   void handleEraseAfter(CheckerContext &C, SVal Cont, SVal Iter1,
53a3f4d17aSAdam Balogh                         SVal Iter2) const;
54a3f4d17aSAdam Balogh   const NoteTag *getChangeTag(CheckerContext &C, StringRef Text,
55a3f4d17aSAdam Balogh                               const MemRegion *ContReg,
56a3f4d17aSAdam Balogh                               const Expr *ContE) const;
579a08a3faSAdam Balogh   void printState(raw_ostream &Out, ProgramStateRef State, const char *NL,
589a08a3faSAdam Balogh                   const char *Sep) const override;
599a08a3faSAdam Balogh 
609a08a3faSAdam Balogh public:
61a3f4d17aSAdam Balogh   ContainerModeling() = default;
629a08a3faSAdam Balogh 
639a08a3faSAdam Balogh   void checkPostCall(const CallEvent &Call, CheckerContext &C) const;
649a08a3faSAdam Balogh   void checkLiveSymbols(ProgramStateRef State, SymbolReaper &SR) const;
659a08a3faSAdam Balogh   void checkDeadSymbols(SymbolReaper &SR, CheckerContext &C) const;
669a08a3faSAdam Balogh 
67a3f4d17aSAdam Balogh   using NoItParamFn = void (ContainerModeling::*)(CheckerContext &, SVal,
68a3f4d17aSAdam Balogh                                                   const Expr *) const;
69a3f4d17aSAdam Balogh   using OneItParamFn = void (ContainerModeling::*)(CheckerContext &, SVal,
70a3f4d17aSAdam Balogh                                                    SVal) const;
71a3f4d17aSAdam Balogh   using TwoItParamFn = void (ContainerModeling::*)(CheckerContext &, SVal, SVal,
72a3f4d17aSAdam Balogh                                                    SVal) const;
739a08a3faSAdam Balogh 
749a08a3faSAdam Balogh   CallDescriptionMap<NoItParamFn> NoIterParamFunctions = {
75*e6ef134fSBalazs Benics       {{"clear", 0}, &ContainerModeling::handleClear},
76*e6ef134fSBalazs Benics       {{"assign", 2}, &ContainerModeling::handleAssign},
77*e6ef134fSBalazs Benics       {{"push_back", 1}, &ContainerModeling::handlePushBack},
78*e6ef134fSBalazs Benics       {{"emplace_back", 1}, &ContainerModeling::handlePushBack},
79*e6ef134fSBalazs Benics       {{"pop_back", 0}, &ContainerModeling::handlePopBack},
80*e6ef134fSBalazs Benics       {{"push_front", 1}, &ContainerModeling::handlePushFront},
81*e6ef134fSBalazs Benics       {{"emplace_front", 1}, &ContainerModeling::handlePushFront},
82*e6ef134fSBalazs Benics       {{"pop_front", 0}, &ContainerModeling::handlePopFront},
839a08a3faSAdam Balogh   };
849a08a3faSAdam Balogh 
859a08a3faSAdam Balogh   CallDescriptionMap<OneItParamFn> OneIterParamFunctions = {
86*e6ef134fSBalazs Benics       {{"insert", 2}, &ContainerModeling::handleInsert},
87*e6ef134fSBalazs Benics       {{"emplace", 2}, &ContainerModeling::handleInsert},
88*e6ef134fSBalazs Benics       {{"erase", 1}, &ContainerModeling::handleErase},
89*e6ef134fSBalazs Benics       {{"erase_after", 1}, &ContainerModeling::handleEraseAfter},
909a08a3faSAdam Balogh   };
919a08a3faSAdam Balogh 
929a08a3faSAdam Balogh   CallDescriptionMap<TwoItParamFn> TwoIterParamFunctions = {
93*e6ef134fSBalazs Benics       {{"erase", 2}, &ContainerModeling::handleErase},
94*e6ef134fSBalazs Benics       {{"erase_after", 2}, &ContainerModeling::handleEraseAfter},
959a08a3faSAdam Balogh   };
969a08a3faSAdam Balogh };
979a08a3faSAdam Balogh 
989a08a3faSAdam Balogh bool isBeginCall(const FunctionDecl *Func);
999a08a3faSAdam Balogh bool isEndCall(const FunctionDecl *Func);
1009a08a3faSAdam Balogh bool hasSubscriptOperator(ProgramStateRef State, const MemRegion *Reg);
1019a08a3faSAdam Balogh bool frontModifiable(ProgramStateRef State, const MemRegion *Reg);
1029a08a3faSAdam Balogh bool backModifiable(ProgramStateRef State, const MemRegion *Reg);
1039a08a3faSAdam Balogh SymbolRef getContainerBegin(ProgramStateRef State, const MemRegion *Cont);
1049a08a3faSAdam Balogh SymbolRef getContainerEnd(ProgramStateRef State, const MemRegion *Cont);
1059a08a3faSAdam Balogh ProgramStateRef createContainerBegin(ProgramStateRef State,
1069a08a3faSAdam Balogh                                      const MemRegion *Cont, const Expr *E,
1079a08a3faSAdam Balogh                                      QualType T, const LocationContext *LCtx,
1089a08a3faSAdam Balogh                                      unsigned BlockCount);
1099a08a3faSAdam Balogh ProgramStateRef createContainerEnd(ProgramStateRef State, const MemRegion *Cont,
1109a08a3faSAdam Balogh                                    const Expr *E, QualType T,
1119a08a3faSAdam Balogh                                    const LocationContext *LCtx,
1129a08a3faSAdam Balogh                                    unsigned BlockCount);
1139a08a3faSAdam Balogh ProgramStateRef setContainerData(ProgramStateRef State, const MemRegion *Cont,
1149a08a3faSAdam Balogh                                  const ContainerData &CData);
1159a08a3faSAdam Balogh ProgramStateRef invalidateAllIteratorPositions(ProgramStateRef State,
1169a08a3faSAdam Balogh                                                const MemRegion *Cont);
1179a08a3faSAdam Balogh ProgramStateRef
1189a08a3faSAdam Balogh invalidateAllIteratorPositionsExcept(ProgramStateRef State,
1199a08a3faSAdam Balogh                                      const MemRegion *Cont, SymbolRef Offset,
1209a08a3faSAdam Balogh                                      BinaryOperator::Opcode Opc);
1219a08a3faSAdam Balogh ProgramStateRef invalidateIteratorPositions(ProgramStateRef State,
1229a08a3faSAdam Balogh                                             SymbolRef Offset,
1239a08a3faSAdam Balogh                                             BinaryOperator::Opcode Opc);
1249a08a3faSAdam Balogh ProgramStateRef invalidateIteratorPositions(ProgramStateRef State,
1259a08a3faSAdam Balogh                                             SymbolRef Offset1,
1269a08a3faSAdam Balogh                                             BinaryOperator::Opcode Opc1,
1279a08a3faSAdam Balogh                                             SymbolRef Offset2,
1289a08a3faSAdam Balogh                                             BinaryOperator::Opcode Opc2);
1299a08a3faSAdam Balogh ProgramStateRef reassignAllIteratorPositions(ProgramStateRef State,
1309a08a3faSAdam Balogh                                              const MemRegion *Cont,
1319a08a3faSAdam Balogh                                              const MemRegion *NewCont);
1329a08a3faSAdam Balogh ProgramStateRef reassignAllIteratorPositionsUnless(ProgramStateRef State,
1339a08a3faSAdam Balogh                                                    const MemRegion *Cont,
1349a08a3faSAdam Balogh                                                    const MemRegion *NewCont,
1359a08a3faSAdam Balogh                                                    SymbolRef Offset,
1369a08a3faSAdam Balogh                                                    BinaryOperator::Opcode Opc);
1379a08a3faSAdam Balogh ProgramStateRef rebaseSymbolInIteratorPositionsIf(
1389a08a3faSAdam Balogh     ProgramStateRef State, SValBuilder &SVB, SymbolRef OldSym,
1399a08a3faSAdam Balogh     SymbolRef NewSym, SymbolRef CondSym, BinaryOperator::Opcode Opc);
1409a08a3faSAdam Balogh SymbolRef rebaseSymbol(ProgramStateRef State, SValBuilder &SVB, SymbolRef Expr,
1419a08a3faSAdam Balogh                         SymbolRef OldSym, SymbolRef NewSym);
1429a08a3faSAdam Balogh bool hasLiveIterators(ProgramStateRef State, const MemRegion *Cont);
1439a08a3faSAdam Balogh 
1449a08a3faSAdam Balogh } // namespace
1459a08a3faSAdam Balogh 
checkPostCall(const CallEvent & Call,CheckerContext & C) const1469a08a3faSAdam Balogh void ContainerModeling::checkPostCall(const CallEvent &Call,
1479a08a3faSAdam Balogh                                      CheckerContext &C) const {
1489a08a3faSAdam Balogh   const auto *Func = dyn_cast_or_null<FunctionDecl>(Call.getDecl());
1499a08a3faSAdam Balogh   if (!Func)
1509a08a3faSAdam Balogh     return;
1519a08a3faSAdam Balogh 
1529a08a3faSAdam Balogh   if (Func->isOverloadedOperator()) {
1539a08a3faSAdam Balogh     const auto Op = Func->getOverloadedOperator();
1549a08a3faSAdam Balogh     if (Op == OO_Equal) {
1559a08a3faSAdam Balogh       // Overloaded 'operator=' must be a non-static member function.
1569a08a3faSAdam Balogh       const auto *InstCall = cast<CXXInstanceCall>(&Call);
1579a08a3faSAdam Balogh       if (cast<CXXMethodDecl>(Func)->isMoveAssignmentOperator()) {
1589a08a3faSAdam Balogh         handleAssignment(C, InstCall->getCXXThisVal(), Call.getOriginExpr(),
1599a08a3faSAdam Balogh                      Call.getArgSVal(0));
1609a08a3faSAdam Balogh         return;
1619a08a3faSAdam Balogh       }
1629a08a3faSAdam Balogh 
1639a08a3faSAdam Balogh       handleAssignment(C, InstCall->getCXXThisVal());
1649a08a3faSAdam Balogh       return;
1659a08a3faSAdam Balogh     }
1669a08a3faSAdam Balogh   } else {
1679a08a3faSAdam Balogh     if (const auto *InstCall = dyn_cast<CXXInstanceCall>(&Call)) {
1689a08a3faSAdam Balogh       const NoItParamFn *Handler0 = NoIterParamFunctions.lookup(Call);
1699a08a3faSAdam Balogh       if (Handler0) {
170a3f4d17aSAdam Balogh         (this->**Handler0)(C, InstCall->getCXXThisVal(),
171a3f4d17aSAdam Balogh                            InstCall->getCXXThisExpr());
1729a08a3faSAdam Balogh         return;
1739a08a3faSAdam Balogh       }
1749a08a3faSAdam Balogh 
1759a08a3faSAdam Balogh       const OneItParamFn *Handler1 = OneIterParamFunctions.lookup(Call);
1769a08a3faSAdam Balogh       if (Handler1) {
1779a08a3faSAdam Balogh         (this->**Handler1)(C, InstCall->getCXXThisVal(), Call.getArgSVal(0));
1789a08a3faSAdam Balogh         return;
1799a08a3faSAdam Balogh       }
1809a08a3faSAdam Balogh 
1819a08a3faSAdam Balogh       const TwoItParamFn *Handler2 = TwoIterParamFunctions.lookup(Call);
1829a08a3faSAdam Balogh       if (Handler2) {
1839a08a3faSAdam Balogh         (this->**Handler2)(C, InstCall->getCXXThisVal(), Call.getArgSVal(0),
1849a08a3faSAdam Balogh                            Call.getArgSVal(1));
1859a08a3faSAdam Balogh         return;
1869a08a3faSAdam Balogh       }
1879a08a3faSAdam Balogh 
1889a08a3faSAdam Balogh       const auto *OrigExpr = Call.getOriginExpr();
1899a08a3faSAdam Balogh       if (!OrigExpr)
1909a08a3faSAdam Balogh         return;
1919a08a3faSAdam Balogh 
1929a08a3faSAdam Balogh       if (isBeginCall(Func)) {
1939a08a3faSAdam Balogh         handleBegin(C, OrigExpr, Call.getReturnValue(),
1949a08a3faSAdam Balogh                     InstCall->getCXXThisVal());
1959a08a3faSAdam Balogh         return;
1969a08a3faSAdam Balogh       }
1979a08a3faSAdam Balogh 
1989a08a3faSAdam Balogh       if (isEndCall(Func)) {
1999a08a3faSAdam Balogh         handleEnd(C, OrigExpr, Call.getReturnValue(),
2009a08a3faSAdam Balogh                   InstCall->getCXXThisVal());
2019a08a3faSAdam Balogh         return;
2029a08a3faSAdam Balogh       }
2039a08a3faSAdam Balogh     }
2049a08a3faSAdam Balogh   }
2059a08a3faSAdam Balogh }
2069a08a3faSAdam Balogh 
checkLiveSymbols(ProgramStateRef State,SymbolReaper & SR) const2079a08a3faSAdam Balogh void ContainerModeling::checkLiveSymbols(ProgramStateRef State,
2089a08a3faSAdam Balogh                                          SymbolReaper &SR) const {
2099a08a3faSAdam Balogh   // Keep symbolic expressions of container begins and ends alive
2109a08a3faSAdam Balogh   auto ContMap = State->get<ContainerMap>();
2119a08a3faSAdam Balogh   for (const auto &Cont : ContMap) {
2129a08a3faSAdam Balogh     const auto CData = Cont.second;
2139a08a3faSAdam Balogh     if (CData.getBegin()) {
2149a08a3faSAdam Balogh       SR.markLive(CData.getBegin());
2159a08a3faSAdam Balogh       if(const auto *SIE = dyn_cast<SymIntExpr>(CData.getBegin()))
2169a08a3faSAdam Balogh         SR.markLive(SIE->getLHS());
2179a08a3faSAdam Balogh     }
2189a08a3faSAdam Balogh     if (CData.getEnd()) {
2199a08a3faSAdam Balogh       SR.markLive(CData.getEnd());
2209a08a3faSAdam Balogh       if(const auto *SIE = dyn_cast<SymIntExpr>(CData.getEnd()))
2219a08a3faSAdam Balogh         SR.markLive(SIE->getLHS());
2229a08a3faSAdam Balogh     }
2239a08a3faSAdam Balogh   }
2249a08a3faSAdam Balogh }
2259a08a3faSAdam Balogh 
checkDeadSymbols(SymbolReaper & SR,CheckerContext & C) const2269a08a3faSAdam Balogh void ContainerModeling::checkDeadSymbols(SymbolReaper &SR,
2279a08a3faSAdam Balogh                                          CheckerContext &C) const {
2289a08a3faSAdam Balogh   // Cleanup
2299a08a3faSAdam Balogh   auto State = C.getState();
2309a08a3faSAdam Balogh 
2319a08a3faSAdam Balogh   auto ContMap = State->get<ContainerMap>();
2329a08a3faSAdam Balogh   for (const auto &Cont : ContMap) {
2339a08a3faSAdam Balogh     if (!SR.isLiveRegion(Cont.first)) {
2349a08a3faSAdam Balogh       // We must keep the container data while it has live iterators to be able
2359a08a3faSAdam Balogh       // to compare them to the begin and the end of the container.
2369a08a3faSAdam Balogh       if (!hasLiveIterators(State, Cont.first)) {
2379a08a3faSAdam Balogh         State = State->remove<ContainerMap>(Cont.first);
2389a08a3faSAdam Balogh       }
2399a08a3faSAdam Balogh     }
2409a08a3faSAdam Balogh   }
2419a08a3faSAdam Balogh 
2429a08a3faSAdam Balogh   C.addTransition(State);
2439a08a3faSAdam Balogh }
2449a08a3faSAdam Balogh 
handleBegin(CheckerContext & C,const Expr * CE,SVal RetVal,SVal Cont) const2459a08a3faSAdam Balogh void ContainerModeling::handleBegin(CheckerContext &C, const Expr *CE,
246a3f4d17aSAdam Balogh                                    SVal RetVal, SVal Cont) const {
2479a08a3faSAdam Balogh   const auto *ContReg = Cont.getAsRegion();
2489a08a3faSAdam Balogh   if (!ContReg)
2499a08a3faSAdam Balogh     return;
2509a08a3faSAdam Balogh 
2519a08a3faSAdam Balogh   ContReg = ContReg->getMostDerivedObjectRegion();
2529a08a3faSAdam Balogh 
2539a08a3faSAdam Balogh   // If the container already has a begin symbol then use it. Otherwise first
2549a08a3faSAdam Balogh   // create a new one.
2559a08a3faSAdam Balogh   auto State = C.getState();
2569a08a3faSAdam Balogh   auto BeginSym = getContainerBegin(State, ContReg);
2579a08a3faSAdam Balogh   if (!BeginSym) {
2589a08a3faSAdam Balogh     State = createContainerBegin(State, ContReg, CE, C.getASTContext().LongTy,
2599a08a3faSAdam Balogh                                  C.getLocationContext(), C.blockCount());
2609a08a3faSAdam Balogh     BeginSym = getContainerBegin(State, ContReg);
2619a08a3faSAdam Balogh   }
2629a08a3faSAdam Balogh   State = setIteratorPosition(State, RetVal,
2639a08a3faSAdam Balogh                               IteratorPosition::getPosition(ContReg, BeginSym));
2649a08a3faSAdam Balogh   C.addTransition(State);
2659a08a3faSAdam Balogh }
2669a08a3faSAdam Balogh 
handleEnd(CheckerContext & C,const Expr * CE,SVal RetVal,SVal Cont) const2679a08a3faSAdam Balogh void ContainerModeling::handleEnd(CheckerContext &C, const Expr *CE,
268a3f4d17aSAdam Balogh                                  SVal RetVal, SVal Cont) const {
2699a08a3faSAdam Balogh   const auto *ContReg = Cont.getAsRegion();
2709a08a3faSAdam Balogh   if (!ContReg)
2719a08a3faSAdam Balogh     return;
2729a08a3faSAdam Balogh 
2739a08a3faSAdam Balogh   ContReg = ContReg->getMostDerivedObjectRegion();
2749a08a3faSAdam Balogh 
2759a08a3faSAdam Balogh   // If the container already has an end symbol then use it. Otherwise first
2769a08a3faSAdam Balogh   // create a new one.
2779a08a3faSAdam Balogh   auto State = C.getState();
2789a08a3faSAdam Balogh   auto EndSym = getContainerEnd(State, ContReg);
2799a08a3faSAdam Balogh   if (!EndSym) {
2809a08a3faSAdam Balogh     State = createContainerEnd(State, ContReg, CE, C.getASTContext().LongTy,
2819a08a3faSAdam Balogh                                C.getLocationContext(), C.blockCount());
2829a08a3faSAdam Balogh     EndSym = getContainerEnd(State, ContReg);
2839a08a3faSAdam Balogh   }
2849a08a3faSAdam Balogh   State = setIteratorPosition(State, RetVal,
2859a08a3faSAdam Balogh                               IteratorPosition::getPosition(ContReg, EndSym));
2869a08a3faSAdam Balogh   C.addTransition(State);
2879a08a3faSAdam Balogh }
2889a08a3faSAdam Balogh 
handleAssignment(CheckerContext & C,SVal Cont,const Expr * CE,SVal OldCont) const289a3f4d17aSAdam Balogh void ContainerModeling::handleAssignment(CheckerContext &C, SVal Cont,
290a3f4d17aSAdam Balogh                                          const Expr *CE, SVal OldCont) const {
2919a08a3faSAdam Balogh   const auto *ContReg = Cont.getAsRegion();
2929a08a3faSAdam Balogh   if (!ContReg)
2939a08a3faSAdam Balogh     return;
2949a08a3faSAdam Balogh 
2959a08a3faSAdam Balogh   ContReg = ContReg->getMostDerivedObjectRegion();
2969a08a3faSAdam Balogh 
2979a08a3faSAdam Balogh   // Assignment of a new value to a container always invalidates all its
2989a08a3faSAdam Balogh   // iterators
2999a08a3faSAdam Balogh   auto State = C.getState();
3009a08a3faSAdam Balogh   const auto CData = getContainerData(State, ContReg);
3019a08a3faSAdam Balogh   if (CData) {
3029a08a3faSAdam Balogh     State = invalidateAllIteratorPositions(State, ContReg);
3039a08a3faSAdam Balogh   }
3049a08a3faSAdam Balogh 
3059a08a3faSAdam Balogh   // In case of move, iterators of the old container (except the past-end
3069a08a3faSAdam Balogh   // iterators) remain valid but refer to the new container
3079a08a3faSAdam Balogh   if (!OldCont.isUndef()) {
3089a08a3faSAdam Balogh     const auto *OldContReg = OldCont.getAsRegion();
3099a08a3faSAdam Balogh     if (OldContReg) {
3109a08a3faSAdam Balogh       OldContReg = OldContReg->getMostDerivedObjectRegion();
3119a08a3faSAdam Balogh       const auto OldCData = getContainerData(State, OldContReg);
3129a08a3faSAdam Balogh       if (OldCData) {
3139a08a3faSAdam Balogh         if (const auto OldEndSym = OldCData->getEnd()) {
3149a08a3faSAdam Balogh           // If we already assigned an "end" symbol to the old container, then
3159a08a3faSAdam Balogh           // first reassign all iterator positions to the new container which
3169a08a3faSAdam Balogh           // are not past the container (thus not greater or equal to the
3179a08a3faSAdam Balogh           // current "end" symbol).
3189a08a3faSAdam Balogh           State = reassignAllIteratorPositionsUnless(State, OldContReg, ContReg,
3199a08a3faSAdam Balogh                                                      OldEndSym, BO_GE);
3209a08a3faSAdam Balogh           auto &SymMgr = C.getSymbolManager();
3219a08a3faSAdam Balogh           auto &SVB = C.getSValBuilder();
3229a08a3faSAdam Balogh           // Then generate and assign a new "end" symbol for the new container.
3239a08a3faSAdam Balogh           auto NewEndSym =
3249a08a3faSAdam Balogh               SymMgr.conjureSymbol(CE, C.getLocationContext(),
3259a08a3faSAdam Balogh                                    C.getASTContext().LongTy, C.blockCount());
3269a08a3faSAdam Balogh           State = assumeNoOverflow(State, NewEndSym, 4);
3279a08a3faSAdam Balogh           if (CData) {
3289a08a3faSAdam Balogh             State = setContainerData(State, ContReg, CData->newEnd(NewEndSym));
3299a08a3faSAdam Balogh           } else {
3309a08a3faSAdam Balogh             State = setContainerData(State, ContReg,
3319a08a3faSAdam Balogh                                      ContainerData::fromEnd(NewEndSym));
3329a08a3faSAdam Balogh           }
3339a08a3faSAdam Balogh           // Finally, replace the old "end" symbol in the already reassigned
3349a08a3faSAdam Balogh           // iterator positions with the new "end" symbol.
3359a08a3faSAdam Balogh           State = rebaseSymbolInIteratorPositionsIf(
3369a08a3faSAdam Balogh               State, SVB, OldEndSym, NewEndSym, OldEndSym, BO_LT);
3379a08a3faSAdam Balogh         } else {
3389a08a3faSAdam Balogh           // There was no "end" symbol assigned yet to the old container,
3399a08a3faSAdam Balogh           // so reassign all iterator positions to the new container.
3409a08a3faSAdam Balogh           State = reassignAllIteratorPositions(State, OldContReg, ContReg);
3419a08a3faSAdam Balogh         }
3429a08a3faSAdam Balogh         if (const auto OldBeginSym = OldCData->getBegin()) {
3439a08a3faSAdam Balogh           // If we already assigned a "begin" symbol to the old container, then
3449a08a3faSAdam Balogh           // assign it to the new container and remove it from the old one.
3459a08a3faSAdam Balogh           if (CData) {
3469a08a3faSAdam Balogh             State =
3479a08a3faSAdam Balogh                 setContainerData(State, ContReg, CData->newBegin(OldBeginSym));
3489a08a3faSAdam Balogh           } else {
3499a08a3faSAdam Balogh             State = setContainerData(State, ContReg,
3509a08a3faSAdam Balogh                                      ContainerData::fromBegin(OldBeginSym));
3519a08a3faSAdam Balogh           }
3529a08a3faSAdam Balogh           State =
3539a08a3faSAdam Balogh               setContainerData(State, OldContReg, OldCData->newBegin(nullptr));
3549a08a3faSAdam Balogh         }
3559a08a3faSAdam Balogh       } else {
3569a08a3faSAdam Balogh         // There was neither "begin" nor "end" symbol assigned yet to the old
3579a08a3faSAdam Balogh         // container, so reassign all iterator positions to the new container.
3589a08a3faSAdam Balogh         State = reassignAllIteratorPositions(State, OldContReg, ContReg);
3599a08a3faSAdam Balogh       }
3609a08a3faSAdam Balogh     }
3619a08a3faSAdam Balogh   }
3629a08a3faSAdam Balogh   C.addTransition(State);
3639a08a3faSAdam Balogh }
3649a08a3faSAdam Balogh 
handleAssign(CheckerContext & C,SVal Cont,const Expr * ContE) const365a3f4d17aSAdam Balogh void ContainerModeling::handleAssign(CheckerContext &C, SVal Cont,
366a3f4d17aSAdam Balogh                                      const Expr *ContE) const {
3679a08a3faSAdam Balogh   const auto *ContReg = Cont.getAsRegion();
3689a08a3faSAdam Balogh   if (!ContReg)
3699a08a3faSAdam Balogh     return;
3709a08a3faSAdam Balogh 
3719a08a3faSAdam Balogh   ContReg = ContReg->getMostDerivedObjectRegion();
3729a08a3faSAdam Balogh 
3739a08a3faSAdam Balogh   // The assign() operation invalidates all the iterators
3749a08a3faSAdam Balogh   auto State = C.getState();
3759a08a3faSAdam Balogh   State = invalidateAllIteratorPositions(State, ContReg);
3769a08a3faSAdam Balogh   C.addTransition(State);
3779a08a3faSAdam Balogh }
3789a08a3faSAdam Balogh 
handleClear(CheckerContext & C,SVal Cont,const Expr * ContE) const379a3f4d17aSAdam Balogh void ContainerModeling::handleClear(CheckerContext &C, SVal Cont,
380a3f4d17aSAdam Balogh                                     const Expr *ContE) const {
3819a08a3faSAdam Balogh   const auto *ContReg = Cont.getAsRegion();
3829a08a3faSAdam Balogh   if (!ContReg)
3839a08a3faSAdam Balogh     return;
3849a08a3faSAdam Balogh 
3859a08a3faSAdam Balogh   ContReg = ContReg->getMostDerivedObjectRegion();
3869a08a3faSAdam Balogh 
3879a08a3faSAdam Balogh   // The clear() operation invalidates all the iterators, except the past-end
3889a08a3faSAdam Balogh   // iterators of list-like containers
3899a08a3faSAdam Balogh   auto State = C.getState();
3909a08a3faSAdam Balogh   if (!hasSubscriptOperator(State, ContReg) ||
3919a08a3faSAdam Balogh       !backModifiable(State, ContReg)) {
3929a08a3faSAdam Balogh     const auto CData = getContainerData(State, ContReg);
3939a08a3faSAdam Balogh     if (CData) {
3949a08a3faSAdam Balogh       if (const auto EndSym = CData->getEnd()) {
3959a08a3faSAdam Balogh         State =
3969a08a3faSAdam Balogh             invalidateAllIteratorPositionsExcept(State, ContReg, EndSym, BO_GE);
3979a08a3faSAdam Balogh         C.addTransition(State);
3989a08a3faSAdam Balogh         return;
3999a08a3faSAdam Balogh       }
4009a08a3faSAdam Balogh     }
4019a08a3faSAdam Balogh   }
402a3f4d17aSAdam Balogh   const NoteTag *ChangeTag =
403a3f4d17aSAdam Balogh     getChangeTag(C, "became empty", ContReg, ContE);
4049a08a3faSAdam Balogh   State = invalidateAllIteratorPositions(State, ContReg);
405a3f4d17aSAdam Balogh   C.addTransition(State, ChangeTag);
4069a08a3faSAdam Balogh }
4079a08a3faSAdam Balogh 
handlePushBack(CheckerContext & C,SVal Cont,const Expr * ContE) const408a3f4d17aSAdam Balogh void ContainerModeling::handlePushBack(CheckerContext &C, SVal Cont,
409a3f4d17aSAdam Balogh                                        const Expr *ContE) const {
4109a08a3faSAdam Balogh   const auto *ContReg = Cont.getAsRegion();
4119a08a3faSAdam Balogh   if (!ContReg)
4129a08a3faSAdam Balogh     return;
4139a08a3faSAdam Balogh 
4149a08a3faSAdam Balogh   ContReg = ContReg->getMostDerivedObjectRegion();
4159a08a3faSAdam Balogh 
4169a08a3faSAdam Balogh   // For deque-like containers invalidate all iterator positions
4179a08a3faSAdam Balogh   auto State = C.getState();
4189a08a3faSAdam Balogh   if (hasSubscriptOperator(State, ContReg) && frontModifiable(State, ContReg)) {
4199a08a3faSAdam Balogh     State = invalidateAllIteratorPositions(State, ContReg);
4209a08a3faSAdam Balogh     C.addTransition(State);
4219a08a3faSAdam Balogh     return;
4229a08a3faSAdam Balogh   }
4239a08a3faSAdam Balogh 
4249a08a3faSAdam Balogh   const auto CData = getContainerData(State, ContReg);
4259a08a3faSAdam Balogh   if (!CData)
4269a08a3faSAdam Balogh     return;
4279a08a3faSAdam Balogh 
4289a08a3faSAdam Balogh   // For vector-like containers invalidate the past-end iterator positions
4299a08a3faSAdam Balogh   if (const auto EndSym = CData->getEnd()) {
4309a08a3faSAdam Balogh     if (hasSubscriptOperator(State, ContReg)) {
4319a08a3faSAdam Balogh       State = invalidateIteratorPositions(State, EndSym, BO_GE);
4329a08a3faSAdam Balogh     }
4339a08a3faSAdam Balogh     auto &SymMgr = C.getSymbolManager();
4349a08a3faSAdam Balogh     auto &BVF = SymMgr.getBasicVals();
4359a08a3faSAdam Balogh     auto &SVB = C.getSValBuilder();
4369a08a3faSAdam Balogh     const auto newEndSym =
4379a08a3faSAdam Balogh       SVB.evalBinOp(State, BO_Add,
4389a08a3faSAdam Balogh                     nonloc::SymbolVal(EndSym),
4399a08a3faSAdam Balogh                     nonloc::ConcreteInt(BVF.getValue(llvm::APSInt::get(1))),
4409a08a3faSAdam Balogh                     SymMgr.getType(EndSym)).getAsSymbol();
441a3f4d17aSAdam Balogh     const NoteTag *ChangeTag =
442a3f4d17aSAdam Balogh       getChangeTag(C, "extended to the back by 1 position", ContReg, ContE);
4439a08a3faSAdam Balogh     State = setContainerData(State, ContReg, CData->newEnd(newEndSym));
444a3f4d17aSAdam Balogh     C.addTransition(State, ChangeTag);
4459a08a3faSAdam Balogh   }
4469a08a3faSAdam Balogh }
4479a08a3faSAdam Balogh 
handlePopBack(CheckerContext & C,SVal Cont,const Expr * ContE) const448a3f4d17aSAdam Balogh void ContainerModeling::handlePopBack(CheckerContext &C, SVal Cont,
449a3f4d17aSAdam Balogh                                       const Expr *ContE) const {
4509a08a3faSAdam Balogh   const auto *ContReg = Cont.getAsRegion();
4519a08a3faSAdam Balogh   if (!ContReg)
4529a08a3faSAdam Balogh     return;
4539a08a3faSAdam Balogh 
4549a08a3faSAdam Balogh   ContReg = ContReg->getMostDerivedObjectRegion();
4559a08a3faSAdam Balogh 
4569a08a3faSAdam Balogh   auto State = C.getState();
4579a08a3faSAdam Balogh   const auto CData = getContainerData(State, ContReg);
4589a08a3faSAdam Balogh   if (!CData)
4599a08a3faSAdam Balogh     return;
4609a08a3faSAdam Balogh 
4619a08a3faSAdam Balogh   if (const auto EndSym = CData->getEnd()) {
4629a08a3faSAdam Balogh     auto &SymMgr = C.getSymbolManager();
4639a08a3faSAdam Balogh     auto &BVF = SymMgr.getBasicVals();
4649a08a3faSAdam Balogh     auto &SVB = C.getSValBuilder();
4659a08a3faSAdam Balogh     const auto BackSym =
4669a08a3faSAdam Balogh       SVB.evalBinOp(State, BO_Sub,
4679a08a3faSAdam Balogh                     nonloc::SymbolVal(EndSym),
4689a08a3faSAdam Balogh                     nonloc::ConcreteInt(BVF.getValue(llvm::APSInt::get(1))),
4699a08a3faSAdam Balogh                     SymMgr.getType(EndSym)).getAsSymbol();
470a3f4d17aSAdam Balogh     const NoteTag *ChangeTag =
471a3f4d17aSAdam Balogh       getChangeTag(C, "shrank from the back by 1 position", ContReg, ContE);
4729a08a3faSAdam Balogh     // For vector-like and deque-like containers invalidate the last and the
4739a08a3faSAdam Balogh     // past-end iterator positions. For list-like containers only invalidate
4749a08a3faSAdam Balogh     // the last position
4759a08a3faSAdam Balogh     if (hasSubscriptOperator(State, ContReg) &&
4769a08a3faSAdam Balogh         backModifiable(State, ContReg)) {
4779a08a3faSAdam Balogh       State = invalidateIteratorPositions(State, BackSym, BO_GE);
4789a08a3faSAdam Balogh       State = setContainerData(State, ContReg, CData->newEnd(nullptr));
4799a08a3faSAdam Balogh     } else {
4809a08a3faSAdam Balogh       State = invalidateIteratorPositions(State, BackSym, BO_EQ);
4819a08a3faSAdam Balogh     }
4829a08a3faSAdam Balogh     auto newEndSym = BackSym;
4839a08a3faSAdam Balogh     State = setContainerData(State, ContReg, CData->newEnd(newEndSym));
484a3f4d17aSAdam Balogh     C.addTransition(State, ChangeTag);
4859a08a3faSAdam Balogh   }
4869a08a3faSAdam Balogh }
4879a08a3faSAdam Balogh 
handlePushFront(CheckerContext & C,SVal Cont,const Expr * ContE) const488a3f4d17aSAdam Balogh void ContainerModeling::handlePushFront(CheckerContext &C, SVal Cont,
489a3f4d17aSAdam Balogh                                         const Expr *ContE) const {
4909a08a3faSAdam Balogh   const auto *ContReg = Cont.getAsRegion();
4919a08a3faSAdam Balogh   if (!ContReg)
4929a08a3faSAdam Balogh     return;
4939a08a3faSAdam Balogh 
4949a08a3faSAdam Balogh   ContReg = ContReg->getMostDerivedObjectRegion();
4959a08a3faSAdam Balogh 
4969a08a3faSAdam Balogh   // For deque-like containers invalidate all iterator positions
4979a08a3faSAdam Balogh   auto State = C.getState();
4989a08a3faSAdam Balogh   if (hasSubscriptOperator(State, ContReg)) {
4999a08a3faSAdam Balogh     State = invalidateAllIteratorPositions(State, ContReg);
5009a08a3faSAdam Balogh     C.addTransition(State);
5019a08a3faSAdam Balogh   } else {
5029a08a3faSAdam Balogh     const auto CData = getContainerData(State, ContReg);
5039a08a3faSAdam Balogh     if (!CData)
5049a08a3faSAdam Balogh       return;
5059a08a3faSAdam Balogh 
5069a08a3faSAdam Balogh     if (const auto BeginSym = CData->getBegin()) {
5079a08a3faSAdam Balogh       auto &SymMgr = C.getSymbolManager();
5089a08a3faSAdam Balogh       auto &BVF = SymMgr.getBasicVals();
5099a08a3faSAdam Balogh       auto &SVB = C.getSValBuilder();
5109a08a3faSAdam Balogh       const auto newBeginSym =
5119a08a3faSAdam Balogh         SVB.evalBinOp(State, BO_Sub,
5129a08a3faSAdam Balogh                       nonloc::SymbolVal(BeginSym),
5139a08a3faSAdam Balogh                       nonloc::ConcreteInt(BVF.getValue(llvm::APSInt::get(1))),
5149a08a3faSAdam Balogh                       SymMgr.getType(BeginSym)).getAsSymbol();
515a3f4d17aSAdam Balogh       const NoteTag *ChangeTag =
516a3f4d17aSAdam Balogh         getChangeTag(C, "extended to the front by 1 position", ContReg, ContE);
5179a08a3faSAdam Balogh       State = setContainerData(State, ContReg, CData->newBegin(newBeginSym));
518a3f4d17aSAdam Balogh       C.addTransition(State, ChangeTag);
5199a08a3faSAdam Balogh     }
5209a08a3faSAdam Balogh   }
5219a08a3faSAdam Balogh }
5229a08a3faSAdam Balogh 
handlePopFront(CheckerContext & C,SVal Cont,const Expr * ContE) const523a3f4d17aSAdam Balogh void ContainerModeling::handlePopFront(CheckerContext &C, SVal Cont,
524a3f4d17aSAdam Balogh                                        const Expr *ContE) const {
5259a08a3faSAdam Balogh   const auto *ContReg = Cont.getAsRegion();
5269a08a3faSAdam Balogh   if (!ContReg)
5279a08a3faSAdam Balogh     return;
5289a08a3faSAdam Balogh 
5299a08a3faSAdam Balogh   ContReg = ContReg->getMostDerivedObjectRegion();
5309a08a3faSAdam Balogh 
5319a08a3faSAdam Balogh   auto State = C.getState();
5329a08a3faSAdam Balogh   const auto CData = getContainerData(State, ContReg);
5339a08a3faSAdam Balogh   if (!CData)
5349a08a3faSAdam Balogh     return;
5359a08a3faSAdam Balogh 
5369a08a3faSAdam Balogh   // For deque-like containers invalidate all iterator positions. For list-like
5379a08a3faSAdam Balogh   // iterators only invalidate the first position
5389a08a3faSAdam Balogh   if (const auto BeginSym = CData->getBegin()) {
5399a08a3faSAdam Balogh     if (hasSubscriptOperator(State, ContReg)) {
5409a08a3faSAdam Balogh       State = invalidateIteratorPositions(State, BeginSym, BO_LE);
5419a08a3faSAdam Balogh     } else {
5429a08a3faSAdam Balogh       State = invalidateIteratorPositions(State, BeginSym, BO_EQ);
5439a08a3faSAdam Balogh     }
5449a08a3faSAdam Balogh     auto &SymMgr = C.getSymbolManager();
5459a08a3faSAdam Balogh     auto &BVF = SymMgr.getBasicVals();
5469a08a3faSAdam Balogh     auto &SVB = C.getSValBuilder();
5479a08a3faSAdam Balogh     const auto newBeginSym =
5489a08a3faSAdam Balogh       SVB.evalBinOp(State, BO_Add,
5499a08a3faSAdam Balogh                     nonloc::SymbolVal(BeginSym),
5509a08a3faSAdam Balogh                     nonloc::ConcreteInt(BVF.getValue(llvm::APSInt::get(1))),
5519a08a3faSAdam Balogh                     SymMgr.getType(BeginSym)).getAsSymbol();
552a3f4d17aSAdam Balogh     const NoteTag *ChangeTag =
553a3f4d17aSAdam Balogh       getChangeTag(C, "shrank from the front by 1 position", ContReg, ContE);
5549a08a3faSAdam Balogh     State = setContainerData(State, ContReg, CData->newBegin(newBeginSym));
555a3f4d17aSAdam Balogh     C.addTransition(State, ChangeTag);
5569a08a3faSAdam Balogh   }
5579a08a3faSAdam Balogh }
5589a08a3faSAdam Balogh 
handleInsert(CheckerContext & C,SVal Cont,SVal Iter) const559a3f4d17aSAdam Balogh void ContainerModeling::handleInsert(CheckerContext &C, SVal Cont,
560a3f4d17aSAdam Balogh                                      SVal Iter) const {
5619a08a3faSAdam Balogh   const auto *ContReg = Cont.getAsRegion();
5629a08a3faSAdam Balogh   if (!ContReg)
5639a08a3faSAdam Balogh     return;
5649a08a3faSAdam Balogh 
5659a08a3faSAdam Balogh   ContReg = ContReg->getMostDerivedObjectRegion();
5669a08a3faSAdam Balogh 
5679a08a3faSAdam Balogh   auto State = C.getState();
5689a08a3faSAdam Balogh   const auto *Pos = getIteratorPosition(State, Iter);
5699a08a3faSAdam Balogh   if (!Pos)
5709a08a3faSAdam Balogh     return;
5719a08a3faSAdam Balogh 
5729a08a3faSAdam Balogh   // For deque-like containers invalidate all iterator positions. For
5739a08a3faSAdam Balogh   // vector-like containers invalidate iterator positions after the insertion.
5749a08a3faSAdam Balogh   if (hasSubscriptOperator(State, ContReg) && backModifiable(State, ContReg)) {
5759a08a3faSAdam Balogh     if (frontModifiable(State, ContReg)) {
5769a08a3faSAdam Balogh       State = invalidateAllIteratorPositions(State, ContReg);
5779a08a3faSAdam Balogh     } else {
5789a08a3faSAdam Balogh       State = invalidateIteratorPositions(State, Pos->getOffset(), BO_GE);
5799a08a3faSAdam Balogh     }
5809a08a3faSAdam Balogh     if (const auto *CData = getContainerData(State, ContReg)) {
5819a08a3faSAdam Balogh       if (const auto EndSym = CData->getEnd()) {
5829a08a3faSAdam Balogh         State = invalidateIteratorPositions(State, EndSym, BO_GE);
5839a08a3faSAdam Balogh         State = setContainerData(State, ContReg, CData->newEnd(nullptr));
5849a08a3faSAdam Balogh       }
5859a08a3faSAdam Balogh     }
5869a08a3faSAdam Balogh     C.addTransition(State);
5879a08a3faSAdam Balogh   }
5889a08a3faSAdam Balogh }
5899a08a3faSAdam Balogh 
handleErase(CheckerContext & C,SVal Cont,SVal Iter) const590a3f4d17aSAdam Balogh void ContainerModeling::handleErase(CheckerContext &C, SVal Cont,
591a3f4d17aSAdam Balogh                                     SVal Iter) const {
5929a08a3faSAdam Balogh   const auto *ContReg = Cont.getAsRegion();
5939a08a3faSAdam Balogh   if (!ContReg)
5949a08a3faSAdam Balogh     return;
5959a08a3faSAdam Balogh 
5969a08a3faSAdam Balogh   ContReg = ContReg->getMostDerivedObjectRegion();
5979a08a3faSAdam Balogh 
5989a08a3faSAdam Balogh   auto State = C.getState();
5999a08a3faSAdam Balogh   const auto *Pos = getIteratorPosition(State, Iter);
6009a08a3faSAdam Balogh   if (!Pos)
6019a08a3faSAdam Balogh     return;
6029a08a3faSAdam Balogh 
6039a08a3faSAdam Balogh   // For deque-like containers invalidate all iterator positions. For
6049a08a3faSAdam Balogh   // vector-like containers invalidate iterator positions at and after the
6059a08a3faSAdam Balogh   // deletion. For list-like containers only invalidate the deleted position.
6069a08a3faSAdam Balogh   if (hasSubscriptOperator(State, ContReg) && backModifiable(State, ContReg)) {
6079a08a3faSAdam Balogh     if (frontModifiable(State, ContReg)) {
6089a08a3faSAdam Balogh       State = invalidateAllIteratorPositions(State, ContReg);
6099a08a3faSAdam Balogh     } else {
6109a08a3faSAdam Balogh       State = invalidateIteratorPositions(State, Pos->getOffset(), BO_GE);
6119a08a3faSAdam Balogh     }
6129a08a3faSAdam Balogh     if (const auto *CData = getContainerData(State, ContReg)) {
6139a08a3faSAdam Balogh       if (const auto EndSym = CData->getEnd()) {
6149a08a3faSAdam Balogh         State = invalidateIteratorPositions(State, EndSym, BO_GE);
6159a08a3faSAdam Balogh         State = setContainerData(State, ContReg, CData->newEnd(nullptr));
6169a08a3faSAdam Balogh       }
6179a08a3faSAdam Balogh     }
6189a08a3faSAdam Balogh   } else {
6199a08a3faSAdam Balogh     State = invalidateIteratorPositions(State, Pos->getOffset(), BO_EQ);
6209a08a3faSAdam Balogh   }
6219a08a3faSAdam Balogh   C.addTransition(State);
6229a08a3faSAdam Balogh }
6239a08a3faSAdam Balogh 
handleErase(CheckerContext & C,SVal Cont,SVal Iter1,SVal Iter2) const624a3f4d17aSAdam Balogh void ContainerModeling::handleErase(CheckerContext &C, SVal Cont, SVal Iter1,
625a3f4d17aSAdam Balogh                                     SVal Iter2) const {
6269a08a3faSAdam Balogh   const auto *ContReg = Cont.getAsRegion();
6279a08a3faSAdam Balogh   if (!ContReg)
6289a08a3faSAdam Balogh     return;
6299a08a3faSAdam Balogh 
6309a08a3faSAdam Balogh   ContReg = ContReg->getMostDerivedObjectRegion();
6319a08a3faSAdam Balogh   auto State = C.getState();
6329a08a3faSAdam Balogh   const auto *Pos1 = getIteratorPosition(State, Iter1);
6339a08a3faSAdam Balogh   const auto *Pos2 = getIteratorPosition(State, Iter2);
6349a08a3faSAdam Balogh   if (!Pos1 || !Pos2)
6359a08a3faSAdam Balogh     return;
6369a08a3faSAdam Balogh 
6379a08a3faSAdam Balogh   // For deque-like containers invalidate all iterator positions. For
6389a08a3faSAdam Balogh   // vector-like containers invalidate iterator positions at and after the
6399a08a3faSAdam Balogh   // deletion range. For list-like containers only invalidate the deleted
6409a08a3faSAdam Balogh   // position range [first..last].
6419a08a3faSAdam Balogh   if (hasSubscriptOperator(State, ContReg) && backModifiable(State, ContReg)) {
6429a08a3faSAdam Balogh     if (frontModifiable(State, ContReg)) {
6439a08a3faSAdam Balogh       State = invalidateAllIteratorPositions(State, ContReg);
6449a08a3faSAdam Balogh     } else {
6459a08a3faSAdam Balogh       State = invalidateIteratorPositions(State, Pos1->getOffset(), BO_GE);
6469a08a3faSAdam Balogh     }
6479a08a3faSAdam Balogh     if (const auto *CData = getContainerData(State, ContReg)) {
6489a08a3faSAdam Balogh       if (const auto EndSym = CData->getEnd()) {
6499a08a3faSAdam Balogh         State = invalidateIteratorPositions(State, EndSym, BO_GE);
6509a08a3faSAdam Balogh         State = setContainerData(State, ContReg, CData->newEnd(nullptr));
6519a08a3faSAdam Balogh       }
6529a08a3faSAdam Balogh     }
6539a08a3faSAdam Balogh   } else {
6549a08a3faSAdam Balogh     State = invalidateIteratorPositions(State, Pos1->getOffset(), BO_GE,
6559a08a3faSAdam Balogh                                         Pos2->getOffset(), BO_LT);
6569a08a3faSAdam Balogh   }
6579a08a3faSAdam Balogh   C.addTransition(State);
6589a08a3faSAdam Balogh }
6599a08a3faSAdam Balogh 
handleEraseAfter(CheckerContext & C,SVal Cont,SVal Iter) const660a3f4d17aSAdam Balogh void ContainerModeling::handleEraseAfter(CheckerContext &C, SVal Cont,
661a3f4d17aSAdam Balogh                                         SVal Iter) const {
6629a08a3faSAdam Balogh   auto State = C.getState();
6639a08a3faSAdam Balogh   const auto *Pos = getIteratorPosition(State, Iter);
6649a08a3faSAdam Balogh   if (!Pos)
6659a08a3faSAdam Balogh     return;
6669a08a3faSAdam Balogh 
6679a08a3faSAdam Balogh   // Invalidate the deleted iterator position, which is the position of the
6689a08a3faSAdam Balogh   // parameter plus one.
6699a08a3faSAdam Balogh   auto &SymMgr = C.getSymbolManager();
6709a08a3faSAdam Balogh   auto &BVF = SymMgr.getBasicVals();
6719a08a3faSAdam Balogh   auto &SVB = C.getSValBuilder();
6729a08a3faSAdam Balogh   const auto NextSym =
6739a08a3faSAdam Balogh     SVB.evalBinOp(State, BO_Add,
6749a08a3faSAdam Balogh                   nonloc::SymbolVal(Pos->getOffset()),
6759a08a3faSAdam Balogh                   nonloc::ConcreteInt(BVF.getValue(llvm::APSInt::get(1))),
6769a08a3faSAdam Balogh                   SymMgr.getType(Pos->getOffset())).getAsSymbol();
6779a08a3faSAdam Balogh   State = invalidateIteratorPositions(State, NextSym, BO_EQ);
6789a08a3faSAdam Balogh   C.addTransition(State);
6799a08a3faSAdam Balogh }
6809a08a3faSAdam Balogh 
handleEraseAfter(CheckerContext & C,SVal Cont,SVal Iter1,SVal Iter2) const681a3f4d17aSAdam Balogh void ContainerModeling::handleEraseAfter(CheckerContext &C, SVal Cont,
682a3f4d17aSAdam Balogh                                          SVal Iter1, SVal Iter2) const {
6839a08a3faSAdam Balogh   auto State = C.getState();
6849a08a3faSAdam Balogh   const auto *Pos1 = getIteratorPosition(State, Iter1);
6859a08a3faSAdam Balogh   const auto *Pos2 = getIteratorPosition(State, Iter2);
6869a08a3faSAdam Balogh   if (!Pos1 || !Pos2)
6879a08a3faSAdam Balogh     return;
6889a08a3faSAdam Balogh 
6899a08a3faSAdam Balogh   // Invalidate the deleted iterator position range (first..last)
6909a08a3faSAdam Balogh   State = invalidateIteratorPositions(State, Pos1->getOffset(), BO_GT,
6919a08a3faSAdam Balogh                                       Pos2->getOffset(), BO_LT);
6929a08a3faSAdam Balogh   C.addTransition(State);
6939a08a3faSAdam Balogh }
6949a08a3faSAdam Balogh 
getChangeTag(CheckerContext & C,StringRef Text,const MemRegion * ContReg,const Expr * ContE) const695a3f4d17aSAdam Balogh const NoteTag *ContainerModeling::getChangeTag(CheckerContext &C,
696a3f4d17aSAdam Balogh                                                StringRef Text,
697a3f4d17aSAdam Balogh                                                const MemRegion *ContReg,
698a3f4d17aSAdam Balogh                                                const Expr *ContE) const {
699a3f4d17aSAdam Balogh   StringRef Name;
700a3f4d17aSAdam Balogh   // First try to get the name of the variable from the region
701a3f4d17aSAdam Balogh   if (const auto *DR = dyn_cast<DeclRegion>(ContReg)) {
702a3f4d17aSAdam Balogh     Name = DR->getDecl()->getName();
703a3f4d17aSAdam Balogh   // If the region is not a `DeclRegion` then use the expression instead
704a3f4d17aSAdam Balogh   } else if (const auto *DRE =
705a3f4d17aSAdam Balogh              dyn_cast<DeclRefExpr>(ContE->IgnoreParenCasts())) {
706a3f4d17aSAdam Balogh     Name = DRE->getDecl()->getName();
707a3f4d17aSAdam Balogh   }
708a3f4d17aSAdam Balogh 
709a3f4d17aSAdam Balogh   return C.getNoteTag(
710a3f4d17aSAdam Balogh       [Text, Name, ContReg](PathSensitiveBugReport &BR) -> std::string {
7111a27d63aSAdam Balogh         if (!BR.isInteresting(ContReg))
7121a27d63aSAdam Balogh           return "";
7131a27d63aSAdam Balogh 
714a3f4d17aSAdam Balogh         SmallString<256> Msg;
715a3f4d17aSAdam Balogh         llvm::raw_svector_ostream Out(Msg);
716a3f4d17aSAdam Balogh         Out << "Container " << (!Name.empty() ? ("'" + Name.str() + "' ") : "" )
717a3f4d17aSAdam Balogh             << Text;
718a3f4d17aSAdam Balogh         return std::string(Out.str());
719a3f4d17aSAdam Balogh       });
720a3f4d17aSAdam Balogh }
721a3f4d17aSAdam Balogh 
printState(raw_ostream & Out,ProgramStateRef State,const char * NL,const char * Sep) const7229a08a3faSAdam Balogh void ContainerModeling::printState(raw_ostream &Out, ProgramStateRef State,
7239a08a3faSAdam Balogh                                   const char *NL, const char *Sep) const {
7249a08a3faSAdam Balogh   auto ContMap = State->get<ContainerMap>();
7259a08a3faSAdam Balogh 
7269a08a3faSAdam Balogh   if (!ContMap.isEmpty()) {
7279a08a3faSAdam Balogh     Out << Sep << "Container Data :" << NL;
7289a08a3faSAdam Balogh     for (const auto &Cont : ContMap) {
7299a08a3faSAdam Balogh       Cont.first->dumpToStream(Out);
7309a08a3faSAdam Balogh       Out << " : [ ";
7319a08a3faSAdam Balogh       const auto CData = Cont.second;
7329a08a3faSAdam Balogh       if (CData.getBegin())
7339a08a3faSAdam Balogh         CData.getBegin()->dumpToStream(Out);
7349a08a3faSAdam Balogh       else
7359a08a3faSAdam Balogh         Out << "<Unknown>";
7369a08a3faSAdam Balogh       Out << " .. ";
7379a08a3faSAdam Balogh       if (CData.getEnd())
7389a08a3faSAdam Balogh         CData.getEnd()->dumpToStream(Out);
7399a08a3faSAdam Balogh       else
7409a08a3faSAdam Balogh         Out << "<Unknown>";
7419a08a3faSAdam Balogh       Out << " ]";
7429a08a3faSAdam Balogh     }
7439a08a3faSAdam Balogh   }
7449a08a3faSAdam Balogh }
7459a08a3faSAdam Balogh 
7469a08a3faSAdam Balogh namespace {
7479a08a3faSAdam Balogh 
isBeginCall(const FunctionDecl * Func)7489a08a3faSAdam Balogh bool isBeginCall(const FunctionDecl *Func) {
7499a08a3faSAdam Balogh   const auto *IdInfo = Func->getIdentifier();
7509a08a3faSAdam Balogh   if (!IdInfo)
7519a08a3faSAdam Balogh     return false;
752e5c7c171SMartin Storsjö   return IdInfo->getName().endswith_insensitive("begin");
7539a08a3faSAdam Balogh }
7549a08a3faSAdam Balogh 
isEndCall(const FunctionDecl * Func)7559a08a3faSAdam Balogh bool isEndCall(const FunctionDecl *Func) {
7569a08a3faSAdam Balogh   const auto *IdInfo = Func->getIdentifier();
7579a08a3faSAdam Balogh   if (!IdInfo)
7589a08a3faSAdam Balogh     return false;
759e5c7c171SMartin Storsjö   return IdInfo->getName().endswith_insensitive("end");
7609a08a3faSAdam Balogh }
7619a08a3faSAdam Balogh 
getCXXRecordDecl(ProgramStateRef State,const MemRegion * Reg)7629a08a3faSAdam Balogh const CXXRecordDecl *getCXXRecordDecl(ProgramStateRef State,
7639a08a3faSAdam Balogh                                       const MemRegion *Reg) {
7649a08a3faSAdam Balogh   auto TI = getDynamicTypeInfo(State, Reg);
7659a08a3faSAdam Balogh   if (!TI.isValid())
7669a08a3faSAdam Balogh     return nullptr;
7679a08a3faSAdam Balogh 
7689a08a3faSAdam Balogh   auto Type = TI.getType();
7699a08a3faSAdam Balogh   if (const auto *RefT = Type->getAs<ReferenceType>()) {
7709a08a3faSAdam Balogh     Type = RefT->getPointeeType();
7719a08a3faSAdam Balogh   }
7729a08a3faSAdam Balogh 
7739a08a3faSAdam Balogh   return Type->getUnqualifiedDesugaredType()->getAsCXXRecordDecl();
7749a08a3faSAdam Balogh }
7759a08a3faSAdam Balogh 
hasSubscriptOperator(ProgramStateRef State,const MemRegion * Reg)7769a08a3faSAdam Balogh bool hasSubscriptOperator(ProgramStateRef State, const MemRegion *Reg) {
7779a08a3faSAdam Balogh   const auto *CRD = getCXXRecordDecl(State, Reg);
7789a08a3faSAdam Balogh   if (!CRD)
7799a08a3faSAdam Balogh     return false;
7809a08a3faSAdam Balogh 
7819a08a3faSAdam Balogh   for (const auto *Method : CRD->methods()) {
7829a08a3faSAdam Balogh     if (!Method->isOverloadedOperator())
7839a08a3faSAdam Balogh       continue;
7849a08a3faSAdam Balogh     const auto OPK = Method->getOverloadedOperator();
7859a08a3faSAdam Balogh     if (OPK == OO_Subscript) {
7869a08a3faSAdam Balogh       return true;
7879a08a3faSAdam Balogh     }
7889a08a3faSAdam Balogh   }
7899a08a3faSAdam Balogh   return false;
7909a08a3faSAdam Balogh }
7919a08a3faSAdam Balogh 
frontModifiable(ProgramStateRef State,const MemRegion * Reg)7929a08a3faSAdam Balogh bool frontModifiable(ProgramStateRef State, const MemRegion *Reg) {
7939a08a3faSAdam Balogh   const auto *CRD = getCXXRecordDecl(State, Reg);
7949a08a3faSAdam Balogh   if (!CRD)
7959a08a3faSAdam Balogh     return false;
7969a08a3faSAdam Balogh 
7979a08a3faSAdam Balogh   for (const auto *Method : CRD->methods()) {
7989a08a3faSAdam Balogh     if (!Method->getDeclName().isIdentifier())
7999a08a3faSAdam Balogh       continue;
8009a08a3faSAdam Balogh     if (Method->getName() == "push_front" || Method->getName() == "pop_front") {
8019a08a3faSAdam Balogh       return true;
8029a08a3faSAdam Balogh     }
8039a08a3faSAdam Balogh   }
8049a08a3faSAdam Balogh   return false;
8059a08a3faSAdam Balogh }
8069a08a3faSAdam Balogh 
backModifiable(ProgramStateRef State,const MemRegion * Reg)8079a08a3faSAdam Balogh bool backModifiable(ProgramStateRef State, const MemRegion *Reg) {
8089a08a3faSAdam Balogh   const auto *CRD = getCXXRecordDecl(State, Reg);
8099a08a3faSAdam Balogh   if (!CRD)
8109a08a3faSAdam Balogh     return false;
8119a08a3faSAdam Balogh 
8129a08a3faSAdam Balogh   for (const auto *Method : CRD->methods()) {
8139a08a3faSAdam Balogh     if (!Method->getDeclName().isIdentifier())
8149a08a3faSAdam Balogh       continue;
8159a08a3faSAdam Balogh     if (Method->getName() == "push_back" || Method->getName() == "pop_back") {
8169a08a3faSAdam Balogh       return true;
8179a08a3faSAdam Balogh     }
8189a08a3faSAdam Balogh   }
8199a08a3faSAdam Balogh   return false;
8209a08a3faSAdam Balogh }
8219a08a3faSAdam Balogh 
getContainerBegin(ProgramStateRef State,const MemRegion * Cont)8229a08a3faSAdam Balogh SymbolRef getContainerBegin(ProgramStateRef State, const MemRegion *Cont) {
8239a08a3faSAdam Balogh   const auto *CDataPtr = getContainerData(State, Cont);
8249a08a3faSAdam Balogh   if (!CDataPtr)
8259a08a3faSAdam Balogh     return nullptr;
8269a08a3faSAdam Balogh 
8279a08a3faSAdam Balogh   return CDataPtr->getBegin();
8289a08a3faSAdam Balogh }
8299a08a3faSAdam Balogh 
getContainerEnd(ProgramStateRef State,const MemRegion * Cont)8309a08a3faSAdam Balogh SymbolRef getContainerEnd(ProgramStateRef State, const MemRegion *Cont) {
8319a08a3faSAdam Balogh   const auto *CDataPtr = getContainerData(State, Cont);
8329a08a3faSAdam Balogh   if (!CDataPtr)
8339a08a3faSAdam Balogh     return nullptr;
8349a08a3faSAdam Balogh 
8359a08a3faSAdam Balogh   return CDataPtr->getEnd();
8369a08a3faSAdam Balogh }
8379a08a3faSAdam Balogh 
createContainerBegin(ProgramStateRef State,const MemRegion * Cont,const Expr * E,QualType T,const LocationContext * LCtx,unsigned BlockCount)8389a08a3faSAdam Balogh ProgramStateRef createContainerBegin(ProgramStateRef State,
8399a08a3faSAdam Balogh                                      const MemRegion *Cont, const Expr *E,
8409a08a3faSAdam Balogh                                      QualType T, const LocationContext *LCtx,
8419a08a3faSAdam Balogh                                      unsigned BlockCount) {
8429a08a3faSAdam Balogh   // Only create if it does not exist
8439a08a3faSAdam Balogh   const auto *CDataPtr = getContainerData(State, Cont);
8449a08a3faSAdam Balogh   if (CDataPtr && CDataPtr->getBegin())
8459a08a3faSAdam Balogh     return State;
8469a08a3faSAdam Balogh 
8479a08a3faSAdam Balogh   auto &SymMgr = State->getSymbolManager();
8489a08a3faSAdam Balogh   const SymbolConjured *Sym = SymMgr.conjureSymbol(E, LCtx, T, BlockCount,
8499a08a3faSAdam Balogh                                                    "begin");
8509a08a3faSAdam Balogh   State = assumeNoOverflow(State, Sym, 4);
8519a08a3faSAdam Balogh 
8529a08a3faSAdam Balogh   if (CDataPtr) {
8539a08a3faSAdam Balogh     const auto CData = CDataPtr->newBegin(Sym);
8549a08a3faSAdam Balogh     return setContainerData(State, Cont, CData);
8559a08a3faSAdam Balogh   }
8569a08a3faSAdam Balogh 
8579a08a3faSAdam Balogh   const auto CData = ContainerData::fromBegin(Sym);
8589a08a3faSAdam Balogh   return setContainerData(State, Cont, CData);
8599a08a3faSAdam Balogh }
8609a08a3faSAdam Balogh 
createContainerEnd(ProgramStateRef State,const MemRegion * Cont,const Expr * E,QualType T,const LocationContext * LCtx,unsigned BlockCount)8619a08a3faSAdam Balogh ProgramStateRef createContainerEnd(ProgramStateRef State, const MemRegion *Cont,
8629a08a3faSAdam Balogh                                    const Expr *E, QualType T,
8639a08a3faSAdam Balogh                                    const LocationContext *LCtx,
8649a08a3faSAdam Balogh                                    unsigned BlockCount) {
8659a08a3faSAdam Balogh   // Only create if it does not exist
8669a08a3faSAdam Balogh   const auto *CDataPtr = getContainerData(State, Cont);
8679a08a3faSAdam Balogh   if (CDataPtr && CDataPtr->getEnd())
8689a08a3faSAdam Balogh     return State;
8699a08a3faSAdam Balogh 
8709a08a3faSAdam Balogh   auto &SymMgr = State->getSymbolManager();
8719a08a3faSAdam Balogh   const SymbolConjured *Sym = SymMgr.conjureSymbol(E, LCtx, T, BlockCount,
8729a08a3faSAdam Balogh                                                   "end");
8739a08a3faSAdam Balogh   State = assumeNoOverflow(State, Sym, 4);
8749a08a3faSAdam Balogh 
8759a08a3faSAdam Balogh   if (CDataPtr) {
8769a08a3faSAdam Balogh     const auto CData = CDataPtr->newEnd(Sym);
8779a08a3faSAdam Balogh     return setContainerData(State, Cont, CData);
8789a08a3faSAdam Balogh   }
8799a08a3faSAdam Balogh 
8809a08a3faSAdam Balogh   const auto CData = ContainerData::fromEnd(Sym);
8819a08a3faSAdam Balogh   return setContainerData(State, Cont, CData);
8829a08a3faSAdam Balogh }
8839a08a3faSAdam Balogh 
setContainerData(ProgramStateRef State,const MemRegion * Cont,const ContainerData & CData)8849a08a3faSAdam Balogh ProgramStateRef setContainerData(ProgramStateRef State, const MemRegion *Cont,
8859a08a3faSAdam Balogh                                  const ContainerData &CData) {
8869a08a3faSAdam Balogh   return State->set<ContainerMap>(Cont, CData);
8879a08a3faSAdam Balogh }
8889a08a3faSAdam Balogh 
8899a08a3faSAdam Balogh template <typename Condition, typename Process>
processIteratorPositions(ProgramStateRef State,Condition Cond,Process Proc)8909a08a3faSAdam Balogh ProgramStateRef processIteratorPositions(ProgramStateRef State, Condition Cond,
8919a08a3faSAdam Balogh                                          Process Proc) {
8929a08a3faSAdam Balogh   auto &RegionMapFactory = State->get_context<IteratorRegionMap>();
8939a08a3faSAdam Balogh   auto RegionMap = State->get<IteratorRegionMap>();
8949a08a3faSAdam Balogh   bool Changed = false;
8959a08a3faSAdam Balogh   for (const auto &Reg : RegionMap) {
8969a08a3faSAdam Balogh     if (Cond(Reg.second)) {
8979a08a3faSAdam Balogh       RegionMap = RegionMapFactory.add(RegionMap, Reg.first, Proc(Reg.second));
8989a08a3faSAdam Balogh       Changed = true;
8999a08a3faSAdam Balogh     }
9009a08a3faSAdam Balogh   }
9019a08a3faSAdam Balogh 
9029a08a3faSAdam Balogh   if (Changed)
9039a08a3faSAdam Balogh     State = State->set<IteratorRegionMap>(RegionMap);
9049a08a3faSAdam Balogh 
9059a08a3faSAdam Balogh   auto &SymbolMapFactory = State->get_context<IteratorSymbolMap>();
9069a08a3faSAdam Balogh   auto SymbolMap = State->get<IteratorSymbolMap>();
9079a08a3faSAdam Balogh   Changed = false;
9089a08a3faSAdam Balogh   for (const auto &Sym : SymbolMap) {
9099a08a3faSAdam Balogh     if (Cond(Sym.second)) {
9109a08a3faSAdam Balogh       SymbolMap = SymbolMapFactory.add(SymbolMap, Sym.first, Proc(Sym.second));
9119a08a3faSAdam Balogh       Changed = true;
9129a08a3faSAdam Balogh     }
9139a08a3faSAdam Balogh   }
9149a08a3faSAdam Balogh 
9159a08a3faSAdam Balogh   if (Changed)
9169a08a3faSAdam Balogh     State = State->set<IteratorSymbolMap>(SymbolMap);
9179a08a3faSAdam Balogh 
9189a08a3faSAdam Balogh   return State;
9199a08a3faSAdam Balogh }
9209a08a3faSAdam Balogh 
invalidateAllIteratorPositions(ProgramStateRef State,const MemRegion * Cont)9219a08a3faSAdam Balogh ProgramStateRef invalidateAllIteratorPositions(ProgramStateRef State,
9229a08a3faSAdam Balogh                                                const MemRegion *Cont) {
9239a08a3faSAdam Balogh   auto MatchCont = [&](const IteratorPosition &Pos) {
9249a08a3faSAdam Balogh     return Pos.getContainer() == Cont;
9259a08a3faSAdam Balogh   };
9269a08a3faSAdam Balogh   auto Invalidate = [&](const IteratorPosition &Pos) {
9279a08a3faSAdam Balogh     return Pos.invalidate();
9289a08a3faSAdam Balogh   };
9299a08a3faSAdam Balogh   return processIteratorPositions(State, MatchCont, Invalidate);
9309a08a3faSAdam Balogh }
9319a08a3faSAdam Balogh 
9329a08a3faSAdam Balogh ProgramStateRef
invalidateAllIteratorPositionsExcept(ProgramStateRef State,const MemRegion * Cont,SymbolRef Offset,BinaryOperator::Opcode Opc)9339a08a3faSAdam Balogh invalidateAllIteratorPositionsExcept(ProgramStateRef State,
9349a08a3faSAdam Balogh                                      const MemRegion *Cont, SymbolRef Offset,
9359a08a3faSAdam Balogh                                      BinaryOperator::Opcode Opc) {
9369a08a3faSAdam Balogh   auto MatchContAndCompare = [&](const IteratorPosition &Pos) {
9379a08a3faSAdam Balogh     return Pos.getContainer() == Cont &&
9389a08a3faSAdam Balogh            !compare(State, Pos.getOffset(), Offset, Opc);
9399a08a3faSAdam Balogh   };
9409a08a3faSAdam Balogh   auto Invalidate = [&](const IteratorPosition &Pos) {
9419a08a3faSAdam Balogh     return Pos.invalidate();
9429a08a3faSAdam Balogh   };
9439a08a3faSAdam Balogh   return processIteratorPositions(State, MatchContAndCompare, Invalidate);
9449a08a3faSAdam Balogh }
9459a08a3faSAdam Balogh 
invalidateIteratorPositions(ProgramStateRef State,SymbolRef Offset,BinaryOperator::Opcode Opc)9469a08a3faSAdam Balogh ProgramStateRef invalidateIteratorPositions(ProgramStateRef State,
9479a08a3faSAdam Balogh                                             SymbolRef Offset,
9489a08a3faSAdam Balogh                                             BinaryOperator::Opcode Opc) {
9499a08a3faSAdam Balogh   auto Compare = [&](const IteratorPosition &Pos) {
9509a08a3faSAdam Balogh     return compare(State, Pos.getOffset(), Offset, Opc);
9519a08a3faSAdam Balogh   };
9529a08a3faSAdam Balogh   auto Invalidate = [&](const IteratorPosition &Pos) {
9539a08a3faSAdam Balogh     return Pos.invalidate();
9549a08a3faSAdam Balogh   };
9559a08a3faSAdam Balogh   return processIteratorPositions(State, Compare, Invalidate);
9569a08a3faSAdam Balogh }
9579a08a3faSAdam Balogh 
invalidateIteratorPositions(ProgramStateRef State,SymbolRef Offset1,BinaryOperator::Opcode Opc1,SymbolRef Offset2,BinaryOperator::Opcode Opc2)9589a08a3faSAdam Balogh ProgramStateRef invalidateIteratorPositions(ProgramStateRef State,
9599a08a3faSAdam Balogh                                             SymbolRef Offset1,
9609a08a3faSAdam Balogh                                             BinaryOperator::Opcode Opc1,
9619a08a3faSAdam Balogh                                             SymbolRef Offset2,
9629a08a3faSAdam Balogh                                             BinaryOperator::Opcode Opc2) {
9639a08a3faSAdam Balogh   auto Compare = [&](const IteratorPosition &Pos) {
9649a08a3faSAdam Balogh     return compare(State, Pos.getOffset(), Offset1, Opc1) &&
9659a08a3faSAdam Balogh            compare(State, Pos.getOffset(), Offset2, Opc2);
9669a08a3faSAdam Balogh   };
9679a08a3faSAdam Balogh   auto Invalidate = [&](const IteratorPosition &Pos) {
9689a08a3faSAdam Balogh     return Pos.invalidate();
9699a08a3faSAdam Balogh   };
9709a08a3faSAdam Balogh   return processIteratorPositions(State, Compare, Invalidate);
9719a08a3faSAdam Balogh }
9729a08a3faSAdam Balogh 
reassignAllIteratorPositions(ProgramStateRef State,const MemRegion * Cont,const MemRegion * NewCont)9739a08a3faSAdam Balogh ProgramStateRef reassignAllIteratorPositions(ProgramStateRef State,
9749a08a3faSAdam Balogh                                              const MemRegion *Cont,
9759a08a3faSAdam Balogh                                              const MemRegion *NewCont) {
9769a08a3faSAdam Balogh   auto MatchCont = [&](const IteratorPosition &Pos) {
9779a08a3faSAdam Balogh     return Pos.getContainer() == Cont;
9789a08a3faSAdam Balogh   };
9799a08a3faSAdam Balogh   auto ReAssign = [&](const IteratorPosition &Pos) {
9809a08a3faSAdam Balogh     return Pos.reAssign(NewCont);
9819a08a3faSAdam Balogh   };
9829a08a3faSAdam Balogh   return processIteratorPositions(State, MatchCont, ReAssign);
9839a08a3faSAdam Balogh }
9849a08a3faSAdam Balogh 
reassignAllIteratorPositionsUnless(ProgramStateRef State,const MemRegion * Cont,const MemRegion * NewCont,SymbolRef Offset,BinaryOperator::Opcode Opc)9859a08a3faSAdam Balogh ProgramStateRef reassignAllIteratorPositionsUnless(ProgramStateRef State,
9869a08a3faSAdam Balogh                                                    const MemRegion *Cont,
9879a08a3faSAdam Balogh                                                    const MemRegion *NewCont,
9889a08a3faSAdam Balogh                                                    SymbolRef Offset,
9899a08a3faSAdam Balogh                                                    BinaryOperator::Opcode Opc) {
9909a08a3faSAdam Balogh   auto MatchContAndCompare = [&](const IteratorPosition &Pos) {
9919a08a3faSAdam Balogh     return Pos.getContainer() == Cont &&
9929a08a3faSAdam Balogh     !compare(State, Pos.getOffset(), Offset, Opc);
9939a08a3faSAdam Balogh   };
9949a08a3faSAdam Balogh   auto ReAssign = [&](const IteratorPosition &Pos) {
9959a08a3faSAdam Balogh     return Pos.reAssign(NewCont);
9969a08a3faSAdam Balogh   };
9979a08a3faSAdam Balogh   return processIteratorPositions(State, MatchContAndCompare, ReAssign);
9989a08a3faSAdam Balogh }
9999a08a3faSAdam Balogh 
10009a08a3faSAdam Balogh // This function rebases symbolic expression `OldSym + Int` to `NewSym + Int`,
10019a08a3faSAdam Balogh // `OldSym - Int` to `NewSym - Int` and  `OldSym` to `NewSym` in any iterator
10029a08a3faSAdam Balogh // position offsets where `CondSym` is true.
rebaseSymbolInIteratorPositionsIf(ProgramStateRef State,SValBuilder & SVB,SymbolRef OldSym,SymbolRef NewSym,SymbolRef CondSym,BinaryOperator::Opcode Opc)10039a08a3faSAdam Balogh ProgramStateRef rebaseSymbolInIteratorPositionsIf(
10049a08a3faSAdam Balogh     ProgramStateRef State, SValBuilder &SVB, SymbolRef OldSym,
10059a08a3faSAdam Balogh     SymbolRef NewSym, SymbolRef CondSym, BinaryOperator::Opcode Opc) {
10069a08a3faSAdam Balogh   auto LessThanEnd = [&](const IteratorPosition &Pos) {
10079a08a3faSAdam Balogh     return compare(State, Pos.getOffset(), CondSym, Opc);
10089a08a3faSAdam Balogh   };
10099a08a3faSAdam Balogh   auto RebaseSymbol = [&](const IteratorPosition &Pos) {
10109a08a3faSAdam Balogh     return Pos.setTo(rebaseSymbol(State, SVB, Pos.getOffset(), OldSym,
10119a08a3faSAdam Balogh                                    NewSym));
10129a08a3faSAdam Balogh   };
10139a08a3faSAdam Balogh   return processIteratorPositions(State, LessThanEnd, RebaseSymbol);
10149a08a3faSAdam Balogh }
10159a08a3faSAdam Balogh 
10169a08a3faSAdam Balogh // This function rebases symbolic expression `OldExpr + Int` to `NewExpr + Int`,
10179a08a3faSAdam Balogh // `OldExpr - Int` to `NewExpr - Int` and  `OldExpr` to `NewExpr` in expression
10189a08a3faSAdam Balogh // `OrigExpr`.
rebaseSymbol(ProgramStateRef State,SValBuilder & SVB,SymbolRef OrigExpr,SymbolRef OldExpr,SymbolRef NewSym)10199a08a3faSAdam Balogh SymbolRef rebaseSymbol(ProgramStateRef State, SValBuilder &SVB,
10209a08a3faSAdam Balogh                        SymbolRef OrigExpr, SymbolRef OldExpr,
10219a08a3faSAdam Balogh                        SymbolRef NewSym) {
10229a08a3faSAdam Balogh   auto &SymMgr = SVB.getSymbolManager();
10239a08a3faSAdam Balogh   auto Diff = SVB.evalBinOpNN(State, BO_Sub, nonloc::SymbolVal(OrigExpr),
10249a08a3faSAdam Balogh                               nonloc::SymbolVal(OldExpr),
10259a08a3faSAdam Balogh                               SymMgr.getType(OrigExpr));
10269a08a3faSAdam Balogh 
10279a08a3faSAdam Balogh   const auto DiffInt = Diff.getAs<nonloc::ConcreteInt>();
10289a08a3faSAdam Balogh   if (!DiffInt)
10299a08a3faSAdam Balogh     return OrigExpr;
10309a08a3faSAdam Balogh 
10319a08a3faSAdam Balogh   return SVB.evalBinOpNN(State, BO_Add, *DiffInt, nonloc::SymbolVal(NewSym),
10329a08a3faSAdam Balogh                          SymMgr.getType(OrigExpr)).getAsSymbol();
10339a08a3faSAdam Balogh }
10349a08a3faSAdam Balogh 
hasLiveIterators(ProgramStateRef State,const MemRegion * Cont)10359a08a3faSAdam Balogh bool hasLiveIterators(ProgramStateRef State, const MemRegion *Cont) {
10369a08a3faSAdam Balogh   auto RegionMap = State->get<IteratorRegionMap>();
10379a08a3faSAdam Balogh   for (const auto &Reg : RegionMap) {
10389a08a3faSAdam Balogh     if (Reg.second.getContainer() == Cont)
10399a08a3faSAdam Balogh       return true;
10409a08a3faSAdam Balogh   }
10419a08a3faSAdam Balogh 
10429a08a3faSAdam Balogh   auto SymbolMap = State->get<IteratorSymbolMap>();
10439a08a3faSAdam Balogh   for (const auto &Sym : SymbolMap) {
10449a08a3faSAdam Balogh     if (Sym.second.getContainer() == Cont)
10459a08a3faSAdam Balogh       return true;
10469a08a3faSAdam Balogh   }
10479a08a3faSAdam Balogh 
10489a08a3faSAdam Balogh   return false;
10499a08a3faSAdam Balogh }
10509a08a3faSAdam Balogh 
10519a08a3faSAdam Balogh } // namespace
10529a08a3faSAdam Balogh 
registerContainerModeling(CheckerManager & mgr)10539a08a3faSAdam Balogh void ento::registerContainerModeling(CheckerManager &mgr) {
10549a08a3faSAdam Balogh   mgr.registerChecker<ContainerModeling>();
10559a08a3faSAdam Balogh }
10569a08a3faSAdam Balogh 
shouldRegisterContainerModeling(const CheckerManager & mgr)1057bda3dd0dSKirstóf Umann bool ento::shouldRegisterContainerModeling(const CheckerManager &mgr) {
1058afcb77ccSAdam Balogh   if (!mgr.getLangOpts().CPlusPlus)
1059afcb77ccSAdam Balogh     return false;
1060afcb77ccSAdam Balogh 
1061afcb77ccSAdam Balogh   if (!mgr.getAnalyzerOptions().ShouldAggressivelySimplifyBinaryOperation) {
1062afcb77ccSAdam Balogh     mgr.getASTContext().getDiagnostics().Report(
1063afcb77ccSAdam Balogh         diag::err_analyzer_checker_incompatible_analyzer_option)
1064afcb77ccSAdam Balogh       << "aggressive-binary-operation-simplification" << "false";
1065afcb77ccSAdam Balogh     return false;
1066afcb77ccSAdam Balogh   }
1067afcb77ccSAdam Balogh 
10689a08a3faSAdam Balogh   return true;
10699a08a3faSAdam Balogh }
1070