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*06eedffeSNagyDonat {{CDM::CXXMethod, {"clear"}, 0}, &ContainerModeling::handleClear},
76*06eedffeSNagyDonat {{CDM::CXXMethod, {"assign"}, 2}, &ContainerModeling::handleAssign},
77*06eedffeSNagyDonat {{CDM::CXXMethod, {"push_back"}, 1}, &ContainerModeling::handlePushBack},
78*06eedffeSNagyDonat {{CDM::CXXMethod, {"emplace_back"}, 1},
79*06eedffeSNagyDonat &ContainerModeling::handlePushBack},
80*06eedffeSNagyDonat {{CDM::CXXMethod, {"pop_back"}, 0}, &ContainerModeling::handlePopBack},
81*06eedffeSNagyDonat {{CDM::CXXMethod, {"push_front"}, 1},
82*06eedffeSNagyDonat &ContainerModeling::handlePushFront},
83*06eedffeSNagyDonat {{CDM::CXXMethod, {"emplace_front"}, 1},
84*06eedffeSNagyDonat &ContainerModeling::handlePushFront},
85*06eedffeSNagyDonat {{CDM::CXXMethod, {"pop_front"}, 0}, &ContainerModeling::handlePopFront},
869a08a3faSAdam Balogh };
879a08a3faSAdam Balogh
889a08a3faSAdam Balogh CallDescriptionMap<OneItParamFn> OneIterParamFunctions = {
89*06eedffeSNagyDonat {{CDM::CXXMethod, {"insert"}, 2}, &ContainerModeling::handleInsert},
90*06eedffeSNagyDonat {{CDM::CXXMethod, {"emplace"}, 2}, &ContainerModeling::handleInsert},
91*06eedffeSNagyDonat {{CDM::CXXMethod, {"erase"}, 1}, &ContainerModeling::handleErase},
92*06eedffeSNagyDonat {{CDM::CXXMethod, {"erase_after"}, 1},
93*06eedffeSNagyDonat &ContainerModeling::handleEraseAfter},
949a08a3faSAdam Balogh };
959a08a3faSAdam Balogh
969a08a3faSAdam Balogh CallDescriptionMap<TwoItParamFn> TwoIterParamFunctions = {
97*06eedffeSNagyDonat {{CDM::CXXMethod, {"erase"}, 2}, &ContainerModeling::handleErase},
98*06eedffeSNagyDonat {{CDM::CXXMethod, {"erase_after"}, 2},
99*06eedffeSNagyDonat &ContainerModeling::handleEraseAfter},
1009a08a3faSAdam Balogh };
1019a08a3faSAdam Balogh };
1029a08a3faSAdam Balogh
1039a08a3faSAdam Balogh bool isBeginCall(const FunctionDecl *Func);
1049a08a3faSAdam Balogh bool isEndCall(const FunctionDecl *Func);
1059a08a3faSAdam Balogh bool hasSubscriptOperator(ProgramStateRef State, const MemRegion *Reg);
1069a08a3faSAdam Balogh bool frontModifiable(ProgramStateRef State, const MemRegion *Reg);
1079a08a3faSAdam Balogh bool backModifiable(ProgramStateRef State, const MemRegion *Reg);
1089a08a3faSAdam Balogh SymbolRef getContainerBegin(ProgramStateRef State, const MemRegion *Cont);
1099a08a3faSAdam Balogh SymbolRef getContainerEnd(ProgramStateRef State, const MemRegion *Cont);
1109a08a3faSAdam Balogh ProgramStateRef createContainerBegin(ProgramStateRef State,
1119a08a3faSAdam Balogh const MemRegion *Cont, const Expr *E,
1129a08a3faSAdam Balogh QualType T, const LocationContext *LCtx,
1139a08a3faSAdam Balogh unsigned BlockCount);
1149a08a3faSAdam Balogh ProgramStateRef createContainerEnd(ProgramStateRef State, const MemRegion *Cont,
1159a08a3faSAdam Balogh const Expr *E, QualType T,
1169a08a3faSAdam Balogh const LocationContext *LCtx,
1179a08a3faSAdam Balogh unsigned BlockCount);
1189a08a3faSAdam Balogh ProgramStateRef setContainerData(ProgramStateRef State, const MemRegion *Cont,
1199a08a3faSAdam Balogh const ContainerData &CData);
1209a08a3faSAdam Balogh ProgramStateRef invalidateAllIteratorPositions(ProgramStateRef State,
1219a08a3faSAdam Balogh const MemRegion *Cont);
1229a08a3faSAdam Balogh ProgramStateRef
1239a08a3faSAdam Balogh invalidateAllIteratorPositionsExcept(ProgramStateRef State,
1249a08a3faSAdam Balogh const MemRegion *Cont, SymbolRef Offset,
1259a08a3faSAdam Balogh BinaryOperator::Opcode Opc);
1269a08a3faSAdam Balogh ProgramStateRef invalidateIteratorPositions(ProgramStateRef State,
1279a08a3faSAdam Balogh SymbolRef Offset,
1289a08a3faSAdam Balogh BinaryOperator::Opcode Opc);
1299a08a3faSAdam Balogh ProgramStateRef invalidateIteratorPositions(ProgramStateRef State,
1309a08a3faSAdam Balogh SymbolRef Offset1,
1319a08a3faSAdam Balogh BinaryOperator::Opcode Opc1,
1329a08a3faSAdam Balogh SymbolRef Offset2,
1339a08a3faSAdam Balogh BinaryOperator::Opcode Opc2);
1349a08a3faSAdam Balogh ProgramStateRef reassignAllIteratorPositions(ProgramStateRef State,
1359a08a3faSAdam Balogh const MemRegion *Cont,
1369a08a3faSAdam Balogh const MemRegion *NewCont);
1379a08a3faSAdam Balogh ProgramStateRef reassignAllIteratorPositionsUnless(ProgramStateRef State,
1389a08a3faSAdam Balogh const MemRegion *Cont,
1399a08a3faSAdam Balogh const MemRegion *NewCont,
1409a08a3faSAdam Balogh SymbolRef Offset,
1419a08a3faSAdam Balogh BinaryOperator::Opcode Opc);
1429a08a3faSAdam Balogh ProgramStateRef rebaseSymbolInIteratorPositionsIf(
1439a08a3faSAdam Balogh ProgramStateRef State, SValBuilder &SVB, SymbolRef OldSym,
1449a08a3faSAdam Balogh SymbolRef NewSym, SymbolRef CondSym, BinaryOperator::Opcode Opc);
1459a08a3faSAdam Balogh SymbolRef rebaseSymbol(ProgramStateRef State, SValBuilder &SVB, SymbolRef Expr,
1469a08a3faSAdam Balogh SymbolRef OldSym, SymbolRef NewSym);
1479a08a3faSAdam Balogh bool hasLiveIterators(ProgramStateRef State, const MemRegion *Cont);
1489a08a3faSAdam Balogh
1499a08a3faSAdam Balogh } // namespace
1509a08a3faSAdam Balogh
checkPostCall(const CallEvent & Call,CheckerContext & C) const1519a08a3faSAdam Balogh void ContainerModeling::checkPostCall(const CallEvent &Call,
1529a08a3faSAdam Balogh CheckerContext &C) const {
1539a08a3faSAdam Balogh const auto *Func = dyn_cast_or_null<FunctionDecl>(Call.getDecl());
1549a08a3faSAdam Balogh if (!Func)
1559a08a3faSAdam Balogh return;
1569a08a3faSAdam Balogh
1579a08a3faSAdam Balogh if (Func->isOverloadedOperator()) {
1589a08a3faSAdam Balogh const auto Op = Func->getOverloadedOperator();
1599a08a3faSAdam Balogh if (Op == OO_Equal) {
1609a08a3faSAdam Balogh // Overloaded 'operator=' must be a non-static member function.
1619a08a3faSAdam Balogh const auto *InstCall = cast<CXXInstanceCall>(&Call);
1629a08a3faSAdam Balogh if (cast<CXXMethodDecl>(Func)->isMoveAssignmentOperator()) {
1639a08a3faSAdam Balogh handleAssignment(C, InstCall->getCXXThisVal(), Call.getOriginExpr(),
1649a08a3faSAdam Balogh Call.getArgSVal(0));
1659a08a3faSAdam Balogh return;
1669a08a3faSAdam Balogh }
1679a08a3faSAdam Balogh
1689a08a3faSAdam Balogh handleAssignment(C, InstCall->getCXXThisVal());
1699a08a3faSAdam Balogh return;
1709a08a3faSAdam Balogh }
1719a08a3faSAdam Balogh } else {
1729a08a3faSAdam Balogh if (const auto *InstCall = dyn_cast<CXXInstanceCall>(&Call)) {
1739a08a3faSAdam Balogh const NoItParamFn *Handler0 = NoIterParamFunctions.lookup(Call);
1749a08a3faSAdam Balogh if (Handler0) {
175a3f4d17aSAdam Balogh (this->**Handler0)(C, InstCall->getCXXThisVal(),
176a3f4d17aSAdam Balogh InstCall->getCXXThisExpr());
1779a08a3faSAdam Balogh return;
1789a08a3faSAdam Balogh }
1799a08a3faSAdam Balogh
1809a08a3faSAdam Balogh const OneItParamFn *Handler1 = OneIterParamFunctions.lookup(Call);
1819a08a3faSAdam Balogh if (Handler1) {
1829a08a3faSAdam Balogh (this->**Handler1)(C, InstCall->getCXXThisVal(), Call.getArgSVal(0));
1839a08a3faSAdam Balogh return;
1849a08a3faSAdam Balogh }
1859a08a3faSAdam Balogh
1869a08a3faSAdam Balogh const TwoItParamFn *Handler2 = TwoIterParamFunctions.lookup(Call);
1879a08a3faSAdam Balogh if (Handler2) {
1889a08a3faSAdam Balogh (this->**Handler2)(C, InstCall->getCXXThisVal(), Call.getArgSVal(0),
1899a08a3faSAdam Balogh Call.getArgSVal(1));
1909a08a3faSAdam Balogh return;
1919a08a3faSAdam Balogh }
1929a08a3faSAdam Balogh
1939a08a3faSAdam Balogh const auto *OrigExpr = Call.getOriginExpr();
1949a08a3faSAdam Balogh if (!OrigExpr)
1959a08a3faSAdam Balogh return;
1969a08a3faSAdam Balogh
1979a08a3faSAdam Balogh if (isBeginCall(Func)) {
1989a08a3faSAdam Balogh handleBegin(C, OrigExpr, Call.getReturnValue(),
1999a08a3faSAdam Balogh InstCall->getCXXThisVal());
2009a08a3faSAdam Balogh return;
2019a08a3faSAdam Balogh }
2029a08a3faSAdam Balogh
2039a08a3faSAdam Balogh if (isEndCall(Func)) {
2049a08a3faSAdam Balogh handleEnd(C, OrigExpr, Call.getReturnValue(),
2059a08a3faSAdam Balogh InstCall->getCXXThisVal());
2069a08a3faSAdam Balogh return;
2079a08a3faSAdam Balogh }
2089a08a3faSAdam Balogh }
2099a08a3faSAdam Balogh }
2109a08a3faSAdam Balogh }
2119a08a3faSAdam Balogh
checkLiveSymbols(ProgramStateRef State,SymbolReaper & SR) const2129a08a3faSAdam Balogh void ContainerModeling::checkLiveSymbols(ProgramStateRef State,
2139a08a3faSAdam Balogh SymbolReaper &SR) const {
2149a08a3faSAdam Balogh // Keep symbolic expressions of container begins and ends alive
2159a08a3faSAdam Balogh auto ContMap = State->get<ContainerMap>();
2169a08a3faSAdam Balogh for (const auto &Cont : ContMap) {
2179a08a3faSAdam Balogh const auto CData = Cont.second;
2189a08a3faSAdam Balogh if (CData.getBegin()) {
2199a08a3faSAdam Balogh SR.markLive(CData.getBegin());
2209a08a3faSAdam Balogh if(const auto *SIE = dyn_cast<SymIntExpr>(CData.getBegin()))
2219a08a3faSAdam Balogh SR.markLive(SIE->getLHS());
2229a08a3faSAdam Balogh }
2239a08a3faSAdam Balogh if (CData.getEnd()) {
2249a08a3faSAdam Balogh SR.markLive(CData.getEnd());
2259a08a3faSAdam Balogh if(const auto *SIE = dyn_cast<SymIntExpr>(CData.getEnd()))
2269a08a3faSAdam Balogh SR.markLive(SIE->getLHS());
2279a08a3faSAdam Balogh }
2289a08a3faSAdam Balogh }
2299a08a3faSAdam Balogh }
2309a08a3faSAdam Balogh
checkDeadSymbols(SymbolReaper & SR,CheckerContext & C) const2319a08a3faSAdam Balogh void ContainerModeling::checkDeadSymbols(SymbolReaper &SR,
2329a08a3faSAdam Balogh CheckerContext &C) const {
2339a08a3faSAdam Balogh // Cleanup
2349a08a3faSAdam Balogh auto State = C.getState();
2359a08a3faSAdam Balogh
2369a08a3faSAdam Balogh auto ContMap = State->get<ContainerMap>();
2379a08a3faSAdam Balogh for (const auto &Cont : ContMap) {
2389a08a3faSAdam Balogh if (!SR.isLiveRegion(Cont.first)) {
2399a08a3faSAdam Balogh // We must keep the container data while it has live iterators to be able
2409a08a3faSAdam Balogh // to compare them to the begin and the end of the container.
2419a08a3faSAdam Balogh if (!hasLiveIterators(State, Cont.first)) {
2429a08a3faSAdam Balogh State = State->remove<ContainerMap>(Cont.first);
2439a08a3faSAdam Balogh }
2449a08a3faSAdam Balogh }
2459a08a3faSAdam Balogh }
2469a08a3faSAdam Balogh
2479a08a3faSAdam Balogh C.addTransition(State);
2489a08a3faSAdam Balogh }
2499a08a3faSAdam Balogh
handleBegin(CheckerContext & C,const Expr * CE,SVal RetVal,SVal Cont) const2509a08a3faSAdam Balogh void ContainerModeling::handleBegin(CheckerContext &C, const Expr *CE,
251a3f4d17aSAdam Balogh SVal RetVal, SVal Cont) const {
2529a08a3faSAdam Balogh const auto *ContReg = Cont.getAsRegion();
2539a08a3faSAdam Balogh if (!ContReg)
2549a08a3faSAdam Balogh return;
2559a08a3faSAdam Balogh
2569a08a3faSAdam Balogh ContReg = ContReg->getMostDerivedObjectRegion();
2579a08a3faSAdam Balogh
2589a08a3faSAdam Balogh // If the container already has a begin symbol then use it. Otherwise first
2599a08a3faSAdam Balogh // create a new one.
2609a08a3faSAdam Balogh auto State = C.getState();
2619a08a3faSAdam Balogh auto BeginSym = getContainerBegin(State, ContReg);
2629a08a3faSAdam Balogh if (!BeginSym) {
2639a08a3faSAdam Balogh State = createContainerBegin(State, ContReg, CE, C.getASTContext().LongTy,
2649a08a3faSAdam Balogh C.getLocationContext(), C.blockCount());
2659a08a3faSAdam Balogh BeginSym = getContainerBegin(State, ContReg);
2669a08a3faSAdam Balogh }
2679a08a3faSAdam Balogh State = setIteratorPosition(State, RetVal,
2689a08a3faSAdam Balogh IteratorPosition::getPosition(ContReg, BeginSym));
2699a08a3faSAdam Balogh C.addTransition(State);
2709a08a3faSAdam Balogh }
2719a08a3faSAdam Balogh
handleEnd(CheckerContext & C,const Expr * CE,SVal RetVal,SVal Cont) const2729a08a3faSAdam Balogh void ContainerModeling::handleEnd(CheckerContext &C, const Expr *CE,
273a3f4d17aSAdam Balogh SVal RetVal, SVal Cont) const {
2749a08a3faSAdam Balogh const auto *ContReg = Cont.getAsRegion();
2759a08a3faSAdam Balogh if (!ContReg)
2769a08a3faSAdam Balogh return;
2779a08a3faSAdam Balogh
2789a08a3faSAdam Balogh ContReg = ContReg->getMostDerivedObjectRegion();
2799a08a3faSAdam Balogh
2809a08a3faSAdam Balogh // If the container already has an end symbol then use it. Otherwise first
2819a08a3faSAdam Balogh // create a new one.
2829a08a3faSAdam Balogh auto State = C.getState();
2839a08a3faSAdam Balogh auto EndSym = getContainerEnd(State, ContReg);
2849a08a3faSAdam Balogh if (!EndSym) {
2859a08a3faSAdam Balogh State = createContainerEnd(State, ContReg, CE, C.getASTContext().LongTy,
2869a08a3faSAdam Balogh C.getLocationContext(), C.blockCount());
2879a08a3faSAdam Balogh EndSym = getContainerEnd(State, ContReg);
2889a08a3faSAdam Balogh }
2899a08a3faSAdam Balogh State = setIteratorPosition(State, RetVal,
2909a08a3faSAdam Balogh IteratorPosition::getPosition(ContReg, EndSym));
2919a08a3faSAdam Balogh C.addTransition(State);
2929a08a3faSAdam Balogh }
2939a08a3faSAdam Balogh
handleAssignment(CheckerContext & C,SVal Cont,const Expr * CE,SVal OldCont) const294a3f4d17aSAdam Balogh void ContainerModeling::handleAssignment(CheckerContext &C, SVal Cont,
295a3f4d17aSAdam Balogh const Expr *CE, SVal OldCont) const {
2969a08a3faSAdam Balogh const auto *ContReg = Cont.getAsRegion();
2979a08a3faSAdam Balogh if (!ContReg)
2989a08a3faSAdam Balogh return;
2999a08a3faSAdam Balogh
3009a08a3faSAdam Balogh ContReg = ContReg->getMostDerivedObjectRegion();
3019a08a3faSAdam Balogh
3029a08a3faSAdam Balogh // Assignment of a new value to a container always invalidates all its
3039a08a3faSAdam Balogh // iterators
3049a08a3faSAdam Balogh auto State = C.getState();
3059a08a3faSAdam Balogh const auto CData = getContainerData(State, ContReg);
3069a08a3faSAdam Balogh if (CData) {
3079a08a3faSAdam Balogh State = invalidateAllIteratorPositions(State, ContReg);
3089a08a3faSAdam Balogh }
3099a08a3faSAdam Balogh
3109a08a3faSAdam Balogh // In case of move, iterators of the old container (except the past-end
3119a08a3faSAdam Balogh // iterators) remain valid but refer to the new container
3129a08a3faSAdam Balogh if (!OldCont.isUndef()) {
3139a08a3faSAdam Balogh const auto *OldContReg = OldCont.getAsRegion();
3149a08a3faSAdam Balogh if (OldContReg) {
3159a08a3faSAdam Balogh OldContReg = OldContReg->getMostDerivedObjectRegion();
3169a08a3faSAdam Balogh const auto OldCData = getContainerData(State, OldContReg);
3179a08a3faSAdam Balogh if (OldCData) {
3189a08a3faSAdam Balogh if (const auto OldEndSym = OldCData->getEnd()) {
3199a08a3faSAdam Balogh // If we already assigned an "end" symbol to the old container, then
3209a08a3faSAdam Balogh // first reassign all iterator positions to the new container which
3219a08a3faSAdam Balogh // are not past the container (thus not greater or equal to the
3229a08a3faSAdam Balogh // current "end" symbol).
3239a08a3faSAdam Balogh State = reassignAllIteratorPositionsUnless(State, OldContReg, ContReg,
3249a08a3faSAdam Balogh OldEndSym, BO_GE);
3259a08a3faSAdam Balogh auto &SymMgr = C.getSymbolManager();
3269a08a3faSAdam Balogh auto &SVB = C.getSValBuilder();
3279a08a3faSAdam Balogh // Then generate and assign a new "end" symbol for the new container.
3289a08a3faSAdam Balogh auto NewEndSym =
3299a08a3faSAdam Balogh SymMgr.conjureSymbol(CE, C.getLocationContext(),
3309a08a3faSAdam Balogh C.getASTContext().LongTy, C.blockCount());
3319a08a3faSAdam Balogh State = assumeNoOverflow(State, NewEndSym, 4);
3329a08a3faSAdam Balogh if (CData) {
3339a08a3faSAdam Balogh State = setContainerData(State, ContReg, CData->newEnd(NewEndSym));
3349a08a3faSAdam Balogh } else {
3359a08a3faSAdam Balogh State = setContainerData(State, ContReg,
3369a08a3faSAdam Balogh ContainerData::fromEnd(NewEndSym));
3379a08a3faSAdam Balogh }
3389a08a3faSAdam Balogh // Finally, replace the old "end" symbol in the already reassigned
3399a08a3faSAdam Balogh // iterator positions with the new "end" symbol.
3409a08a3faSAdam Balogh State = rebaseSymbolInIteratorPositionsIf(
3419a08a3faSAdam Balogh State, SVB, OldEndSym, NewEndSym, OldEndSym, BO_LT);
3429a08a3faSAdam Balogh } else {
3439a08a3faSAdam Balogh // There was no "end" symbol assigned yet to the old container,
3449a08a3faSAdam Balogh // so reassign all iterator positions to the new container.
3459a08a3faSAdam Balogh State = reassignAllIteratorPositions(State, OldContReg, ContReg);
3469a08a3faSAdam Balogh }
3479a08a3faSAdam Balogh if (const auto OldBeginSym = OldCData->getBegin()) {
3489a08a3faSAdam Balogh // If we already assigned a "begin" symbol to the old container, then
3499a08a3faSAdam Balogh // assign it to the new container and remove it from the old one.
3509a08a3faSAdam Balogh if (CData) {
3519a08a3faSAdam Balogh State =
3529a08a3faSAdam Balogh setContainerData(State, ContReg, CData->newBegin(OldBeginSym));
3539a08a3faSAdam Balogh } else {
3549a08a3faSAdam Balogh State = setContainerData(State, ContReg,
3559a08a3faSAdam Balogh ContainerData::fromBegin(OldBeginSym));
3569a08a3faSAdam Balogh }
3579a08a3faSAdam Balogh State =
3589a08a3faSAdam Balogh setContainerData(State, OldContReg, OldCData->newBegin(nullptr));
3599a08a3faSAdam Balogh }
3609a08a3faSAdam Balogh } else {
3619a08a3faSAdam Balogh // There was neither "begin" nor "end" symbol assigned yet to the old
3629a08a3faSAdam Balogh // container, so reassign all iterator positions to the new container.
3639a08a3faSAdam Balogh State = reassignAllIteratorPositions(State, OldContReg, ContReg);
3649a08a3faSAdam Balogh }
3659a08a3faSAdam Balogh }
3669a08a3faSAdam Balogh }
3679a08a3faSAdam Balogh C.addTransition(State);
3689a08a3faSAdam Balogh }
3699a08a3faSAdam Balogh
handleAssign(CheckerContext & C,SVal Cont,const Expr * ContE) const370a3f4d17aSAdam Balogh void ContainerModeling::handleAssign(CheckerContext &C, SVal Cont,
371a3f4d17aSAdam Balogh const Expr *ContE) const {
3729a08a3faSAdam Balogh const auto *ContReg = Cont.getAsRegion();
3739a08a3faSAdam Balogh if (!ContReg)
3749a08a3faSAdam Balogh return;
3759a08a3faSAdam Balogh
3769a08a3faSAdam Balogh ContReg = ContReg->getMostDerivedObjectRegion();
3779a08a3faSAdam Balogh
3789a08a3faSAdam Balogh // The assign() operation invalidates all the iterators
3799a08a3faSAdam Balogh auto State = C.getState();
3809a08a3faSAdam Balogh State = invalidateAllIteratorPositions(State, ContReg);
3819a08a3faSAdam Balogh C.addTransition(State);
3829a08a3faSAdam Balogh }
3839a08a3faSAdam Balogh
handleClear(CheckerContext & C,SVal Cont,const Expr * ContE) const384a3f4d17aSAdam Balogh void ContainerModeling::handleClear(CheckerContext &C, SVal Cont,
385a3f4d17aSAdam Balogh const Expr *ContE) const {
3869a08a3faSAdam Balogh const auto *ContReg = Cont.getAsRegion();
3879a08a3faSAdam Balogh if (!ContReg)
3889a08a3faSAdam Balogh return;
3899a08a3faSAdam Balogh
3909a08a3faSAdam Balogh ContReg = ContReg->getMostDerivedObjectRegion();
3919a08a3faSAdam Balogh
3929a08a3faSAdam Balogh // The clear() operation invalidates all the iterators, except the past-end
3939a08a3faSAdam Balogh // iterators of list-like containers
3949a08a3faSAdam Balogh auto State = C.getState();
3959a08a3faSAdam Balogh if (!hasSubscriptOperator(State, ContReg) ||
3969a08a3faSAdam Balogh !backModifiable(State, ContReg)) {
3979a08a3faSAdam Balogh const auto CData = getContainerData(State, ContReg);
3989a08a3faSAdam Balogh if (CData) {
3999a08a3faSAdam Balogh if (const auto EndSym = CData->getEnd()) {
4009a08a3faSAdam Balogh State =
4019a08a3faSAdam Balogh invalidateAllIteratorPositionsExcept(State, ContReg, EndSym, BO_GE);
4029a08a3faSAdam Balogh C.addTransition(State);
4039a08a3faSAdam Balogh return;
4049a08a3faSAdam Balogh }
4059a08a3faSAdam Balogh }
4069a08a3faSAdam Balogh }
407a3f4d17aSAdam Balogh const NoteTag *ChangeTag =
408a3f4d17aSAdam Balogh getChangeTag(C, "became empty", ContReg, ContE);
4099a08a3faSAdam Balogh State = invalidateAllIteratorPositions(State, ContReg);
410a3f4d17aSAdam Balogh C.addTransition(State, ChangeTag);
4119a08a3faSAdam Balogh }
4129a08a3faSAdam Balogh
handlePushBack(CheckerContext & C,SVal Cont,const Expr * ContE) const413a3f4d17aSAdam Balogh void ContainerModeling::handlePushBack(CheckerContext &C, SVal Cont,
414a3f4d17aSAdam Balogh const Expr *ContE) const {
4159a08a3faSAdam Balogh const auto *ContReg = Cont.getAsRegion();
4169a08a3faSAdam Balogh if (!ContReg)
4179a08a3faSAdam Balogh return;
4189a08a3faSAdam Balogh
4199a08a3faSAdam Balogh ContReg = ContReg->getMostDerivedObjectRegion();
4209a08a3faSAdam Balogh
4219a08a3faSAdam Balogh // For deque-like containers invalidate all iterator positions
4229a08a3faSAdam Balogh auto State = C.getState();
4239a08a3faSAdam Balogh if (hasSubscriptOperator(State, ContReg) && frontModifiable(State, ContReg)) {
4249a08a3faSAdam Balogh State = invalidateAllIteratorPositions(State, ContReg);
4259a08a3faSAdam Balogh C.addTransition(State);
4269a08a3faSAdam Balogh return;
4279a08a3faSAdam Balogh }
4289a08a3faSAdam Balogh
4299a08a3faSAdam Balogh const auto CData = getContainerData(State, ContReg);
4309a08a3faSAdam Balogh if (!CData)
4319a08a3faSAdam Balogh return;
4329a08a3faSAdam Balogh
4339a08a3faSAdam Balogh // For vector-like containers invalidate the past-end iterator positions
4349a08a3faSAdam Balogh if (const auto EndSym = CData->getEnd()) {
4359a08a3faSAdam Balogh if (hasSubscriptOperator(State, ContReg)) {
4369a08a3faSAdam Balogh State = invalidateIteratorPositions(State, EndSym, BO_GE);
4379a08a3faSAdam Balogh }
4389a08a3faSAdam Balogh auto &SymMgr = C.getSymbolManager();
4399a08a3faSAdam Balogh auto &BVF = SymMgr.getBasicVals();
4409a08a3faSAdam Balogh auto &SVB = C.getSValBuilder();
4419a08a3faSAdam Balogh const auto newEndSym =
4429a08a3faSAdam Balogh SVB.evalBinOp(State, BO_Add,
4439a08a3faSAdam Balogh nonloc::SymbolVal(EndSym),
4449a08a3faSAdam Balogh nonloc::ConcreteInt(BVF.getValue(llvm::APSInt::get(1))),
4459a08a3faSAdam Balogh SymMgr.getType(EndSym)).getAsSymbol();
446a3f4d17aSAdam Balogh const NoteTag *ChangeTag =
447a3f4d17aSAdam Balogh getChangeTag(C, "extended to the back by 1 position", ContReg, ContE);
4489a08a3faSAdam Balogh State = setContainerData(State, ContReg, CData->newEnd(newEndSym));
449a3f4d17aSAdam Balogh C.addTransition(State, ChangeTag);
4509a08a3faSAdam Balogh }
4519a08a3faSAdam Balogh }
4529a08a3faSAdam Balogh
handlePopBack(CheckerContext & C,SVal Cont,const Expr * ContE) const453a3f4d17aSAdam Balogh void ContainerModeling::handlePopBack(CheckerContext &C, SVal Cont,
454a3f4d17aSAdam Balogh const Expr *ContE) const {
4559a08a3faSAdam Balogh const auto *ContReg = Cont.getAsRegion();
4569a08a3faSAdam Balogh if (!ContReg)
4579a08a3faSAdam Balogh return;
4589a08a3faSAdam Balogh
4599a08a3faSAdam Balogh ContReg = ContReg->getMostDerivedObjectRegion();
4609a08a3faSAdam Balogh
4619a08a3faSAdam Balogh auto State = C.getState();
4629a08a3faSAdam Balogh const auto CData = getContainerData(State, ContReg);
4639a08a3faSAdam Balogh if (!CData)
4649a08a3faSAdam Balogh return;
4659a08a3faSAdam Balogh
4669a08a3faSAdam Balogh if (const auto EndSym = CData->getEnd()) {
4679a08a3faSAdam Balogh auto &SymMgr = C.getSymbolManager();
4689a08a3faSAdam Balogh auto &BVF = SymMgr.getBasicVals();
4699a08a3faSAdam Balogh auto &SVB = C.getSValBuilder();
4709a08a3faSAdam Balogh const auto BackSym =
4719a08a3faSAdam Balogh SVB.evalBinOp(State, BO_Sub,
4729a08a3faSAdam Balogh nonloc::SymbolVal(EndSym),
4739a08a3faSAdam Balogh nonloc::ConcreteInt(BVF.getValue(llvm::APSInt::get(1))),
4749a08a3faSAdam Balogh SymMgr.getType(EndSym)).getAsSymbol();
475a3f4d17aSAdam Balogh const NoteTag *ChangeTag =
476a3f4d17aSAdam Balogh getChangeTag(C, "shrank from the back by 1 position", ContReg, ContE);
4779a08a3faSAdam Balogh // For vector-like and deque-like containers invalidate the last and the
4789a08a3faSAdam Balogh // past-end iterator positions. For list-like containers only invalidate
4799a08a3faSAdam Balogh // the last position
4809a08a3faSAdam Balogh if (hasSubscriptOperator(State, ContReg) &&
4819a08a3faSAdam Balogh backModifiable(State, ContReg)) {
4829a08a3faSAdam Balogh State = invalidateIteratorPositions(State, BackSym, BO_GE);
4839a08a3faSAdam Balogh State = setContainerData(State, ContReg, CData->newEnd(nullptr));
4849a08a3faSAdam Balogh } else {
4859a08a3faSAdam Balogh State = invalidateIteratorPositions(State, BackSym, BO_EQ);
4869a08a3faSAdam Balogh }
4879a08a3faSAdam Balogh auto newEndSym = BackSym;
4889a08a3faSAdam Balogh State = setContainerData(State, ContReg, CData->newEnd(newEndSym));
489a3f4d17aSAdam Balogh C.addTransition(State, ChangeTag);
4909a08a3faSAdam Balogh }
4919a08a3faSAdam Balogh }
4929a08a3faSAdam Balogh
handlePushFront(CheckerContext & C,SVal Cont,const Expr * ContE) const493a3f4d17aSAdam Balogh void ContainerModeling::handlePushFront(CheckerContext &C, SVal Cont,
494a3f4d17aSAdam Balogh const Expr *ContE) const {
4959a08a3faSAdam Balogh const auto *ContReg = Cont.getAsRegion();
4969a08a3faSAdam Balogh if (!ContReg)
4979a08a3faSAdam Balogh return;
4989a08a3faSAdam Balogh
4999a08a3faSAdam Balogh ContReg = ContReg->getMostDerivedObjectRegion();
5009a08a3faSAdam Balogh
5019a08a3faSAdam Balogh // For deque-like containers invalidate all iterator positions
5029a08a3faSAdam Balogh auto State = C.getState();
5039a08a3faSAdam Balogh if (hasSubscriptOperator(State, ContReg)) {
5049a08a3faSAdam Balogh State = invalidateAllIteratorPositions(State, ContReg);
5059a08a3faSAdam Balogh C.addTransition(State);
5069a08a3faSAdam Balogh } else {
5079a08a3faSAdam Balogh const auto CData = getContainerData(State, ContReg);
5089a08a3faSAdam Balogh if (!CData)
5099a08a3faSAdam Balogh return;
5109a08a3faSAdam Balogh
5119a08a3faSAdam Balogh if (const auto BeginSym = CData->getBegin()) {
5129a08a3faSAdam Balogh auto &SymMgr = C.getSymbolManager();
5139a08a3faSAdam Balogh auto &BVF = SymMgr.getBasicVals();
5149a08a3faSAdam Balogh auto &SVB = C.getSValBuilder();
5159a08a3faSAdam Balogh const auto newBeginSym =
5169a08a3faSAdam Balogh SVB.evalBinOp(State, BO_Sub,
5179a08a3faSAdam Balogh nonloc::SymbolVal(BeginSym),
5189a08a3faSAdam Balogh nonloc::ConcreteInt(BVF.getValue(llvm::APSInt::get(1))),
5199a08a3faSAdam Balogh SymMgr.getType(BeginSym)).getAsSymbol();
520a3f4d17aSAdam Balogh const NoteTag *ChangeTag =
521a3f4d17aSAdam Balogh getChangeTag(C, "extended to the front by 1 position", ContReg, ContE);
5229a08a3faSAdam Balogh State = setContainerData(State, ContReg, CData->newBegin(newBeginSym));
523a3f4d17aSAdam Balogh C.addTransition(State, ChangeTag);
5249a08a3faSAdam Balogh }
5259a08a3faSAdam Balogh }
5269a08a3faSAdam Balogh }
5279a08a3faSAdam Balogh
handlePopFront(CheckerContext & C,SVal Cont,const Expr * ContE) const528a3f4d17aSAdam Balogh void ContainerModeling::handlePopFront(CheckerContext &C, SVal Cont,
529a3f4d17aSAdam Balogh const Expr *ContE) const {
5309a08a3faSAdam Balogh const auto *ContReg = Cont.getAsRegion();
5319a08a3faSAdam Balogh if (!ContReg)
5329a08a3faSAdam Balogh return;
5339a08a3faSAdam Balogh
5349a08a3faSAdam Balogh ContReg = ContReg->getMostDerivedObjectRegion();
5359a08a3faSAdam Balogh
5369a08a3faSAdam Balogh auto State = C.getState();
5379a08a3faSAdam Balogh const auto CData = getContainerData(State, ContReg);
5389a08a3faSAdam Balogh if (!CData)
5399a08a3faSAdam Balogh return;
5409a08a3faSAdam Balogh
5419a08a3faSAdam Balogh // For deque-like containers invalidate all iterator positions. For list-like
5429a08a3faSAdam Balogh // iterators only invalidate the first position
5439a08a3faSAdam Balogh if (const auto BeginSym = CData->getBegin()) {
5449a08a3faSAdam Balogh if (hasSubscriptOperator(State, ContReg)) {
5459a08a3faSAdam Balogh State = invalidateIteratorPositions(State, BeginSym, BO_LE);
5469a08a3faSAdam Balogh } else {
5479a08a3faSAdam Balogh State = invalidateIteratorPositions(State, BeginSym, BO_EQ);
5489a08a3faSAdam Balogh }
5499a08a3faSAdam Balogh auto &SymMgr = C.getSymbolManager();
5509a08a3faSAdam Balogh auto &BVF = SymMgr.getBasicVals();
5519a08a3faSAdam Balogh auto &SVB = C.getSValBuilder();
5529a08a3faSAdam Balogh const auto newBeginSym =
5539a08a3faSAdam Balogh SVB.evalBinOp(State, BO_Add,
5549a08a3faSAdam Balogh nonloc::SymbolVal(BeginSym),
5559a08a3faSAdam Balogh nonloc::ConcreteInt(BVF.getValue(llvm::APSInt::get(1))),
5569a08a3faSAdam Balogh SymMgr.getType(BeginSym)).getAsSymbol();
557a3f4d17aSAdam Balogh const NoteTag *ChangeTag =
558a3f4d17aSAdam Balogh getChangeTag(C, "shrank from the front by 1 position", ContReg, ContE);
5599a08a3faSAdam Balogh State = setContainerData(State, ContReg, CData->newBegin(newBeginSym));
560a3f4d17aSAdam Balogh C.addTransition(State, ChangeTag);
5619a08a3faSAdam Balogh }
5629a08a3faSAdam Balogh }
5639a08a3faSAdam Balogh
handleInsert(CheckerContext & C,SVal Cont,SVal Iter) const564a3f4d17aSAdam Balogh void ContainerModeling::handleInsert(CheckerContext &C, SVal Cont,
565a3f4d17aSAdam Balogh SVal Iter) const {
5669a08a3faSAdam Balogh const auto *ContReg = Cont.getAsRegion();
5679a08a3faSAdam Balogh if (!ContReg)
5689a08a3faSAdam Balogh return;
5699a08a3faSAdam Balogh
5709a08a3faSAdam Balogh ContReg = ContReg->getMostDerivedObjectRegion();
5719a08a3faSAdam Balogh
5729a08a3faSAdam Balogh auto State = C.getState();
5739a08a3faSAdam Balogh const auto *Pos = getIteratorPosition(State, Iter);
5749a08a3faSAdam Balogh if (!Pos)
5759a08a3faSAdam Balogh return;
5769a08a3faSAdam Balogh
5779a08a3faSAdam Balogh // For deque-like containers invalidate all iterator positions. For
5789a08a3faSAdam Balogh // vector-like containers invalidate iterator positions after the insertion.
5799a08a3faSAdam Balogh if (hasSubscriptOperator(State, ContReg) && backModifiable(State, ContReg)) {
5809a08a3faSAdam Balogh if (frontModifiable(State, ContReg)) {
5819a08a3faSAdam Balogh State = invalidateAllIteratorPositions(State, ContReg);
5829a08a3faSAdam Balogh } else {
5839a08a3faSAdam Balogh State = invalidateIteratorPositions(State, Pos->getOffset(), BO_GE);
5849a08a3faSAdam Balogh }
5859a08a3faSAdam Balogh if (const auto *CData = getContainerData(State, ContReg)) {
5869a08a3faSAdam Balogh if (const auto EndSym = CData->getEnd()) {
5879a08a3faSAdam Balogh State = invalidateIteratorPositions(State, EndSym, BO_GE);
5889a08a3faSAdam Balogh State = setContainerData(State, ContReg, CData->newEnd(nullptr));
5899a08a3faSAdam Balogh }
5909a08a3faSAdam Balogh }
5919a08a3faSAdam Balogh C.addTransition(State);
5929a08a3faSAdam Balogh }
5939a08a3faSAdam Balogh }
5949a08a3faSAdam Balogh
handleErase(CheckerContext & C,SVal Cont,SVal Iter) const595a3f4d17aSAdam Balogh void ContainerModeling::handleErase(CheckerContext &C, SVal Cont,
596a3f4d17aSAdam Balogh SVal Iter) const {
5979a08a3faSAdam Balogh const auto *ContReg = Cont.getAsRegion();
5989a08a3faSAdam Balogh if (!ContReg)
5999a08a3faSAdam Balogh return;
6009a08a3faSAdam Balogh
6019a08a3faSAdam Balogh ContReg = ContReg->getMostDerivedObjectRegion();
6029a08a3faSAdam Balogh
6039a08a3faSAdam Balogh auto State = C.getState();
6049a08a3faSAdam Balogh const auto *Pos = getIteratorPosition(State, Iter);
6059a08a3faSAdam Balogh if (!Pos)
6069a08a3faSAdam Balogh return;
6079a08a3faSAdam Balogh
6089a08a3faSAdam Balogh // For deque-like containers invalidate all iterator positions. For
6099a08a3faSAdam Balogh // vector-like containers invalidate iterator positions at and after the
6109a08a3faSAdam Balogh // deletion. For list-like containers only invalidate the deleted position.
6119a08a3faSAdam Balogh if (hasSubscriptOperator(State, ContReg) && backModifiable(State, ContReg)) {
6129a08a3faSAdam Balogh if (frontModifiable(State, ContReg)) {
6139a08a3faSAdam Balogh State = invalidateAllIteratorPositions(State, ContReg);
6149a08a3faSAdam Balogh } else {
6159a08a3faSAdam Balogh State = invalidateIteratorPositions(State, Pos->getOffset(), BO_GE);
6169a08a3faSAdam Balogh }
6179a08a3faSAdam Balogh if (const auto *CData = getContainerData(State, ContReg)) {
6189a08a3faSAdam Balogh if (const auto EndSym = CData->getEnd()) {
6199a08a3faSAdam Balogh State = invalidateIteratorPositions(State, EndSym, BO_GE);
6209a08a3faSAdam Balogh State = setContainerData(State, ContReg, CData->newEnd(nullptr));
6219a08a3faSAdam Balogh }
6229a08a3faSAdam Balogh }
6239a08a3faSAdam Balogh } else {
6249a08a3faSAdam Balogh State = invalidateIteratorPositions(State, Pos->getOffset(), BO_EQ);
6259a08a3faSAdam Balogh }
6269a08a3faSAdam Balogh C.addTransition(State);
6279a08a3faSAdam Balogh }
6289a08a3faSAdam Balogh
handleErase(CheckerContext & C,SVal Cont,SVal Iter1,SVal Iter2) const629a3f4d17aSAdam Balogh void ContainerModeling::handleErase(CheckerContext &C, SVal Cont, SVal Iter1,
630a3f4d17aSAdam Balogh SVal Iter2) const {
6319a08a3faSAdam Balogh const auto *ContReg = Cont.getAsRegion();
6329a08a3faSAdam Balogh if (!ContReg)
6339a08a3faSAdam Balogh return;
6349a08a3faSAdam Balogh
6359a08a3faSAdam Balogh ContReg = ContReg->getMostDerivedObjectRegion();
6369a08a3faSAdam Balogh auto State = C.getState();
6379a08a3faSAdam Balogh const auto *Pos1 = getIteratorPosition(State, Iter1);
6389a08a3faSAdam Balogh const auto *Pos2 = getIteratorPosition(State, Iter2);
6399a08a3faSAdam Balogh if (!Pos1 || !Pos2)
6409a08a3faSAdam Balogh return;
6419a08a3faSAdam Balogh
6429a08a3faSAdam Balogh // For deque-like containers invalidate all iterator positions. For
6439a08a3faSAdam Balogh // vector-like containers invalidate iterator positions at and after the
6449a08a3faSAdam Balogh // deletion range. For list-like containers only invalidate the deleted
6459a08a3faSAdam Balogh // position range [first..last].
6469a08a3faSAdam Balogh if (hasSubscriptOperator(State, ContReg) && backModifiable(State, ContReg)) {
6479a08a3faSAdam Balogh if (frontModifiable(State, ContReg)) {
6489a08a3faSAdam Balogh State = invalidateAllIteratorPositions(State, ContReg);
6499a08a3faSAdam Balogh } else {
6509a08a3faSAdam Balogh State = invalidateIteratorPositions(State, Pos1->getOffset(), BO_GE);
6519a08a3faSAdam Balogh }
6529a08a3faSAdam Balogh if (const auto *CData = getContainerData(State, ContReg)) {
6539a08a3faSAdam Balogh if (const auto EndSym = CData->getEnd()) {
6549a08a3faSAdam Balogh State = invalidateIteratorPositions(State, EndSym, BO_GE);
6559a08a3faSAdam Balogh State = setContainerData(State, ContReg, CData->newEnd(nullptr));
6569a08a3faSAdam Balogh }
6579a08a3faSAdam Balogh }
6589a08a3faSAdam Balogh } else {
6599a08a3faSAdam Balogh State = invalidateIteratorPositions(State, Pos1->getOffset(), BO_GE,
6609a08a3faSAdam Balogh Pos2->getOffset(), BO_LT);
6619a08a3faSAdam Balogh }
6629a08a3faSAdam Balogh C.addTransition(State);
6639a08a3faSAdam Balogh }
6649a08a3faSAdam Balogh
handleEraseAfter(CheckerContext & C,SVal Cont,SVal Iter) const665a3f4d17aSAdam Balogh void ContainerModeling::handleEraseAfter(CheckerContext &C, SVal Cont,
666a3f4d17aSAdam Balogh SVal Iter) const {
6679a08a3faSAdam Balogh auto State = C.getState();
6689a08a3faSAdam Balogh const auto *Pos = getIteratorPosition(State, Iter);
6699a08a3faSAdam Balogh if (!Pos)
6709a08a3faSAdam Balogh return;
6719a08a3faSAdam Balogh
6729a08a3faSAdam Balogh // Invalidate the deleted iterator position, which is the position of the
6739a08a3faSAdam Balogh // parameter plus one.
6749a08a3faSAdam Balogh auto &SymMgr = C.getSymbolManager();
6759a08a3faSAdam Balogh auto &BVF = SymMgr.getBasicVals();
6769a08a3faSAdam Balogh auto &SVB = C.getSValBuilder();
6779a08a3faSAdam Balogh const auto NextSym =
6789a08a3faSAdam Balogh SVB.evalBinOp(State, BO_Add,
6799a08a3faSAdam Balogh nonloc::SymbolVal(Pos->getOffset()),
6809a08a3faSAdam Balogh nonloc::ConcreteInt(BVF.getValue(llvm::APSInt::get(1))),
6819a08a3faSAdam Balogh SymMgr.getType(Pos->getOffset())).getAsSymbol();
6829a08a3faSAdam Balogh State = invalidateIteratorPositions(State, NextSym, BO_EQ);
6839a08a3faSAdam Balogh C.addTransition(State);
6849a08a3faSAdam Balogh }
6859a08a3faSAdam Balogh
handleEraseAfter(CheckerContext & C,SVal Cont,SVal Iter1,SVal Iter2) const686a3f4d17aSAdam Balogh void ContainerModeling::handleEraseAfter(CheckerContext &C, SVal Cont,
687a3f4d17aSAdam Balogh SVal Iter1, SVal Iter2) const {
6889a08a3faSAdam Balogh auto State = C.getState();
6899a08a3faSAdam Balogh const auto *Pos1 = getIteratorPosition(State, Iter1);
6909a08a3faSAdam Balogh const auto *Pos2 = getIteratorPosition(State, Iter2);
6919a08a3faSAdam Balogh if (!Pos1 || !Pos2)
6929a08a3faSAdam Balogh return;
6939a08a3faSAdam Balogh
6949a08a3faSAdam Balogh // Invalidate the deleted iterator position range (first..last)
6959a08a3faSAdam Balogh State = invalidateIteratorPositions(State, Pos1->getOffset(), BO_GT,
6969a08a3faSAdam Balogh Pos2->getOffset(), BO_LT);
6979a08a3faSAdam Balogh C.addTransition(State);
6989a08a3faSAdam Balogh }
6999a08a3faSAdam Balogh
getChangeTag(CheckerContext & C,StringRef Text,const MemRegion * ContReg,const Expr * ContE) const700a3f4d17aSAdam Balogh const NoteTag *ContainerModeling::getChangeTag(CheckerContext &C,
701a3f4d17aSAdam Balogh StringRef Text,
702a3f4d17aSAdam Balogh const MemRegion *ContReg,
703a3f4d17aSAdam Balogh const Expr *ContE) const {
704a3f4d17aSAdam Balogh StringRef Name;
705a3f4d17aSAdam Balogh // First try to get the name of the variable from the region
706a3f4d17aSAdam Balogh if (const auto *DR = dyn_cast<DeclRegion>(ContReg)) {
707a3f4d17aSAdam Balogh Name = DR->getDecl()->getName();
708a3f4d17aSAdam Balogh // If the region is not a `DeclRegion` then use the expression instead
709a3f4d17aSAdam Balogh } else if (const auto *DRE =
710a3f4d17aSAdam Balogh dyn_cast<DeclRefExpr>(ContE->IgnoreParenCasts())) {
711a3f4d17aSAdam Balogh Name = DRE->getDecl()->getName();
712a3f4d17aSAdam Balogh }
713a3f4d17aSAdam Balogh
714a3f4d17aSAdam Balogh return C.getNoteTag(
715a3f4d17aSAdam Balogh [Text, Name, ContReg](PathSensitiveBugReport &BR) -> std::string {
7161a27d63aSAdam Balogh if (!BR.isInteresting(ContReg))
7171a27d63aSAdam Balogh return "";
7181a27d63aSAdam Balogh
719a3f4d17aSAdam Balogh SmallString<256> Msg;
720a3f4d17aSAdam Balogh llvm::raw_svector_ostream Out(Msg);
721a3f4d17aSAdam Balogh Out << "Container " << (!Name.empty() ? ("'" + Name.str() + "' ") : "" )
722a3f4d17aSAdam Balogh << Text;
723a3f4d17aSAdam Balogh return std::string(Out.str());
724a3f4d17aSAdam Balogh });
725a3f4d17aSAdam Balogh }
726a3f4d17aSAdam Balogh
printState(raw_ostream & Out,ProgramStateRef State,const char * NL,const char * Sep) const7279a08a3faSAdam Balogh void ContainerModeling::printState(raw_ostream &Out, ProgramStateRef State,
7289a08a3faSAdam Balogh const char *NL, const char *Sep) const {
7299a08a3faSAdam Balogh auto ContMap = State->get<ContainerMap>();
7309a08a3faSAdam Balogh
7319a08a3faSAdam Balogh if (!ContMap.isEmpty()) {
7329a08a3faSAdam Balogh Out << Sep << "Container Data :" << NL;
7339a08a3faSAdam Balogh for (const auto &Cont : ContMap) {
7349a08a3faSAdam Balogh Cont.first->dumpToStream(Out);
7359a08a3faSAdam Balogh Out << " : [ ";
7369a08a3faSAdam Balogh const auto CData = Cont.second;
7379a08a3faSAdam Balogh if (CData.getBegin())
7389a08a3faSAdam Balogh CData.getBegin()->dumpToStream(Out);
7399a08a3faSAdam Balogh else
7409a08a3faSAdam Balogh Out << "<Unknown>";
7419a08a3faSAdam Balogh Out << " .. ";
7429a08a3faSAdam Balogh if (CData.getEnd())
7439a08a3faSAdam Balogh CData.getEnd()->dumpToStream(Out);
7449a08a3faSAdam Balogh else
7459a08a3faSAdam Balogh Out << "<Unknown>";
7469a08a3faSAdam Balogh Out << " ]";
7479a08a3faSAdam Balogh }
7489a08a3faSAdam Balogh }
7499a08a3faSAdam Balogh }
7509a08a3faSAdam Balogh
7519a08a3faSAdam Balogh namespace {
7529a08a3faSAdam Balogh
isBeginCall(const FunctionDecl * Func)7539a08a3faSAdam Balogh bool isBeginCall(const FunctionDecl *Func) {
7549a08a3faSAdam Balogh const auto *IdInfo = Func->getIdentifier();
7559a08a3faSAdam Balogh if (!IdInfo)
7569a08a3faSAdam Balogh return false;
757ed1539c6SKazu Hirata return IdInfo->getName().ends_with_insensitive("begin");
7589a08a3faSAdam Balogh }
7599a08a3faSAdam Balogh
isEndCall(const FunctionDecl * Func)7609a08a3faSAdam Balogh bool isEndCall(const FunctionDecl *Func) {
7619a08a3faSAdam Balogh const auto *IdInfo = Func->getIdentifier();
7629a08a3faSAdam Balogh if (!IdInfo)
7639a08a3faSAdam Balogh return false;
764ed1539c6SKazu Hirata return IdInfo->getName().ends_with_insensitive("end");
7659a08a3faSAdam Balogh }
7669a08a3faSAdam Balogh
getCXXRecordDecl(ProgramStateRef State,const MemRegion * Reg)7679a08a3faSAdam Balogh const CXXRecordDecl *getCXXRecordDecl(ProgramStateRef State,
7689a08a3faSAdam Balogh const MemRegion *Reg) {
7699a08a3faSAdam Balogh auto TI = getDynamicTypeInfo(State, Reg);
7709a08a3faSAdam Balogh if (!TI.isValid())
7719a08a3faSAdam Balogh return nullptr;
7729a08a3faSAdam Balogh
7739a08a3faSAdam Balogh auto Type = TI.getType();
7749a08a3faSAdam Balogh if (const auto *RefT = Type->getAs<ReferenceType>()) {
7759a08a3faSAdam Balogh Type = RefT->getPointeeType();
7769a08a3faSAdam Balogh }
7779a08a3faSAdam Balogh
77805d8b5e6SJOSTAR if (const auto *PtrT = Type->getAs<PointerType>()) {
77905d8b5e6SJOSTAR Type = PtrT->getPointeeType();
78005d8b5e6SJOSTAR }
78105d8b5e6SJOSTAR
7829a08a3faSAdam Balogh return Type->getUnqualifiedDesugaredType()->getAsCXXRecordDecl();
7839a08a3faSAdam Balogh }
7849a08a3faSAdam Balogh
hasSubscriptOperator(ProgramStateRef State,const MemRegion * Reg)7859a08a3faSAdam Balogh bool hasSubscriptOperator(ProgramStateRef State, const MemRegion *Reg) {
7869a08a3faSAdam Balogh const auto *CRD = getCXXRecordDecl(State, Reg);
7879a08a3faSAdam Balogh if (!CRD)
7889a08a3faSAdam Balogh return false;
7899a08a3faSAdam Balogh
7909a08a3faSAdam Balogh for (const auto *Method : CRD->methods()) {
7919a08a3faSAdam Balogh if (!Method->isOverloadedOperator())
7929a08a3faSAdam Balogh continue;
7939a08a3faSAdam Balogh const auto OPK = Method->getOverloadedOperator();
7949a08a3faSAdam Balogh if (OPK == OO_Subscript) {
7959a08a3faSAdam Balogh return true;
7969a08a3faSAdam Balogh }
7979a08a3faSAdam Balogh }
7989a08a3faSAdam Balogh return false;
7999a08a3faSAdam Balogh }
8009a08a3faSAdam Balogh
frontModifiable(ProgramStateRef State,const MemRegion * Reg)8019a08a3faSAdam Balogh bool frontModifiable(ProgramStateRef State, const MemRegion *Reg) {
8029a08a3faSAdam Balogh const auto *CRD = getCXXRecordDecl(State, Reg);
8039a08a3faSAdam Balogh if (!CRD)
8049a08a3faSAdam Balogh return false;
8059a08a3faSAdam Balogh
8069a08a3faSAdam Balogh for (const auto *Method : CRD->methods()) {
8079a08a3faSAdam Balogh if (!Method->getDeclName().isIdentifier())
8089a08a3faSAdam Balogh continue;
8099a08a3faSAdam Balogh if (Method->getName() == "push_front" || Method->getName() == "pop_front") {
8109a08a3faSAdam Balogh return true;
8119a08a3faSAdam Balogh }
8129a08a3faSAdam Balogh }
8139a08a3faSAdam Balogh return false;
8149a08a3faSAdam Balogh }
8159a08a3faSAdam Balogh
backModifiable(ProgramStateRef State,const MemRegion * Reg)8169a08a3faSAdam Balogh bool backModifiable(ProgramStateRef State, const MemRegion *Reg) {
8179a08a3faSAdam Balogh const auto *CRD = getCXXRecordDecl(State, Reg);
8189a08a3faSAdam Balogh if (!CRD)
8199a08a3faSAdam Balogh return false;
8209a08a3faSAdam Balogh
8219a08a3faSAdam Balogh for (const auto *Method : CRD->methods()) {
8229a08a3faSAdam Balogh if (!Method->getDeclName().isIdentifier())
8239a08a3faSAdam Balogh continue;
8249a08a3faSAdam Balogh if (Method->getName() == "push_back" || Method->getName() == "pop_back") {
8259a08a3faSAdam Balogh return true;
8269a08a3faSAdam Balogh }
8279a08a3faSAdam Balogh }
8289a08a3faSAdam Balogh return false;
8299a08a3faSAdam Balogh }
8309a08a3faSAdam Balogh
getContainerBegin(ProgramStateRef State,const MemRegion * Cont)8319a08a3faSAdam Balogh SymbolRef getContainerBegin(ProgramStateRef State, const MemRegion *Cont) {
8329a08a3faSAdam Balogh const auto *CDataPtr = getContainerData(State, Cont);
8339a08a3faSAdam Balogh if (!CDataPtr)
8349a08a3faSAdam Balogh return nullptr;
8359a08a3faSAdam Balogh
8369a08a3faSAdam Balogh return CDataPtr->getBegin();
8379a08a3faSAdam Balogh }
8389a08a3faSAdam Balogh
getContainerEnd(ProgramStateRef State,const MemRegion * Cont)8399a08a3faSAdam Balogh SymbolRef getContainerEnd(ProgramStateRef State, const MemRegion *Cont) {
8409a08a3faSAdam Balogh const auto *CDataPtr = getContainerData(State, Cont);
8419a08a3faSAdam Balogh if (!CDataPtr)
8429a08a3faSAdam Balogh return nullptr;
8439a08a3faSAdam Balogh
8449a08a3faSAdam Balogh return CDataPtr->getEnd();
8459a08a3faSAdam Balogh }
8469a08a3faSAdam Balogh
createContainerBegin(ProgramStateRef State,const MemRegion * Cont,const Expr * E,QualType T,const LocationContext * LCtx,unsigned BlockCount)8479a08a3faSAdam Balogh ProgramStateRef createContainerBegin(ProgramStateRef State,
8489a08a3faSAdam Balogh const MemRegion *Cont, const Expr *E,
8499a08a3faSAdam Balogh QualType T, const LocationContext *LCtx,
8509a08a3faSAdam Balogh unsigned BlockCount) {
8519a08a3faSAdam Balogh // Only create if it does not exist
8529a08a3faSAdam Balogh const auto *CDataPtr = getContainerData(State, Cont);
8539a08a3faSAdam Balogh if (CDataPtr && CDataPtr->getBegin())
8549a08a3faSAdam Balogh return State;
8559a08a3faSAdam Balogh
8569a08a3faSAdam Balogh auto &SymMgr = State->getSymbolManager();
8579a08a3faSAdam Balogh const SymbolConjured *Sym = SymMgr.conjureSymbol(E, LCtx, T, BlockCount,
8589a08a3faSAdam Balogh "begin");
8599a08a3faSAdam Balogh State = assumeNoOverflow(State, Sym, 4);
8609a08a3faSAdam Balogh
8619a08a3faSAdam Balogh if (CDataPtr) {
8629a08a3faSAdam Balogh const auto CData = CDataPtr->newBegin(Sym);
8639a08a3faSAdam Balogh return setContainerData(State, Cont, CData);
8649a08a3faSAdam Balogh }
8659a08a3faSAdam Balogh
8669a08a3faSAdam Balogh const auto CData = ContainerData::fromBegin(Sym);
8679a08a3faSAdam Balogh return setContainerData(State, Cont, CData);
8689a08a3faSAdam Balogh }
8699a08a3faSAdam Balogh
createContainerEnd(ProgramStateRef State,const MemRegion * Cont,const Expr * E,QualType T,const LocationContext * LCtx,unsigned BlockCount)8709a08a3faSAdam Balogh ProgramStateRef createContainerEnd(ProgramStateRef State, const MemRegion *Cont,
8719a08a3faSAdam Balogh const Expr *E, QualType T,
8729a08a3faSAdam Balogh const LocationContext *LCtx,
8739a08a3faSAdam Balogh unsigned BlockCount) {
8749a08a3faSAdam Balogh // Only create if it does not exist
8759a08a3faSAdam Balogh const auto *CDataPtr = getContainerData(State, Cont);
8769a08a3faSAdam Balogh if (CDataPtr && CDataPtr->getEnd())
8779a08a3faSAdam Balogh return State;
8789a08a3faSAdam Balogh
8799a08a3faSAdam Balogh auto &SymMgr = State->getSymbolManager();
8809a08a3faSAdam Balogh const SymbolConjured *Sym = SymMgr.conjureSymbol(E, LCtx, T, BlockCount,
8819a08a3faSAdam Balogh "end");
8829a08a3faSAdam Balogh State = assumeNoOverflow(State, Sym, 4);
8839a08a3faSAdam Balogh
8849a08a3faSAdam Balogh if (CDataPtr) {
8859a08a3faSAdam Balogh const auto CData = CDataPtr->newEnd(Sym);
8869a08a3faSAdam Balogh return setContainerData(State, Cont, CData);
8879a08a3faSAdam Balogh }
8889a08a3faSAdam Balogh
8899a08a3faSAdam Balogh const auto CData = ContainerData::fromEnd(Sym);
8909a08a3faSAdam Balogh return setContainerData(State, Cont, CData);
8919a08a3faSAdam Balogh }
8929a08a3faSAdam Balogh
setContainerData(ProgramStateRef State,const MemRegion * Cont,const ContainerData & CData)8939a08a3faSAdam Balogh ProgramStateRef setContainerData(ProgramStateRef State, const MemRegion *Cont,
8949a08a3faSAdam Balogh const ContainerData &CData) {
8959a08a3faSAdam Balogh return State->set<ContainerMap>(Cont, CData);
8969a08a3faSAdam Balogh }
8979a08a3faSAdam Balogh
8989a08a3faSAdam Balogh template <typename Condition, typename Process>
processIteratorPositions(ProgramStateRef State,Condition Cond,Process Proc)8999a08a3faSAdam Balogh ProgramStateRef processIteratorPositions(ProgramStateRef State, Condition Cond,
9009a08a3faSAdam Balogh Process Proc) {
9019a08a3faSAdam Balogh auto &RegionMapFactory = State->get_context<IteratorRegionMap>();
9029a08a3faSAdam Balogh auto RegionMap = State->get<IteratorRegionMap>();
9039a08a3faSAdam Balogh bool Changed = false;
9049a08a3faSAdam Balogh for (const auto &Reg : RegionMap) {
9059a08a3faSAdam Balogh if (Cond(Reg.second)) {
9069a08a3faSAdam Balogh RegionMap = RegionMapFactory.add(RegionMap, Reg.first, Proc(Reg.second));
9079a08a3faSAdam Balogh Changed = true;
9089a08a3faSAdam Balogh }
9099a08a3faSAdam Balogh }
9109a08a3faSAdam Balogh
9119a08a3faSAdam Balogh if (Changed)
9129a08a3faSAdam Balogh State = State->set<IteratorRegionMap>(RegionMap);
9139a08a3faSAdam Balogh
9149a08a3faSAdam Balogh auto &SymbolMapFactory = State->get_context<IteratorSymbolMap>();
9159a08a3faSAdam Balogh auto SymbolMap = State->get<IteratorSymbolMap>();
9169a08a3faSAdam Balogh Changed = false;
9179a08a3faSAdam Balogh for (const auto &Sym : SymbolMap) {
9189a08a3faSAdam Balogh if (Cond(Sym.second)) {
9199a08a3faSAdam Balogh SymbolMap = SymbolMapFactory.add(SymbolMap, Sym.first, Proc(Sym.second));
9209a08a3faSAdam Balogh Changed = true;
9219a08a3faSAdam Balogh }
9229a08a3faSAdam Balogh }
9239a08a3faSAdam Balogh
9249a08a3faSAdam Balogh if (Changed)
9259a08a3faSAdam Balogh State = State->set<IteratorSymbolMap>(SymbolMap);
9269a08a3faSAdam Balogh
9279a08a3faSAdam Balogh return State;
9289a08a3faSAdam Balogh }
9299a08a3faSAdam Balogh
invalidateAllIteratorPositions(ProgramStateRef State,const MemRegion * Cont)9309a08a3faSAdam Balogh ProgramStateRef invalidateAllIteratorPositions(ProgramStateRef State,
9319a08a3faSAdam Balogh const MemRegion *Cont) {
9329a08a3faSAdam Balogh auto MatchCont = [&](const IteratorPosition &Pos) {
9339a08a3faSAdam Balogh return Pos.getContainer() == Cont;
9349a08a3faSAdam Balogh };
9359a08a3faSAdam Balogh auto Invalidate = [&](const IteratorPosition &Pos) {
9369a08a3faSAdam Balogh return Pos.invalidate();
9379a08a3faSAdam Balogh };
9389a08a3faSAdam Balogh return processIteratorPositions(State, MatchCont, Invalidate);
9399a08a3faSAdam Balogh }
9409a08a3faSAdam Balogh
9419a08a3faSAdam Balogh ProgramStateRef
invalidateAllIteratorPositionsExcept(ProgramStateRef State,const MemRegion * Cont,SymbolRef Offset,BinaryOperator::Opcode Opc)9429a08a3faSAdam Balogh invalidateAllIteratorPositionsExcept(ProgramStateRef State,
9439a08a3faSAdam Balogh const MemRegion *Cont, SymbolRef Offset,
9449a08a3faSAdam Balogh BinaryOperator::Opcode Opc) {
9459a08a3faSAdam Balogh auto MatchContAndCompare = [&](const IteratorPosition &Pos) {
9469a08a3faSAdam Balogh return Pos.getContainer() == Cont &&
9479a08a3faSAdam Balogh !compare(State, Pos.getOffset(), Offset, Opc);
9489a08a3faSAdam Balogh };
9499a08a3faSAdam Balogh auto Invalidate = [&](const IteratorPosition &Pos) {
9509a08a3faSAdam Balogh return Pos.invalidate();
9519a08a3faSAdam Balogh };
9529a08a3faSAdam Balogh return processIteratorPositions(State, MatchContAndCompare, Invalidate);
9539a08a3faSAdam Balogh }
9549a08a3faSAdam Balogh
invalidateIteratorPositions(ProgramStateRef State,SymbolRef Offset,BinaryOperator::Opcode Opc)9559a08a3faSAdam Balogh ProgramStateRef invalidateIteratorPositions(ProgramStateRef State,
9569a08a3faSAdam Balogh SymbolRef Offset,
9579a08a3faSAdam Balogh BinaryOperator::Opcode Opc) {
9589a08a3faSAdam Balogh auto Compare = [&](const IteratorPosition &Pos) {
9599a08a3faSAdam Balogh return compare(State, Pos.getOffset(), Offset, Opc);
9609a08a3faSAdam Balogh };
9619a08a3faSAdam Balogh auto Invalidate = [&](const IteratorPosition &Pos) {
9629a08a3faSAdam Balogh return Pos.invalidate();
9639a08a3faSAdam Balogh };
9649a08a3faSAdam Balogh return processIteratorPositions(State, Compare, Invalidate);
9659a08a3faSAdam Balogh }
9669a08a3faSAdam Balogh
invalidateIteratorPositions(ProgramStateRef State,SymbolRef Offset1,BinaryOperator::Opcode Opc1,SymbolRef Offset2,BinaryOperator::Opcode Opc2)9679a08a3faSAdam Balogh ProgramStateRef invalidateIteratorPositions(ProgramStateRef State,
9689a08a3faSAdam Balogh SymbolRef Offset1,
9699a08a3faSAdam Balogh BinaryOperator::Opcode Opc1,
9709a08a3faSAdam Balogh SymbolRef Offset2,
9719a08a3faSAdam Balogh BinaryOperator::Opcode Opc2) {
9729a08a3faSAdam Balogh auto Compare = [&](const IteratorPosition &Pos) {
9739a08a3faSAdam Balogh return compare(State, Pos.getOffset(), Offset1, Opc1) &&
9749a08a3faSAdam Balogh compare(State, Pos.getOffset(), Offset2, Opc2);
9759a08a3faSAdam Balogh };
9769a08a3faSAdam Balogh auto Invalidate = [&](const IteratorPosition &Pos) {
9779a08a3faSAdam Balogh return Pos.invalidate();
9789a08a3faSAdam Balogh };
9799a08a3faSAdam Balogh return processIteratorPositions(State, Compare, Invalidate);
9809a08a3faSAdam Balogh }
9819a08a3faSAdam Balogh
reassignAllIteratorPositions(ProgramStateRef State,const MemRegion * Cont,const MemRegion * NewCont)9829a08a3faSAdam Balogh ProgramStateRef reassignAllIteratorPositions(ProgramStateRef State,
9839a08a3faSAdam Balogh const MemRegion *Cont,
9849a08a3faSAdam Balogh const MemRegion *NewCont) {
9859a08a3faSAdam Balogh auto MatchCont = [&](const IteratorPosition &Pos) {
9869a08a3faSAdam Balogh return Pos.getContainer() == Cont;
9879a08a3faSAdam Balogh };
9889a08a3faSAdam Balogh auto ReAssign = [&](const IteratorPosition &Pos) {
9899a08a3faSAdam Balogh return Pos.reAssign(NewCont);
9909a08a3faSAdam Balogh };
9919a08a3faSAdam Balogh return processIteratorPositions(State, MatchCont, ReAssign);
9929a08a3faSAdam Balogh }
9939a08a3faSAdam Balogh
reassignAllIteratorPositionsUnless(ProgramStateRef State,const MemRegion * Cont,const MemRegion * NewCont,SymbolRef Offset,BinaryOperator::Opcode Opc)9949a08a3faSAdam Balogh ProgramStateRef reassignAllIteratorPositionsUnless(ProgramStateRef State,
9959a08a3faSAdam Balogh const MemRegion *Cont,
9969a08a3faSAdam Balogh const MemRegion *NewCont,
9979a08a3faSAdam Balogh SymbolRef Offset,
9989a08a3faSAdam Balogh BinaryOperator::Opcode Opc) {
9999a08a3faSAdam Balogh auto MatchContAndCompare = [&](const IteratorPosition &Pos) {
10009a08a3faSAdam Balogh return Pos.getContainer() == Cont &&
10019a08a3faSAdam Balogh !compare(State, Pos.getOffset(), Offset, Opc);
10029a08a3faSAdam Balogh };
10039a08a3faSAdam Balogh auto ReAssign = [&](const IteratorPosition &Pos) {
10049a08a3faSAdam Balogh return Pos.reAssign(NewCont);
10059a08a3faSAdam Balogh };
10069a08a3faSAdam Balogh return processIteratorPositions(State, MatchContAndCompare, ReAssign);
10079a08a3faSAdam Balogh }
10089a08a3faSAdam Balogh
10099a08a3faSAdam Balogh // This function rebases symbolic expression `OldSym + Int` to `NewSym + Int`,
10109a08a3faSAdam Balogh // `OldSym - Int` to `NewSym - Int` and `OldSym` to `NewSym` in any iterator
10119a08a3faSAdam Balogh // position offsets where `CondSym` is true.
rebaseSymbolInIteratorPositionsIf(ProgramStateRef State,SValBuilder & SVB,SymbolRef OldSym,SymbolRef NewSym,SymbolRef CondSym,BinaryOperator::Opcode Opc)10129a08a3faSAdam Balogh ProgramStateRef rebaseSymbolInIteratorPositionsIf(
10139a08a3faSAdam Balogh ProgramStateRef State, SValBuilder &SVB, SymbolRef OldSym,
10149a08a3faSAdam Balogh SymbolRef NewSym, SymbolRef CondSym, BinaryOperator::Opcode Opc) {
10159a08a3faSAdam Balogh auto LessThanEnd = [&](const IteratorPosition &Pos) {
10169a08a3faSAdam Balogh return compare(State, Pos.getOffset(), CondSym, Opc);
10179a08a3faSAdam Balogh };
10189a08a3faSAdam Balogh auto RebaseSymbol = [&](const IteratorPosition &Pos) {
10199a08a3faSAdam Balogh return Pos.setTo(rebaseSymbol(State, SVB, Pos.getOffset(), OldSym,
10209a08a3faSAdam Balogh NewSym));
10219a08a3faSAdam Balogh };
10229a08a3faSAdam Balogh return processIteratorPositions(State, LessThanEnd, RebaseSymbol);
10239a08a3faSAdam Balogh }
10249a08a3faSAdam Balogh
10259a08a3faSAdam Balogh // This function rebases symbolic expression `OldExpr + Int` to `NewExpr + Int`,
10269a08a3faSAdam Balogh // `OldExpr - Int` to `NewExpr - Int` and `OldExpr` to `NewExpr` in expression
10279a08a3faSAdam Balogh // `OrigExpr`.
rebaseSymbol(ProgramStateRef State,SValBuilder & SVB,SymbolRef OrigExpr,SymbolRef OldExpr,SymbolRef NewSym)10289a08a3faSAdam Balogh SymbolRef rebaseSymbol(ProgramStateRef State, SValBuilder &SVB,
10299a08a3faSAdam Balogh SymbolRef OrigExpr, SymbolRef OldExpr,
10309a08a3faSAdam Balogh SymbolRef NewSym) {
10319a08a3faSAdam Balogh auto &SymMgr = SVB.getSymbolManager();
10329a08a3faSAdam Balogh auto Diff = SVB.evalBinOpNN(State, BO_Sub, nonloc::SymbolVal(OrigExpr),
10339a08a3faSAdam Balogh nonloc::SymbolVal(OldExpr),
10349a08a3faSAdam Balogh SymMgr.getType(OrigExpr));
10359a08a3faSAdam Balogh
10369a08a3faSAdam Balogh const auto DiffInt = Diff.getAs<nonloc::ConcreteInt>();
10379a08a3faSAdam Balogh if (!DiffInt)
10389a08a3faSAdam Balogh return OrigExpr;
10399a08a3faSAdam Balogh
10409a08a3faSAdam Balogh return SVB.evalBinOpNN(State, BO_Add, *DiffInt, nonloc::SymbolVal(NewSym),
10419a08a3faSAdam Balogh SymMgr.getType(OrigExpr)).getAsSymbol();
10429a08a3faSAdam Balogh }
10439a08a3faSAdam Balogh
hasLiveIterators(ProgramStateRef State,const MemRegion * Cont)10449a08a3faSAdam Balogh bool hasLiveIterators(ProgramStateRef State, const MemRegion *Cont) {
10459a08a3faSAdam Balogh auto RegionMap = State->get<IteratorRegionMap>();
10469a08a3faSAdam Balogh for (const auto &Reg : RegionMap) {
10479a08a3faSAdam Balogh if (Reg.second.getContainer() == Cont)
10489a08a3faSAdam Balogh return true;
10499a08a3faSAdam Balogh }
10509a08a3faSAdam Balogh
10519a08a3faSAdam Balogh auto SymbolMap = State->get<IteratorSymbolMap>();
10529a08a3faSAdam Balogh for (const auto &Sym : SymbolMap) {
10539a08a3faSAdam Balogh if (Sym.second.getContainer() == Cont)
10549a08a3faSAdam Balogh return true;
10559a08a3faSAdam Balogh }
10569a08a3faSAdam Balogh
10579a08a3faSAdam Balogh return false;
10589a08a3faSAdam Balogh }
10599a08a3faSAdam Balogh
10609a08a3faSAdam Balogh } // namespace
10619a08a3faSAdam Balogh
registerContainerModeling(CheckerManager & mgr)10629a08a3faSAdam Balogh void ento::registerContainerModeling(CheckerManager &mgr) {
10639a08a3faSAdam Balogh mgr.registerChecker<ContainerModeling>();
10649a08a3faSAdam Balogh }
10659a08a3faSAdam Balogh
shouldRegisterContainerModeling(const CheckerManager & mgr)1066bda3dd0dSKirstóf Umann bool ento::shouldRegisterContainerModeling(const CheckerManager &mgr) {
1067afcb77ccSAdam Balogh if (!mgr.getLangOpts().CPlusPlus)
1068afcb77ccSAdam Balogh return false;
1069afcb77ccSAdam Balogh
1070afcb77ccSAdam Balogh if (!mgr.getAnalyzerOptions().ShouldAggressivelySimplifyBinaryOperation) {
1071afcb77ccSAdam Balogh mgr.getASTContext().getDiagnostics().Report(
1072afcb77ccSAdam Balogh diag::err_analyzer_checker_incompatible_analyzer_option)
1073afcb77ccSAdam Balogh << "aggressive-binary-operation-simplification" << "false";
1074afcb77ccSAdam Balogh return false;
1075afcb77ccSAdam Balogh }
1076afcb77ccSAdam Balogh
10779a08a3faSAdam Balogh return true;
10789a08a3faSAdam Balogh }
1079