diff --git a/clang/lib/StaticAnalyzer/Checkers/ContainerModeling.cpp b/clang/lib/StaticAnalyzer/Checkers/ContainerModeling.cpp index 55ed809bfed6c..1af7694169c5e 100644 --- a/clang/lib/StaticAnalyzer/Checkers/ContainerModeling.cpp +++ b/clang/lib/StaticAnalyzer/Checkers/ContainerModeling.cpp @@ -31,7 +31,7 @@ using namespace iterator; namespace { class ContainerModeling - : public Checker { + : public Checker { void handleBegin(CheckerContext &C, const Expr *CE, SVal RetVal, SVal Cont) const; @@ -74,19 +74,25 @@ class ContainerModeling CallDescriptionMap NoIterParamFunctions = { {{CDM::CXXMethod, {"clear"}, 0}, &ContainerModeling::handleClear}, {{CDM::CXXMethod, {"assign"}, 2}, &ContainerModeling::handleAssign}, + {{CDM::CXXMethod, {"assign_range"}, 1}, &ContainerModeling::handleAssign}, {{CDM::CXXMethod, {"push_back"}, 1}, &ContainerModeling::handlePushBack}, {{CDM::CXXMethod, {"emplace_back"}, 1}, &ContainerModeling::handlePushBack}, + {{CDM::CXXMethod, {"append_range"}, 1}, + &ContainerModeling::handlePushBack}, {{CDM::CXXMethod, {"pop_back"}, 0}, &ContainerModeling::handlePopBack}, {{CDM::CXXMethod, {"push_front"}, 1}, &ContainerModeling::handlePushFront}, {{CDM::CXXMethod, {"emplace_front"}, 1}, &ContainerModeling::handlePushFront}, + {{CDM::CXXMethod, {"prepend_range"}, 1}, + &ContainerModeling::handlePushFront}, {{CDM::CXXMethod, {"pop_front"}, 0}, &ContainerModeling::handlePopFront}, }; CallDescriptionMap OneIterParamFunctions = { {{CDM::CXXMethod, {"insert"}, 2}, &ContainerModeling::handleInsert}, + {{CDM::CXXMethod, {"insert_range"}, 2}, &ContainerModeling::handleInsert}, {{CDM::CXXMethod, {"emplace"}, 2}, &ContainerModeling::handleInsert}, {{CDM::CXXMethod, {"erase"}, 1}, &ContainerModeling::handleErase}, {{CDM::CXXMethod, {"erase_after"}, 1}, @@ -143,13 +149,13 @@ ProgramStateRef rebaseSymbolInIteratorPositionsIf( ProgramStateRef State, SValBuilder &SVB, SymbolRef OldSym, SymbolRef NewSym, SymbolRef CondSym, BinaryOperator::Opcode Opc); SymbolRef rebaseSymbol(ProgramStateRef State, SValBuilder &SVB, SymbolRef Expr, - SymbolRef OldSym, SymbolRef NewSym); + SymbolRef OldSym, SymbolRef NewSym); bool hasLiveIterators(ProgramStateRef State, const MemRegion *Cont); } // namespace void ContainerModeling::checkPostCall(const CallEvent &Call, - CheckerContext &C) const { + CheckerContext &C) const { const auto *Func = dyn_cast_or_null(Call.getDecl()); if (!Func) return; @@ -161,7 +167,7 @@ void ContainerModeling::checkPostCall(const CallEvent &Call, const auto *InstCall = cast(&Call); if (cast(Func)->isMoveAssignmentOperator()) { handleAssignment(C, InstCall->getCXXThisVal(), Call.getOriginExpr(), - Call.getArgSVal(0)); + Call.getArgSVal(0)); return; } @@ -248,7 +254,7 @@ void ContainerModeling::checkDeadSymbols(SymbolReaper &SR, } void ContainerModeling::handleBegin(CheckerContext &C, const Expr *CE, - SVal RetVal, SVal Cont) const { + SVal RetVal, SVal Cont) const { const auto *ContReg = Cont.getAsRegion(); if (!ContReg) return; @@ -270,7 +276,7 @@ void ContainerModeling::handleBegin(CheckerContext &C, const Expr *CE, } void ContainerModeling::handleEnd(CheckerContext &C, const Expr *CE, - SVal RetVal, SVal Cont) const { + SVal RetVal, SVal Cont) const { const auto *ContReg = Cont.getAsRegion(); if (!ContReg) return; @@ -439,12 +445,12 @@ void ContainerModeling::handlePushBack(CheckerContext &C, SVal Cont, auto &BVF = SymMgr.getBasicVals(); auto &SVB = C.getSValBuilder(); const auto newEndSym = - SVB.evalBinOp(State, BO_Add, - nonloc::SymbolVal(EndSym), - nonloc::ConcreteInt(BVF.getValue(llvm::APSInt::get(1))), - SymMgr.getType(EndSym)).getAsSymbol(); + SVB.evalBinOp(State, BO_Add, nonloc::SymbolVal(EndSym), + nonloc::ConcreteInt(BVF.getValue(llvm::APSInt::get(1))), + SymMgr.getType(EndSym)) + .getAsSymbol(); const NoteTag *ChangeTag = - getChangeTag(C, "extended to the back by 1 position", ContReg, ContE); + getChangeTag(C, "extended to the back by 1 position", ContReg, ContE); State = setContainerData(State, ContReg, CData->newEnd(newEndSym)); C.addTransition(State, ChangeTag); } @@ -468,12 +474,12 @@ void ContainerModeling::handlePopBack(CheckerContext &C, SVal Cont, auto &BVF = SymMgr.getBasicVals(); auto &SVB = C.getSValBuilder(); const auto BackSym = - SVB.evalBinOp(State, BO_Sub, - nonloc::SymbolVal(EndSym), - nonloc::ConcreteInt(BVF.getValue(llvm::APSInt::get(1))), - SymMgr.getType(EndSym)).getAsSymbol(); + SVB.evalBinOp(State, BO_Sub, nonloc::SymbolVal(EndSym), + nonloc::ConcreteInt(BVF.getValue(llvm::APSInt::get(1))), + SymMgr.getType(EndSym)) + .getAsSymbol(); const NoteTag *ChangeTag = - getChangeTag(C, "shrank from the back by 1 position", ContReg, ContE); + getChangeTag(C, "shrank from the back by 1 position", ContReg, ContE); // For vector-like and deque-like containers invalidate the last and the // past-end iterator positions. For list-like containers only invalidate // the last position @@ -513,10 +519,10 @@ void ContainerModeling::handlePushFront(CheckerContext &C, SVal Cont, auto &BVF = SymMgr.getBasicVals(); auto &SVB = C.getSValBuilder(); const auto newBeginSym = - SVB.evalBinOp(State, BO_Sub, - nonloc::SymbolVal(BeginSym), - nonloc::ConcreteInt(BVF.getValue(llvm::APSInt::get(1))), - SymMgr.getType(BeginSym)).getAsSymbol(); + SVB.evalBinOp(State, BO_Sub, nonloc::SymbolVal(BeginSym), + nonloc::ConcreteInt(BVF.getValue(llvm::APSInt::get(1))), + SymMgr.getType(BeginSym)) + .getAsSymbol(); const NoteTag *ChangeTag = getChangeTag(C, "extended to the front by 1 position", ContReg, ContE); State = setContainerData(State, ContReg, CData->newBegin(newBeginSym)); @@ -550,12 +556,12 @@ void ContainerModeling::handlePopFront(CheckerContext &C, SVal Cont, auto &BVF = SymMgr.getBasicVals(); auto &SVB = C.getSValBuilder(); const auto newBeginSym = - SVB.evalBinOp(State, BO_Add, - nonloc::SymbolVal(BeginSym), - nonloc::ConcreteInt(BVF.getValue(llvm::APSInt::get(1))), - SymMgr.getType(BeginSym)).getAsSymbol(); + SVB.evalBinOp(State, BO_Add, nonloc::SymbolVal(BeginSym), + nonloc::ConcreteInt(BVF.getValue(llvm::APSInt::get(1))), + SymMgr.getType(BeginSym)) + .getAsSymbol(); const NoteTag *ChangeTag = - getChangeTag(C, "shrank from the front by 1 position", ContReg, ContE); + getChangeTag(C, "shrank from the front by 1 position", ContReg, ContE); State = setContainerData(State, ContReg, CData->newBegin(newBeginSym)); C.addTransition(State, ChangeTag); } @@ -663,7 +669,7 @@ void ContainerModeling::handleErase(CheckerContext &C, SVal Cont, SVal Iter1, } void ContainerModeling::handleEraseAfter(CheckerContext &C, SVal Cont, - SVal Iter) const { + SVal Iter) const { auto State = C.getState(); const auto *Pos = getIteratorPosition(State, Iter); if (!Pos) @@ -675,10 +681,10 @@ void ContainerModeling::handleEraseAfter(CheckerContext &C, SVal Cont, auto &BVF = SymMgr.getBasicVals(); auto &SVB = C.getSValBuilder(); const auto NextSym = - SVB.evalBinOp(State, BO_Add, - nonloc::SymbolVal(Pos->getOffset()), - nonloc::ConcreteInt(BVF.getValue(llvm::APSInt::get(1))), - SymMgr.getType(Pos->getOffset())).getAsSymbol(); + SVB.evalBinOp(State, BO_Add, nonloc::SymbolVal(Pos->getOffset()), + nonloc::ConcreteInt(BVF.getValue(llvm::APSInt::get(1))), + SymMgr.getType(Pos->getOffset())) + .getAsSymbol(); State = invalidateIteratorPositions(State, NextSym, BO_EQ); C.addTransition(State); } @@ -705,9 +711,9 @@ const NoteTag *ContainerModeling::getChangeTag(CheckerContext &C, // First try to get the name of the variable from the region if (const auto *DR = dyn_cast(ContReg)) { Name = DR->getDecl()->getName(); - // If the region is not a `DeclRegion` then use the expression instead + // If the region is not a `DeclRegion` then use the expression instead } else if (const auto *DRE = - dyn_cast(ContE->IgnoreParenCasts())) { + dyn_cast(ContE->IgnoreParenCasts())) { Name = DRE->getDecl()->getName(); } @@ -725,7 +731,7 @@ const NoteTag *ContainerModeling::getChangeTag(CheckerContext &C, } void ContainerModeling::printState(raw_ostream &Out, ProgramStateRef State, - const char *NL, const char *Sep) const { + const char *NL, const char *Sep) const { auto ContMap = State->get(); if (!ContMap.isEmpty()) { @@ -998,7 +1004,7 @@ ProgramStateRef reassignAllIteratorPositionsUnless(ProgramStateRef State, BinaryOperator::Opcode Opc) { auto MatchContAndCompare = [&](const IteratorPosition &Pos) { return Pos.getContainer() == Cont && - !compare(State, Pos.getOffset(), Offset, Opc); + !compare(State, Pos.getOffset(), Offset, Opc); }; auto ReAssign = [&](const IteratorPosition &Pos) { return Pos.reAssign(NewCont); @@ -1070,7 +1076,7 @@ bool ento::shouldRegisterContainerModeling(const CheckerManager &mgr) { if (!mgr.getAnalyzerOptions().ShouldAggressivelySimplifyBinaryOperation) { mgr.getASTContext().getDiagnostics().Report( diag::err_analyzer_checker_incompatible_analyzer_option) - << "aggressive-binary-operation-simplification" << "false"; + << "aggressive-binary-operation-simplification" << "false"; return false; } diff --git a/clang/test/Analysis/Inputs/system-header-simulator-cxx.h b/clang/test/Analysis/Inputs/system-header-simulator-cxx.h index a379a47515668..5a42a952cca85 100644 --- a/clang/test/Analysis/Inputs/system-header-simulator-cxx.h +++ b/clang/test/Analysis/Inputs/system-header-simulator-cxx.h @@ -336,6 +336,15 @@ namespace std { iterator erase(const_iterator position); iterator erase(const_iterator first, const_iterator last); + template + void assign_range(R&& rg); + + template + iterator insert_range(const_iterator position, R&& rg); + + template + void append_range(R&& rg); + T &operator[](size_t n) { return _start[n]; } @@ -414,6 +423,18 @@ namespace std { iterator erase(const_iterator position); iterator erase(const_iterator first, const_iterator last); + template + void assign_range(R&& rg); + + template + iterator insert_range(const_iterator position, R&& rg); + + template + void append_range(R&& rg); + + template + void prepend_range(R&& rg); + iterator begin() { return iterator(_start); } const_iterator begin() const { return const_iterator(_start); } const_iterator cbegin() const { return const_iterator(_start); } @@ -488,6 +509,18 @@ namespace std { iterator erase(const_iterator position); iterator erase(const_iterator first, const_iterator last); + template + void assign_range(R&& rg); + + template + iterator insert_range(const_iterator position, R&& rg); + + template + void append_range(R&& rg); + + template + void prepend_range(R&& rg); + T &operator[](size_t n) { return _start[n]; } @@ -561,6 +594,15 @@ namespace std { iterator erase_after(const_iterator position); iterator erase_after(const_iterator first, const_iterator last); + template + void assign_range(R&& rg); + + template + void prepend_range(R&& rg); + + template + iterator insert_range_after(const_iterator pos, R&& rg); + iterator begin() { return iterator(_start); } const_iterator begin() const { return const_iterator(_start); } const_iterator cbegin() const { return const_iterator(_start); } diff --git a/clang/test/Analysis/iterator-modeling.cpp b/clang/test/Analysis/iterator-modeling.cpp index 78882da4431fd..d7304f7205fdd 100644 --- a/clang/test/Analysis/iterator-modeling.cpp +++ b/clang/test/Analysis/iterator-modeling.cpp @@ -482,6 +482,39 @@ void forward_list_assign(std::forward_list &FL, int n) { clang_analyzer_eval(clang_analyzer_iterator_validity(i1)); //expected-warning{{FALSE}} } +/// assign_range() +/// +/// - Invalidates all iterators, including the past-the-end iterator for all +/// container types. + +void list_assign_range(std::list &L, std::vector vec) { + auto i0 = L.cbegin(), i1 = L.cend(); + L.assign_range(vec); + clang_analyzer_eval(clang_analyzer_iterator_validity(i0)); //expected-warning{{FALSE}} + clang_analyzer_eval(clang_analyzer_iterator_validity(i1)); //expected-warning{{FALSE}} +} + +void vector_assign_range(std::vector &V, std::vector vec) { + auto i0 = V.cbegin(), i1 = V.cend(); + V.assign_range(vec); + clang_analyzer_eval(clang_analyzer_iterator_validity(i0)); //expected-warning{{FALSE}} + clang_analyzer_eval(clang_analyzer_iterator_validity(i1)); //expected-warning{{FALSE}} +} + +void deque_assign_range(std::deque &D, std::vector vec) { + auto i0 = D.cbegin(), i1 = D.cend(); + D.assign_range(vec); + clang_analyzer_eval(clang_analyzer_iterator_validity(i0)); //expected-warning{{FALSE}} + clang_analyzer_eval(clang_analyzer_iterator_validity(i1)); //expected-warning{{FALSE}} +} + +void forward_list_assign_range(std::forward_list &FL, std::vector vec) { + auto i0 = FL.cbegin(), i1 = FL.cend(); + FL.assign_range(vec); + clang_analyzer_eval(clang_analyzer_iterator_validity(i0)); //expected-warning{{FALSE}} + clang_analyzer_eval(clang_analyzer_iterator_validity(i1)); //expected-warning{{FALSE}} +} + /// clear() /// /// - Invalidates all iterators, including the past-the-end iterator for all @@ -635,6 +668,66 @@ void deque_emplace_back(std::deque &D, int n) { clang_analyzer_eval(clang_analyzer_iterator_validity(i2)); //expected-warning{{FALSE}} } +/// append_range() +/// +/// - Design decision: extends containers to the ->RIGHT-> (i.e. the +/// past-the-end position of the container is incremented). +/// +/// - Iterator invalidation rules depend the container type. + +/// std::list-like containers: No iterators are invalidated. + +void list_append_range(std::list &L, std::vector& vec) { + auto i0 = L.cbegin(), i1 = --L.cend(), i2 = L.cend(); + + clang_analyzer_denote(clang_analyzer_container_begin(L), "$L.begin()"); + clang_analyzer_denote(clang_analyzer_container_end(L), "$L.end()"); + + L.append_range(vec); + + clang_analyzer_eval(clang_analyzer_iterator_validity(i0)); //expected-warning{{TRUE}} + clang_analyzer_eval(clang_analyzer_iterator_validity(i1)); //expected-warning{{TRUE}} + clang_analyzer_eval(clang_analyzer_iterator_validity(i2)); //expected-warning{{TRUE}} + + clang_analyzer_express(clang_analyzer_iterator_position(i0)); // expected-warning-re {{$L.begin(){{$}}}} + clang_analyzer_express(clang_analyzer_iterator_position(i1)); // expected-warning-re {{$L.end() - 1{{$}}}} + clang_analyzer_express(clang_analyzer_iterator_position(i2)); // expected-warning-re {{$L.end(){{$}}}} FIXME: Should be $L.end() + 1 +} + +/// std::vector-like containers: The past-the-end iterator is invalidated. + +void vector_append_range(std::vector &V, std::vector& vec) { + auto i0 = V.cbegin(), i1 = --V.cend(), i2 = V.cend(); + + clang_analyzer_denote(clang_analyzer_container_begin(V), "$V.begin()"); + clang_analyzer_denote(clang_analyzer_container_end(V), "$V.end()"); + + V.emplace_back(vec); + + clang_analyzer_eval(clang_analyzer_iterator_validity(i0)); //expected-warning{{TRUE}} + clang_analyzer_eval(clang_analyzer_iterator_validity(i1)); //expected-warning{{TRUE}} + clang_analyzer_eval(clang_analyzer_iterator_validity(i2)); //expected-warning{{FALSE}} + + clang_analyzer_express(clang_analyzer_iterator_position(i0)); // expected-warning-re {{$V.begin(){{$}}}} + clang_analyzer_express(clang_analyzer_iterator_position(i1)); // expected-warning-re {{$V.end() - 1{{$}}}} +} + +/// std::deque-like containers: All iterators, including the past-the-end +/// iterator, are invalidated. + +void deque_append_range(std::deque &D, std::vector& vec) { + auto i0 = D.cbegin(), i1 = --D.cend(), i2 = D.cend(); + + clang_analyzer_denote(clang_analyzer_container_begin(D), "$D.begin()"); + clang_analyzer_denote(clang_analyzer_container_end(D), "$D.end()"); + + D.append_range(vec); + + clang_analyzer_eval(clang_analyzer_iterator_validity(i0)); //expected-warning{{FALSE}} + clang_analyzer_eval(clang_analyzer_iterator_validity(i1)); //expected-warning{{FALSE}} + clang_analyzer_eval(clang_analyzer_iterator_validity(i2)); //expected-warning{{FALSE}} +} + /// pop_back() /// /// - Design decision: shrinks containers to the <-LEFT<- (i.e. the @@ -811,6 +904,63 @@ void forward_list_emplace_front(std::forward_list &FL, int n) { clang_analyzer_express(clang_analyzer_iterator_position(i1)); // expected-warning-re {{$FL.end(){{$}}}} } +/// prepend_range() +/// +/// - Design decision: extends containers to the <-LEFT<- (i.e. the first +/// position of the container is decremented). +/// +/// - Iterator invalidation rules depend the container type. + +/// std::list-like containers: No iterators are invalidated. + +void list_prepend_range(std::list &L, std::vector& vec) { + auto i0 = L.cbegin(), i1 = L.cend(); + + clang_analyzer_denote(clang_analyzer_container_begin(L), "$L.begin()"); + clang_analyzer_denote(clang_analyzer_container_end(L), "$L.end()"); + + L.prepend_range(vec); + + clang_analyzer_eval(clang_analyzer_iterator_validity(i0)); //expected-warning{{TRUE}} + clang_analyzer_eval(clang_analyzer_iterator_validity(i1)); //expected-warning{{TRUE}} + + clang_analyzer_express(clang_analyzer_iterator_position(i0)); // expected-warning-re {{$L.begin(){{$}}}} + clang_analyzer_express(clang_analyzer_iterator_position(i1)); // expected-warning-re {{$L.end(){{$}}}} +} + +/// std::deque-like containers: All iterators, including the past-the-end +/// iterator, are invalidated. + +void deque_prepend_range(std::deque &D, std::vector& vec) { + auto i0 = D.cbegin(), i1 = --D.cend(), i2 = D.cend(); + + clang_analyzer_denote(clang_analyzer_container_begin(D), "$D.begin()"); + clang_analyzer_denote(clang_analyzer_container_end(D), "$D.end()"); + + D.prepend_range(vec); + + clang_analyzer_eval(clang_analyzer_iterator_validity(i0)); //expected-warning{{FALSE}} + clang_analyzer_eval(clang_analyzer_iterator_validity(i1)); //expected-warning{{FALSE}} + clang_analyzer_eval(clang_analyzer_iterator_validity(i2)); //expected-warning{{FALSE}} +} + +/// std::forward_list-like containers: No iterators are invalidated. + +void forward_list_prepend_range(std::forward_list &FL, std::vector& vec) { + auto i0 = FL.cbegin(), i1 = FL.cend(); + + clang_analyzer_denote(clang_analyzer_container_begin(FL), "$FL.begin()"); + clang_analyzer_denote(clang_analyzer_container_end(FL), "$FL.end()"); + + FL.prepend_range(vec); + + clang_analyzer_eval(clang_analyzer_iterator_validity(i0)); //expected-warning{{TRUE}} + clang_analyzer_eval(clang_analyzer_iterator_validity(i1)); //expected-warning{{TRUE}} + + clang_analyzer_express(clang_analyzer_iterator_position(i0)); // expected-warning-re {{$FL.begin(){{$}}}} + clang_analyzer_express(clang_analyzer_iterator_position(i1)); // expected-warning-re {{$FL.end(){{$}}}} +} + /// pop_front() /// /// - Design decision: shrinks containers to the ->RIGHT-> (i.e. the first @@ -1139,6 +1289,271 @@ void deque_insert_end(std::deque &D, int n) { // clang_analyzer_express(clang_analyzer_iterator_position(i3)); FIXME: expect warning $D.end() - 1 } +/// insert_range() +/// +/// - Design decision: shifts positions to the <-LEFT<- (i.e. all iterator +/// ahead of the insertion point are decremented; if the +/// relation between the insertion point and the first +/// position of the container is known, the first position +/// of the container is also decremented). +/// +/// - Iterator invalidation rules depend the container type. + +/// std::list-like containers: No iterators are invalidated. + +void list_insert_range_begin(std::list &L, std::vector& vec) { + auto i0 = L.cbegin(), i1 = L.cend(); + + clang_analyzer_denote(clang_analyzer_container_begin(L), "$L.begin()"); + clang_analyzer_denote(clang_analyzer_container_end(L), "$L.end()"); + + auto i2 = L.insert_range(i0, vec); + + clang_analyzer_eval(clang_analyzer_iterator_validity(i0)); //expected-warning{{TRUE}} + clang_analyzer_eval(clang_analyzer_iterator_validity(i1)); //expected-warning{{TRUE}} + + clang_analyzer_express(clang_analyzer_iterator_position(i0)); // expected-warning-re {{$L.begin(){{$}}}} + // clang_analyzer_express(clang_analyzer_iterator_position(i2)); FIXME: expect warning $L.begin() - 1 + clang_analyzer_express(clang_analyzer_iterator_position(i1)); // expected-warning-re {{$L.end(){{$}}}} +} + +void list_insert_range_behind_begin(std::list &L, std::vector& vec) { + auto i0 = L.cbegin(), i1 = ++L.cbegin(), i2 = L.cend(); + + clang_analyzer_denote(clang_analyzer_container_begin(L), "$L.begin()"); + clang_analyzer_denote(clang_analyzer_container_end(L), "$L.end()"); + + auto i3 = L.insert_range(i1, vec); + + clang_analyzer_eval(clang_analyzer_iterator_validity(i0)); //expected-warning{{TRUE}} + clang_analyzer_eval(clang_analyzer_iterator_validity(i1)); //expected-warning{{TRUE}} + clang_analyzer_eval(clang_analyzer_iterator_validity(i2)); //expected-warning{{TRUE}} + + clang_analyzer_express(clang_analyzer_iterator_position(i0)); // expected-warning-re {{$L.begin(){{$}}}} FIXME: Should be $L.begin() - 1 + clang_analyzer_express(clang_analyzer_iterator_position(i1)); // expected-warning-re {{$L.begin() + 1{{$}}}} + // clang_analyzer_express(clang_analyzer_iterator_position(i3)); FIXME: expect warning $L.begin() + clang_analyzer_express(clang_analyzer_iterator_position(i2)); // expected-warning-re {{$L.end(){{$}}}} +} + +template Iter return_any_iterator(const Iter &It); + +void list_insert_range_unknown(std::list &L, std::vector& vec) { + auto i0 = L.cbegin(), i1 = return_any_iterator(L.cbegin()), i2 = L.cend(); + + clang_analyzer_denote(clang_analyzer_container_begin(L), "$L.begin()"); + clang_analyzer_denote(clang_analyzer_container_end(L), "$L.end()"); + clang_analyzer_denote(clang_analyzer_iterator_position(i1), "$i1"); + + auto i3 = L.insert_range(i1, vec); + + clang_analyzer_eval(clang_analyzer_iterator_validity(i0)); //expected-warning{{TRUE}} + clang_analyzer_eval(clang_analyzer_iterator_validity(i1)); //expected-warning{{TRUE}} + clang_analyzer_eval(clang_analyzer_iterator_validity(i2)); //expected-warning{{TRUE}} + + clang_analyzer_express(clang_analyzer_iterator_position(i0)); // expected-warning-re {{$L.begin(){{$}}}} + clang_analyzer_express(clang_analyzer_iterator_position(i1)); // expected-warning-re {{$i1{{$}}}} + // clang_analyzer_express(clang_analyzer_iterator_position(i3)); FIXME: expect warning $i - 1 + clang_analyzer_express(clang_analyzer_iterator_position(i2)); // expected-warning-re {{$L.end(){{$}}}} +} + +void list_insert_range_ahead_of_end(std::list &L, std::vector& vec) { + auto i0 = L.cbegin(), i1 = --L.cend(), i2 = L.cend(); + + clang_analyzer_denote(clang_analyzer_container_begin(L), "$L.begin()"); + clang_analyzer_denote(clang_analyzer_container_end(L), "$L.end()"); + + auto i3 = L.insert_range(i1, vec); + + clang_analyzer_eval(clang_analyzer_iterator_validity(i0)); //expected-warning{{TRUE}} + clang_analyzer_eval(clang_analyzer_iterator_validity(i1)); //expected-warning{{TRUE}} + clang_analyzer_eval(clang_analyzer_iterator_validity(i2)); //expected-warning{{TRUE}} + + clang_analyzer_express(clang_analyzer_iterator_position(i0)); // expected-warning-re {{$L.begin(){{$}}}} + clang_analyzer_express(clang_analyzer_iterator_position(i1)); // expected-warning-re {{$L.end() - 1{{$}}}} + clang_analyzer_express(clang_analyzer_iterator_position(i2)); // expected-warning-re {{$L.end(){{$}}}} + // clang_analyzer_express(clang_analyzer_iterator_position(i3)); FIXME: expect warning $L.end() - 2 +} + +void list_insert_range_end(std::list &L, std::vector& vec) { + auto i0 = L.cbegin(), i1 = --L.cend(), i2 = L.cend(); + + clang_analyzer_denote(clang_analyzer_container_begin(L), "$L.begin()"); + clang_analyzer_denote(clang_analyzer_container_end(L), "$L.end()"); + + auto i3 = L.insert_range(i2, vec); + + clang_analyzer_eval(clang_analyzer_iterator_validity(i0)); //expected-warning{{TRUE}} + clang_analyzer_eval(clang_analyzer_iterator_validity(i1)); //expected-warning{{TRUE}} + clang_analyzer_eval(clang_analyzer_iterator_validity(i2)); //expected-warning{{TRUE}} + + clang_analyzer_express(clang_analyzer_iterator_position(i0)); // expected-warning-re {{$L.begin(){{$}}}} + clang_analyzer_express(clang_analyzer_iterator_position(i1)); // expected-warning-re {{$L.end() - 1{{$}}}} FIXME: should be $L.end() - 2 + clang_analyzer_express(clang_analyzer_iterator_position(i2)); // expected-warning-re {{$L.end(){{$}}}} + // clang_analyzer_express(clang_analyzer_iterator_position(i3)); FIXME: expect warning $L.end() - 1 +} + +/// std::vector-like containers: Only the iterators before the insertion point +/// remain valid. The past-the-end iterator is also +/// invalidated. + +void vector_insert_range_begin(std::vector &V, std::vector& vec) { + auto i0 = V.cbegin(), i1 = V.cend(); + + clang_analyzer_denote(clang_analyzer_container_begin(V), "$V.begin()"); + clang_analyzer_denote(clang_analyzer_container_end(V), "$V.end()"); + + auto i2 = V.insert_range(i0, vec); + + clang_analyzer_eval(clang_analyzer_iterator_validity(i0)); //expected-warning{{FALSE}} + clang_analyzer_eval(clang_analyzer_iterator_validity(i1)); //expected-warning{{FALSE}} + + // clang_analyzer_express(clang_analyzer_iterator_position(i2)); FIXME: expect warning $V.begin() - 1 +} + +void vector_insert_range_behind_begin(std::vector &V, std::vector& vec) { + auto i0 = V.cbegin(), i1 = ++V.cbegin(), i2 = V.cend(); + + clang_analyzer_denote(clang_analyzer_container_begin(V), "$V.begin()"); + clang_analyzer_denote(clang_analyzer_container_end(V), "$V.end()"); + + auto i3 = V.insert_range(i1, vec); + + clang_analyzer_eval(clang_analyzer_iterator_validity(i0)); //expected-warning{{TRUE}} + clang_analyzer_eval(clang_analyzer_iterator_validity(i1)); //expected-warning{{FALSE}} + clang_analyzer_eval(clang_analyzer_iterator_validity(i2)); //expected-warning{{FALSE}} + + clang_analyzer_express(clang_analyzer_iterator_position(i0)); // expected-warning-re {{$V.begin(){{$}}}} FIXME: Should be $V.begin() - 1 + // clang_analyzer_express(clang_analyzer_iterator_position(i3)); // FIXME: expect -warning $V.begin() +} + +void vector_insert_range_unknown(std::vector &V, std::vector& vec) { + auto i0 = V.cbegin(), i1 = return_any_iterator(V.cbegin()), i2 = V.cend(); + + clang_analyzer_denote(clang_analyzer_container_begin(V), "$V.begin()"); + clang_analyzer_denote(clang_analyzer_container_end(V), "$V.end()"); + clang_analyzer_denote(clang_analyzer_iterator_position(i1), "$i1"); + + auto i3 = V.insert_range(i1, vec); + + clang_analyzer_eval(clang_analyzer_iterator_validity(i0)); //expected-warning{{TRUE}} + clang_analyzer_eval(clang_analyzer_iterator_validity(i1)); //expected-warning{{FALSE}} + clang_analyzer_eval(clang_analyzer_iterator_validity(i2)); //expected-warning{{FALSE}} + + clang_analyzer_express(clang_analyzer_iterator_position(i0)); // expected-warning-re {{$V.begin(){{$}}}} + // clang_analyzer_express(clang_analyzer_iterator_position(i3)); FIXME: expecte warning $i1 - 1 +} + +void vector_insert_range_ahead_of_end(std::vector &V, std::vector& vec) { + auto i0 = V.cbegin(), i1 = --V.cend(), i2 = V.cend(); + + clang_analyzer_denote(clang_analyzer_container_begin(V), "$V.begin()"); + clang_analyzer_denote(clang_analyzer_container_end(V), "$V.end()"); + + auto i3 = V.insert_range(i1, vec); + + clang_analyzer_eval(clang_analyzer_iterator_validity(i0)); //expected-warning{{TRUE}} + clang_analyzer_eval(clang_analyzer_iterator_validity(i1)); //expected-warning{{FALSE}} + clang_analyzer_eval(clang_analyzer_iterator_validity(i2)); //expected-warning{{FALSE}} + + clang_analyzer_express(clang_analyzer_iterator_position(i0)); // expected-warning-re {{$V.begin(){{$}}}} + // clang_analyzer_express(clang_analyzer_iterator_position(i3)); FIXME: expect warning $V.end() - 2 +} + +void vector_insert_range_end(std::vector &V, std::vector& vec) { + auto i0 = V.cbegin(), i1 = --V.cend(), i2 = V.cend(); + + clang_analyzer_denote(clang_analyzer_container_begin(V), "$V.begin()"); + clang_analyzer_denote(clang_analyzer_container_end(V), "$V.end()"); + + auto i3 = V.insert_range(i2, vec); + + clang_analyzer_eval(clang_analyzer_iterator_validity(i0)); //expected-warning{{TRUE}} + clang_analyzer_eval(clang_analyzer_iterator_validity(i1)); //expected-warning{{TRUE}} + clang_analyzer_eval(clang_analyzer_iterator_validity(i2)); //expected-warning{{FALSE}} + + clang_analyzer_express(clang_analyzer_iterator_position(i0)); // expected-warning-re {{$V.begin(){{$}}}} + clang_analyzer_express(clang_analyzer_iterator_position(i1)); // expected-warning-re {{$V.end() - 1{{$}}}} FIXME: Should be $V.end() - 2 + // clang_analyzer_express(clang_analyzer_iterator_position(i3)); FIXME: expect warning $V.end() - 1 +} + +/// std::deque-like containers: All iterators, including the past-the-end +/// iterator, are invalidated. + +void deque_insert_range_begin(std::deque &D, std::vector& vec) { + auto i0 = D.cbegin(), i1 = D.cend(); + + clang_analyzer_denote(clang_analyzer_container_begin(D), "$D.begin()"); + clang_analyzer_denote(clang_analyzer_container_end(D), "$D.end()"); + + auto i2 = D.insert_range(i0, vec); + + clang_analyzer_eval(clang_analyzer_iterator_validity(i0)); //expected-warning{{FALSE}} + clang_analyzer_eval(clang_analyzer_iterator_validity(i1)); //expected-warning{{FALSE}} + + // clang_analyzer_express(clang_analyzer_iterator_position(i2)); FIXME: expect warning $D.begin() - 1 +} + +void deque_insert_range_behind_begin(std::deque &D, std::vector& vec) { + auto i0 = D.cbegin(), i1 = ++D.cbegin(), i2 = D.cend(); + + clang_analyzer_denote(clang_analyzer_container_begin(D), "$D.begin()"); + clang_analyzer_denote(clang_analyzer_container_end(D), "$D.end()"); + + auto i3 = D.insert_range(i1, vec); + + clang_analyzer_eval(clang_analyzer_iterator_validity(i0)); //expected-warning{{FALSE}} + clang_analyzer_eval(clang_analyzer_iterator_validity(i1)); //expected-warning{{FALSE}} + clang_analyzer_eval(clang_analyzer_iterator_validity(i2)); //expected-warning{{FALSE}} + + // clang_analyzer_express(clang_analyzer_iterator_position(i3)); FIXME: expect warning $D.begin() - 1 +} + +void deque_insert_range_unknown(std::deque &D, std::vector& vec) { + auto i0 = D.cbegin(), i1 = return_any_iterator(D.cbegin()), i2 = D.cend(); + + clang_analyzer_denote(clang_analyzer_container_begin(D), "$D.begin()"); + clang_analyzer_denote(clang_analyzer_container_end(D), "$D.end()"); + clang_analyzer_denote(clang_analyzer_iterator_position(i1), "$i1"); + + auto i3 = D.insert_range(i1, vec); + + clang_analyzer_eval(clang_analyzer_iterator_validity(i0)); //expected-warning{{FALSE}} + clang_analyzer_eval(clang_analyzer_iterator_validity(i1)); //expected-warning{{FALSE}} + clang_analyzer_eval(clang_analyzer_iterator_validity(i2)); //expected-warning{{FALSE}} + + // clang_analyzer_express(clang_analyzer_iterator_position(i3)); FIXME: expect warning $i1 - 1 +} + +void deque_insert_range_ahead_of_end(std::deque &D, std::vector& vec) { + auto i0 = D.cbegin(), i1 = --D.cend(), i2 = D.cend(); + + clang_analyzer_denote(clang_analyzer_container_begin(D), "$D.begin()"); + clang_analyzer_denote(clang_analyzer_container_end(D), "$D.end()"); + + auto i3 = D.insert_range(i1, vec); + + clang_analyzer_eval(clang_analyzer_iterator_validity(i0)); //expected-warning{{FALSE}} + clang_analyzer_eval(clang_analyzer_iterator_validity(i1)); //expected-warning{{FALSE}} + clang_analyzer_eval(clang_analyzer_iterator_validity(i2)); //expected-warning{{FALSE}} + + // clang_analyzer_express(clang_analyzer_iterator_position(i3)); FIXME: expect warning $D.end() - 2 +} + +void deque_insert_range_end(std::deque &D, std::vector& vec) { + auto i0 = D.cbegin(), i1 = --D.cend(), i2 = D.cend(); + + clang_analyzer_denote(clang_analyzer_container_begin(D), "$D.begin()"); + clang_analyzer_denote(clang_analyzer_container_end(D), "$D.end()"); + + auto i3 = D.insert_range(i2, vec); + + clang_analyzer_eval(clang_analyzer_iterator_validity(i0)); //expected-warning{{FALSE}} + clang_analyzer_eval(clang_analyzer_iterator_validity(i1)); //expected-warning{{FALSE}} + clang_analyzer_eval(clang_analyzer_iterator_validity(i2)); //expected-warning{{FALSE}} + + // clang_analyzer_express(clang_analyzer_iterator_position(i3)); FIXME: expect warning $D.end() - 1 +} + /// insert_after() [std::forward_list-like containers] /// /// - Design decision: shifts positions to the ->RIGHT-> (i.e. all iterator @@ -1202,6 +1617,69 @@ void forward_list_insert_after_unknown(std::forward_list &FL, int n) { clang_analyzer_express(clang_analyzer_iterator_position(i2)); // expected-warning-re {{$FL.end(){{$}}}} } +/// insert_range_after() [std::forward_list-like containers] +/// +/// - Design decision: shifts positions to the ->RIGHT-> (i.e. all iterator +/// ahead of the insertion point are incremented; if the +/// relation between the insertion point and the past-the-end +/// position of the container is known, the first position of +/// the container is also incremented). +/// +/// - No iterators are invalidated. + +void forward_list_insert_range_after_begin(std::forward_list &FL, std::vector& vec) { + auto i0 = FL.cbegin(), i1 = FL.cend(); + + clang_analyzer_denote(clang_analyzer_container_begin(FL), "$FL.begin()"); + clang_analyzer_denote(clang_analyzer_container_end(FL), "$FL.end()"); + + auto i2 = FL.insert_range_after(i0, vec); + + clang_analyzer_eval(clang_analyzer_iterator_validity(i0)); //expected-warning{{TRUE}} + clang_analyzer_eval(clang_analyzer_iterator_validity(i1)); //expected-warning{{TRUE}} + + clang_analyzer_express(clang_analyzer_iterator_position(i0)); // expected-warning-re {{$FL.begin(){{$}}}} + // clang_analyzer_express(clang_analyzer_iterator_position(i2)); FIXME: expect warning $FL.begin() + 1 + clang_analyzer_express(clang_analyzer_iterator_position(i1)); // expected-warning-re {{$FL.end(){{$}}}} +} + +void forward_list_insert_range_after_behind_begin(std::forward_list &FL, std::vector& vec) { + auto i0 = FL.cbegin(), i1 = ++FL.cbegin(), i2 = FL.cend(); + + clang_analyzer_denote(clang_analyzer_container_begin(FL), "$FL.begin()"); + clang_analyzer_denote(clang_analyzer_container_end(FL), "$FL.end()"); + + auto i3 = FL.insert_range_after(i1, vec); + + clang_analyzer_eval(clang_analyzer_iterator_validity(i0)); //expected-warning{{TRUE}} + clang_analyzer_eval(clang_analyzer_iterator_validity(i1)); //expected-warning{{TRUE}} + clang_analyzer_eval(clang_analyzer_iterator_validity(i2)); //expected-warning{{TRUE}} + + clang_analyzer_express(clang_analyzer_iterator_position(i0)); // expected-warning-re {{$FL.begin(){{$}}}} + clang_analyzer_express(clang_analyzer_iterator_position(i1)); // expected-warning-re {{$FL.begin() + 1{{$}}}} + // clang_analyzer_express(clang_analyzer_iterator_position(i3)); FIXME: expect warning $FL.begin() + 2 + clang_analyzer_express(clang_analyzer_iterator_position(i2)); // expected-warning-re {{$FL.end(){{$}}}} +} + +void forward_list_insert_range_after_unknown(std::forward_list &FL, std::vector& vec) { + auto i0 = FL.cbegin(), i1 = return_any_iterator(FL.cbegin()), i2 = FL.cend(); + + clang_analyzer_denote(clang_analyzer_container_begin(FL), "$FL.begin()"); + clang_analyzer_denote(clang_analyzer_container_end(FL), "$FL.end()"); + clang_analyzer_denote(clang_analyzer_iterator_position(i1), "$i1"); + + auto i3 = FL.insert_range_after(i1, vec); + + clang_analyzer_eval(clang_analyzer_iterator_validity(i0)); //expected-warning{{TRUE}} + clang_analyzer_eval(clang_analyzer_iterator_validity(i1)); //expected-warning{{TRUE}} + clang_analyzer_eval(clang_analyzer_iterator_validity(i2)); //expected-warning{{TRUE}} + + clang_analyzer_express(clang_analyzer_iterator_position(i0)); // expected-warning-re {{$FL.begin(){{$}}}} + clang_analyzer_express(clang_analyzer_iterator_position(i1)); // expected-warning-re {{$i1{{$}}}} + // clang_analyzer_express(clang_analyzer_iterator_position(i3)); FIXME: expect warning $i1 + 1 + clang_analyzer_express(clang_analyzer_iterator_position(i2)); // expected-warning-re {{$FL.end(){{$}}}} +} + /// emplace() /// /// - Design decision: shifts positions to the <-LEFT<- (i.e. all iterator