Skip to content

Conversation

@flovent
Copy link
Contributor

@flovent flovent commented Mar 4, 2025

this PR add support for the following container methods introduced by C++23 in ContainerModeling:

  1. assign_range: range version of assign, have the same effect for iterator
  2. append_range: range version of push_back, have the same effect for iterator
  3. prepend_range: range version of push_front, have the same effect for iterator
  4. insert_range: range version of insert, have the same effect for iterator
  5. insert_range_after: range version of insert_after, no effect for iterator

@llvmbot llvmbot added clang Clang issues not falling into any other category clang:static analyzer labels Mar 4, 2025
@llvmbot
Copy link
Member

llvmbot commented Mar 4, 2025

@llvm/pr-subscribers-clang-static-analyzer-1

@llvm/pr-subscribers-clang

Author: None (flovent)

Changes

this PR add support for the following container methods introduced by C++23 in ContainerModeling:

  1. assign_range: range version of assign, have the same effect for iterator
  2. append_range: range version of push_back, have the same effect for iterator
  3. prepend_range: range version of push_front, have the same effect for iterator
  4. insert_range: range version of insert, have the same effect for iterator
  5. insert_range_after: range version of insert_after, no effect for iterator

Patch is 36.82 KiB, truncated to 20.00 KiB below, full version: https://github.com/llvm/llvm-project/pull/129711.diff

3 Files Affected:

  • (modified) clang/lib/StaticAnalyzer/Checkers/ContainerModeling.cpp (+26-20)
  • (modified) clang/test/Analysis/Inputs/system-header-simulator-cxx.h (+42)
  • (modified) clang/test/Analysis/iterator-modeling.cpp (+478)
diff --git a/clang/lib/StaticAnalyzer/Checkers/ContainerModeling.cpp b/clang/lib/StaticAnalyzer/Checkers/ContainerModeling.cpp
index 55ed809bfed6c..62b98942788c7 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<check::PostCall, check::LiveSymbols, check::DeadSymbols> {
+    : public Checker<check::PostCall, check::LiveSymbols, check::DeadSymbols> {
 
   void handleBegin(CheckerContext &C, const Expr *CE, SVal RetVal,
                    SVal Cont) const;
@@ -74,19 +74,25 @@ class ContainerModeling
   CallDescriptionMap<NoItParamFn> 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<OneItParamFn> 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<FunctionDecl>(Call.getDecl());
   if (!Func)
     return;
@@ -161,7 +167,7 @@ void ContainerModeling::checkPostCall(const CallEvent &Call,
       const auto *InstCall = cast<CXXInstanceCall>(&Call);
       if (cast<CXXMethodDecl>(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;
@@ -441,10 +447,10 @@ void ContainerModeling::handlePushBack(CheckerContext &C, SVal Cont,
     const auto newEndSym =
       SVB.evalBinOp(State, BO_Add,
                     nonloc::SymbolVal(EndSym),
-                    nonloc::ConcreteInt(BVF.getValue(llvm::APSInt::get(1))),
+                      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);
   }
@@ -470,10 +476,10 @@ void ContainerModeling::handlePopBack(CheckerContext &C, SVal Cont,
     const auto BackSym =
       SVB.evalBinOp(State, BO_Sub,
                     nonloc::SymbolVal(EndSym),
-                    nonloc::ConcreteInt(BVF.getValue(llvm::APSInt::get(1))),
+                      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
@@ -515,7 +521,7 @@ void ContainerModeling::handlePushFront(CheckerContext &C, SVal Cont,
       const auto newBeginSym =
         SVB.evalBinOp(State, BO_Sub,
                       nonloc::SymbolVal(BeginSym),
-                      nonloc::ConcreteInt(BVF.getValue(llvm::APSInt::get(1))),
+                        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);
@@ -552,10 +558,10 @@ void ContainerModeling::handlePopFront(CheckerContext &C, SVal Cont,
     const auto newBeginSym =
       SVB.evalBinOp(State, BO_Add,
                     nonloc::SymbolVal(BeginSym),
-                    nonloc::ConcreteInt(BVF.getValue(llvm::APSInt::get(1))),
+                      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)
@@ -677,7 +683,7 @@ void ContainerModeling::handleEraseAfter(CheckerContext &C, SVal Cont,
   const auto NextSym =
     SVB.evalBinOp(State, BO_Add,
                   nonloc::SymbolVal(Pos->getOffset()),
-                  nonloc::ConcreteInt(BVF.getValue(llvm::APSInt::get(1))),
+                    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<DeclRegion>(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<DeclRefExpr>(ContE->IgnoreParenCasts())) {
+                 dyn_cast<DeclRefExpr>(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<ContainerMap>();
 
   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<typename R>
+    void assign_range(R&& rg);
+
+    template<typename R>
+    iterator insert_range(const_iterator position, R&& rg);
+
+    template<typename R>
+    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<typename R>
+    void assign_range(R&& rg);
+
+    template<typename R>
+    iterator insert_range(const_iterator position, R&& rg);
+
+    template<typename R>
+    void append_range(R&& rg);
+
+    template<typename R>
+    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<typename R>
+    void assign_range(R&& rg);
+
+    template<typename R>
+    iterator insert_range(const_iterator position, R&& rg);
+
+    template<typename R>
+    void append_range(R&& rg);
+
+    template<typename R>
+    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<typename R>
+    void assign_range(R&& rg);
+
+    template<typename R>
+    void prepend_range(R&& rg);
+
+    template<typename R>
+    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<int> &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<int> &L, std::vector<int> 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<int> &V, std::vector<int> 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<int> &D, std::vector<int> 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<int> &FL, std::vector<int> 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<int> &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<int> &L, std::vector<int>& 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<int> &V, std::vector<int>& 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<int> &D, std::vector<int>& 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<int> &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<int> &L, std::vector<int>& 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<int> &D, std::vector<int>& 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<int> &FL, std::vector<int>& 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<int> &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 inv...
[truncated]

@flovent flovent closed this Mar 4, 2025
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

clang:static analyzer clang Clang issues not falling into any other category

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants