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