-
Notifications
You must be signed in to change notification settings - Fork 15.4k
[clang][analyzer] Add support for C++23 container methods releated to iterator in ContainerModeling #129711
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Closed
flovent
wants to merge
2
commits into
llvm:main
from
flovent:ContainerModeling-add-cxx23-new-method-model
Closed
[clang][analyzer] Add support for C++23 container methods releated to iterator in ContainerModeling #129711
flovent
wants to merge
2
commits into
llvm:main
from
flovent:ContainerModeling-add-cxx23-new-method-model
Conversation
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
… iterator in ContainerModeling
Member
|
@llvm/pr-subscribers-clang-static-analyzer-1 @llvm/pr-subscribers-clang Author: None (flovent) Changesthis PR add support for the following container methods introduced by C++23 in ContainerModeling:
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:
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]
|
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
this PR add support for the following container methods introduced by C++23 in ContainerModeling:
assign_range: range version ofassign, have the same effect for iteratorappend_range: range version ofpush_back, have the same effect for iteratorprepend_range: range version ofpush_front, have the same effect for iteratorinsert_range: range version ofinsert, have the same effect for iteratorinsert_range_after: range version ofinsert_after, no effect for iterator