Index: include/clang/StaticAnalyzer/Checkers/Checkers.td =================================================================== --- include/clang/StaticAnalyzer/Checkers/Checkers.td +++ include/clang/StaticAnalyzer/Checkers/Checkers.td @@ -284,6 +284,10 @@ HelpText<"Check iterators used past end">, DescFile<"IteratorPastEndChecker.cpp">; +def MismatchedIteratorChecker : Checker<"MismatchedIterator">, + HelpText<"Check iterators used for the wrong container">, + DescFile<"MismatchedIteratorChecker.cpp">; + } // end: "alpha.cplusplus" Index: lib/StaticAnalyzer/Checkers/CMakeLists.txt =================================================================== --- lib/StaticAnalyzer/Checkers/CMakeLists.txt +++ lib/StaticAnalyzer/Checkers/CMakeLists.txt @@ -48,6 +48,7 @@ MallocChecker.cpp MallocOverflowSecurityChecker.cpp MallocSizeofChecker.cpp + MismatchedIteratorChecker.cpp MPI-Checker/MPIBugReporter.cpp MPI-Checker/MPIChecker.cpp MPI-Checker/MPIFunctionClassifier.cpp Index: lib/StaticAnalyzer/Checkers/MismatchedIteratorChecker.cpp =================================================================== --- /dev/null +++ lib/StaticAnalyzer/Checkers/MismatchedIteratorChecker.cpp @@ -0,0 +1,562 @@ +//===-- MismatchedIteratorChecker.cpp -----------------------------*- C++ -*--// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// Defines a checker for using iterators outside their range (past end). Usage +// means here dereferencing, incrementing, decrementing etc. +// +//===----------------------------------------------------------------------===// + +#include "ClangSACheckers.h" +#include "clang/AST/DeclTemplate.h" +#include "clang/StaticAnalyzer/Core/BugReporter/BugType.h" +#include "clang/StaticAnalyzer/Core/Checker.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h" + +#include + +using namespace clang; +using namespace ento; + +namespace { +class MismatchedIteratorChecker + : public Checker< + check::PreCall, check::PostCall, check::PreStmt, + check::PostStmt, check::PostStmt, + check::PostStmt, check::BeginFunction, + check::DeadSymbols> { + + std::unique_ptr MismatchedBugType; + + void verifyMatch(CheckerContext &C, const SVal &Iter, + const MemRegion *Cont) const; + void verifyMatch(CheckerContext &C, const SVal &Iter1, + const SVal &Iter2) const; + void handleBeginOrEnd(CheckerContext &C, const SVal &RetVal, + const SVal &Cont) const; + void handleAssignment(CheckerContext &C, const SVal &LHS, + const SVal &RHS) const; + + void reportMismatchedBug(const StringRef &Message, const SVal &Val, + CheckerContext &C, ExplodedNode *ErrNode) const; + +public: + MismatchedIteratorChecker(); + + void checkPreCall(const CallEvent &Call, CheckerContext &C) const; + void checkPostCall(const CallEvent &Call, CheckerContext &C) const; + void checkPreStmt(const CXXConstructExpr *CCE, CheckerContext &C) const; + void checkBeginFunction(CheckerContext &C) const; + void checkPostStmt(const CXXConstructExpr *CCE, CheckerContext &C) const; + void checkPostStmt(const DeclStmt *DS, CheckerContext &C) const; + void checkPostStmt(const MaterializeTemporaryExpr *MTE, + CheckerContext &C) const; + void checkDeadSymbols(SymbolReaper &SR, CheckerContext &C) const; +}; +} + +REGISTER_MAP_WITH_PROGRAMSTATE(IteratorSymbolMap, SymbolRef, const MemRegion *) +REGISTER_MAP_WITH_PROGRAMSTATE(IteratorRegionMap, const MemRegion *, + const MemRegion *) + +namespace { + +static bool isIteratorType(const QualType &Type); +static bool isIterator(const CXXRecordDecl *CRD); +static bool isComparisonOperator(OverloadedOperatorKind OK); +static bool isBeginOrEndCall(const FunctionDecl *Func); +static bool isInsertCall(const FunctionDecl *Func); +static bool isEraseCall(const FunctionDecl *Func); +static bool isEmplaceCall(const FunctionDecl *Func); +static const MemRegion *const *getIteratorContainer(ProgramStateRef State, + const SVal &Val); +static ProgramStateRef setIteratorContainer(ProgramStateRef State, + const SVal &Val, + const MemRegion *Cont); +static ProgramStateRef removeIteratorContainer(ProgramStateRef State, + const SVal &Val); +} + +MismatchedIteratorChecker::MismatchedIteratorChecker() { + MismatchedBugType.reset( + new BugType(this, "Mismatched Iterator", "C++ STL Error")); + MismatchedBugType->setSuppressOnSink(true); +} + +void MismatchedIteratorChecker::checkPreCall(const CallEvent &Call, + CheckerContext &C) const { + const auto *Func = dyn_cast_or_null(Call.getDecl()); + if (!Func) + return; + + if (Func->isOverloadedOperator()) { + if (!isComparisonOperator(Func->getOverloadedOperator())) + return; + + if (const auto *InstCall = dyn_cast(&Call)) { + if (Call.getNumArgs() < 1) + return; + + if (!isIteratorType(InstCall->getCXXThisExpr()->getType()) || + !isIteratorType(Call.getArgExpr(0)->getType())) + return; + + verifyMatch(C, InstCall->getCXXThisVal(), Call.getArgSVal(0)); + } else { + if (Call.getNumArgs() < 2) + return; + + if (!isIteratorType(Call.getArgExpr(0)->getType()) || + !isIteratorType(Call.getArgExpr(1)->getType())) + return; + + verifyMatch(C, Call.getArgSVal(0), Call.getArgSVal(1)); + } + } else if (const auto *InstCall = dyn_cast(&Call)) { + const auto *ContReg = InstCall->getCXXThisVal().getAsRegion(); + if (!ContReg) + return; + if (isEraseCall(Func)) { + verifyMatch(C, Call.getArgSVal(0), + InstCall->getCXXThisVal().getAsRegion()); + if (Call.getNumArgs() == 2) + verifyMatch(C, Call.getArgSVal(1), + InstCall->getCXXThisVal().getAsRegion()); + } else if (isInsertCall(Func)) { + verifyMatch(C, Call.getArgSVal(0), + InstCall->getCXXThisVal().getAsRegion()); + if (Call.getNumArgs() == 3 && + isIteratorType(Call.getArgExpr(1)->getType()) && + isIteratorType(Call.getArgExpr(2)->getType())) { + verifyMatch(C, Call.getArgSVal(1), Call.getArgSVal(2)); + } + } else if (isEmplaceCall(Func)) { + verifyMatch(C, Call.getArgSVal(0), + InstCall->getCXXThisVal().getAsRegion()); + } + } else if (!isa(&Call)) { + // The main purpose of iterators is to abstract away from different + // containers and provide a (maybe limited) uniform access to them. + // This implies that any correctly written template function that + // works on multiple containers using iterators takes different + // template parameters for different containers. So we can safely + // assume that passing iterators of different containers as arguments + // whose type replaces the same template parameter is a bug. + const auto *Templ = Func->getPrimaryTemplate(); + if (!Templ) + return; + + const auto *TParams = Templ->getTemplateParameters(); + const auto *TArgs = Func->getTemplateSpecializationArgs(); + + for (auto i = 0U; i < TParams->size(); ++i) { + const auto *TPDecl = dyn_cast(TParams->getParam(i)); + if (!TPDecl) + continue; + + const auto TAType = TArgs->get(i).getAsType(); + if (!isIteratorType(TAType)) + continue; + + SVal LHS = UndefinedVal(); + + for (auto j = 0U; j < Func->getNumParams(); ++j) { + const auto *Param = Func->getParamDecl(j); + const auto *ParamType = + Param->getType()->getAs(); + if (!ParamType || + ParamType->getReplacedParameter()->getDecl() != TPDecl) + continue; + if (LHS.isUndef()) { + LHS = Call.getArgSVal(j); + } else { + verifyMatch(C, LHS, Call.getArgSVal(j)); + } + } + } + } +} + +void MismatchedIteratorChecker::checkPostCall(const CallEvent &Call, + CheckerContext &C) const { + const auto *Func = dyn_cast_or_null(Call.getDecl()); + if (!Func) + return; + + if (const auto *Method = dyn_cast(Func)) { + if (Method->isCopyAssignmentOperator() || + Method->isMoveAssignmentOperator()) { + const auto *InstCall = cast(&Call); + handleAssignment(C, InstCall->getCXXThisVal(), Call.getArgSVal(0)); + } + } + + if (!Call.getOriginExpr()) + return; + + if (!isIteratorType(Call.getResultType())) + return; + + auto State = C.getState(); + // Already bound to container? + if (getIteratorContainer(State, Call.getReturnValue())) + return; + + if (const auto *InstCall = dyn_cast(&Call)) { + if (isBeginOrEndCall(Func)) { + handleBeginOrEnd(C, Call.getReturnValue(), InstCall->getCXXThisVal()); + return; + } + } + + // Assumption: if return value is an iterator which is not yet bound to a + // container, then look for the first iterator argument, and + // bind the return value to the same container. This approach + // works for STL algorithms. + for (unsigned i = 0; i < Call.getNumArgs(); ++i) { + if (isIteratorType(Call.getArgExpr(i)->getType())) { + if (const auto *Cont = getIteratorContainer(State, Call.getArgSVal(i))) { + State = setIteratorContainer(State, Call.getReturnValue(), *Cont); + C.addTransition(State); + return; + } + } + } +} + +void MismatchedIteratorChecker::checkPreStmt(const CXXConstructExpr *CCE, + CheckerContext &C) const { + if (CCE->getNumArgs() < 2) + return; + + const auto *Ctr = CCE->getConstructor(); + if (Ctr->getNumParams() < 2) + return; + + if (Ctr->getParamDecl(0)->getName() != "first" || + Ctr->getParamDecl(1)->getName() != "last") + return; + + if (!isIteratorType(CCE->getArg(0)->getType()) || + !isIteratorType(CCE->getArg(1)->getType())) + return; + + auto State = C.getState(); + const auto *LCtx = C.getPredecessor()->getLocationContext(); + + verifyMatch(C, State->getSVal(CCE->getArg(0), LCtx), + State->getSVal(CCE->getArg(1), LCtx)); +} + +void MismatchedIteratorChecker::checkBeginFunction(CheckerContext &C) const { + // Copy container of iterator arguments to iterator parameters + auto State = C.getState(); + const auto *LCtx = C.getLocationContext(); + + const auto *Site = cast(LCtx)->getCallSite(); + if (!Site) + return; + + const auto *FD = dyn_cast(LCtx->getDecl()); + if (!FD) + return; + + const auto *CE = dyn_cast(Site); + if (!CE) + return; + + bool Change = false; + int idx = 0; + for (const auto P : FD->parameters()) { + auto Param = State->getLValue(P, LCtx); + auto Arg = State->getSVal(CE->getArg(idx++), LCtx->getParent()); + const auto *const *Cont = getIteratorContainer(State, Arg); + if (!Cont) + continue; + State = setIteratorContainer(State, Param, *Cont); + Change = true; + } + if (Change) { + C.addTransition(State); + } +} + +void MismatchedIteratorChecker::checkPostStmt(const CXXConstructExpr *CCE, + CheckerContext &C) const { + // Transfer iterator container in case of copy or move by constructor + const auto *ctr = CCE->getConstructor(); + if (!ctr->isCopyOrMoveConstructor()) + return; + const auto *RHSExpr = CCE->getArg(0); + + auto State = C.getState(); + const auto *LCtx = C.getLocationContext(); + + const auto RetVal = State->getSVal(CCE, LCtx); + + const auto RHSVal = State->getSVal(RHSExpr, LCtx); + const auto *const *RHSCont = getIteratorContainer(State, RHSVal); + if (!RHSCont) + return; + State = setIteratorContainer(State, RetVal, *RHSCont); + C.addTransition(State); +} + +void MismatchedIteratorChecker::checkPostStmt(const DeclStmt *DS, + CheckerContext &C) const { + // Transfer iterator container to new variable declaration + for (const auto *D : DS->decls()) { + const auto *VD = dyn_cast(D); + if (!VD || !VD->hasInit()) + continue; + + auto State = C.getState(); + const auto *LCtx = C.getLocationContext(); + const auto *const *Cont = + getIteratorContainer(State, State->getSVal(VD->getInit(), LCtx)); + if (!Cont) + continue; + State = setIteratorContainer(State, State->getLValue(VD, LCtx), *Cont); + C.addTransition(State); + } +} + +void MismatchedIteratorChecker::checkPostStmt( + const MaterializeTemporaryExpr *MTE, CheckerContext &C) const { + /* Transfer iterator container for to temporary objects */ + auto State = C.getState(); + const auto *LCtx = C.getLocationContext(); + const auto *const *Cont = getIteratorContainer( + State, State->getSVal(MTE->GetTemporaryExpr(), LCtx)); + if (!Cont) + return; + State = setIteratorContainer(State, State->getSVal(MTE, LCtx), *Cont); + C.addTransition(State); +} + +void MismatchedIteratorChecker::checkDeadSymbols(SymbolReaper &SR, + CheckerContext &C) const { + auto State = C.getState(); + + auto RegionMap = State->get(); + for (auto I = RegionMap.begin(), E = RegionMap.end(); I != E; ++I) { + if (!SR.isLiveRegion(I->first)) { + State = State->remove(I->first); + } + } + + auto SymbolMap = State->get(); + for (auto I = SymbolMap.begin(), E = SymbolMap.end(); I != E; ++I) { + if (SR.isDead(I->first)) { + State = State->remove(I->first); + } + } +} + +void MismatchedIteratorChecker::verifyMatch(CheckerContext &C, const SVal &Iter, + const MemRegion *Cont) const { + auto State = C.getState(); + const auto *const *IterCont = getIteratorContainer(State, Iter); + if (IterCont && *IterCont != Cont) { + auto *N = C.generateNonFatalErrorNode(State); + if (!N) { + return; + } + reportMismatchedBug("Iterator access mismatched.", Iter, C, N); + } +} + +void MismatchedIteratorChecker::verifyMatch(CheckerContext &C, + const SVal &Iter1, + const SVal &Iter2) const { + auto State = C.getState(); + const auto *const *IterCont1 = getIteratorContainer(State, Iter1); + const auto *const *IterCont2 = getIteratorContainer(State, Iter2); + if (IterCont1 && IterCont2 && *IterCont1 != *IterCont2) { + auto *N = C.generateNonFatalErrorNode(State); + if (!N) { + return; + } + reportMismatchedBug("Iterator access mismatched.", Iter1, C, N); + } +} + +void MismatchedIteratorChecker::handleBeginOrEnd(CheckerContext &C, + const SVal &RetVal, + const SVal &Cont) const { + const auto *ContReg = Cont.getAsRegion(); + if (!ContReg) + return; + + auto State = C.getState(); + State = setIteratorContainer(State, RetVal, ContReg); + C.addTransition(State); +} + +void MismatchedIteratorChecker::handleAssignment(CheckerContext &C, + const SVal &LHS, + const SVal &RHS) const { + auto State = C.getState(); + const auto *Cont = getIteratorContainer(State, RHS); + if (Cont) { + State = setIteratorContainer(State, LHS, *Cont); + } else { + State = removeIteratorContainer(State, LHS); + } + C.addTransition(State); +} + +void MismatchedIteratorChecker::reportMismatchedBug( + const StringRef &Message, const SVal &Val, CheckerContext &C, + ExplodedNode *ErrNode) const { + auto R = llvm::make_unique(*MismatchedBugType, Message, ErrNode); + R->markInteresting(Val); + C.emitReport(std::move(R)); +} + +namespace { + +static bool isIteratorType(const QualType &Type) { + const auto *CRD = Type->getUnqualifiedDesugaredType()->getAsCXXRecordDecl(); + return isIterator(CRD); +} + +static bool isIterator(const CXXRecordDecl *CRD) { + if (!CRD) + return false; + + const auto Name = CRD->getName(); + if (!(Name.endswith_lower("iterator") || Name.endswith_lower("iter") || + Name.endswith_lower("it"))) + return false; + + bool CopyC = false, CopyA = true, Dest = false, PreIncr = false, + PostIncr = false, Deref = false; + for (const auto *M : CRD->methods()) { + if (const auto *C = dyn_cast(M)) { + if (C->isCopyConstructor()) { + CopyC = !C->isDeleted() && C->getAccess() == AS_public; + } + continue; + } + if (const auto *D = dyn_cast(M)) { + Dest = !D->isDeleted(); + continue; + } + if (M->isCopyAssignmentOperator()) { + CopyA = !M->isDeleted() && M->getAccess() == AS_public; + continue; + } + if (!M->isOverloadedOperator()) + continue; + const auto OPK = M->getOverloadedOperator(); + if (OPK == OO_PlusPlus) { + PreIncr = PreIncr || (M->getNumParams() == 0); + PostIncr = PostIncr || (M->getNumParams() == 1); + continue; + } + if (OPK == OO_Star) { + Deref = (M->getNumParams() == 0); + continue; + } + } + + return CopyC && CopyA && Dest && PreIncr && PostIncr && Deref; +} + +static bool isComparisonOperator(OverloadedOperatorKind OK) { + return OK == OO_EqualEqual || OK == OO_ExclaimEqual || OK == OO_Less || + OK == OO_LessEqual || OK == OO_Greater || OK == OO_GreaterEqual; +} + +static bool isBeginOrEndCall(const FunctionDecl *Func) { + const auto *IdInfo = Func->getIdentifier(); + if (!IdInfo) + return false; + return IdInfo->getName().endswith_lower("begin") || + IdInfo->getName().endswith_lower("end"); +} + +static bool isInsertCall(const FunctionDecl *Func) { + const auto *IdInfo = Func->getIdentifier(); + if (!IdInfo) + return false; + if (Func->getNumParams() < 2 || Func->getNumParams() > 3) + return false; + if (!isIteratorType(Func->getParamDecl(0)->getType())) + return false; + return IdInfo->getName() == "insert"; +} + +static bool isEraseCall(const FunctionDecl *Func) { + const auto *IdInfo = Func->getIdentifier(); + if (!IdInfo) + return false; + if (Func->getNumParams() < 1 || Func->getNumParams() > 2) + return false; + if (!isIteratorType(Func->getParamDecl(0)->getType())) + return false; + if (Func->getNumParams() == 2 && + !isIteratorType(Func->getParamDecl(1)->getType())) + return false; + return IdInfo->getName() == "erase"; +} + +static bool isEmplaceCall(const FunctionDecl *Func) { + const auto *IdInfo = Func->getIdentifier(); + if (!IdInfo) + return false; + if (Func->getNumParams() < 2) + return false; + if (!isIteratorType(Func->getParamDecl(0)->getType())) + return false; + return IdInfo->getName() == "emplace"; +} + +static const MemRegion *const *getIteratorContainer(ProgramStateRef State, + const SVal &Val) { + if (const auto Reg = Val.getAsRegion()) { + return State->get(Reg); + } else if (const auto Sym = Val.getAsSymbol()) { + return State->get(Sym); + } else if (const auto LCVal = Val.getAs()) { + return State->get(LCVal->getRegion()); + } + return nullptr; +} + +static ProgramStateRef setIteratorContainer(ProgramStateRef State, + const SVal &Val, + const MemRegion *Cont) { + if (const auto Reg = Val.getAsRegion()) { + return State->set(Reg, Cont); + } else if (const auto Sym = Val.getAsSymbol()) { + return State->set(Sym, Cont); + } else if (const auto LCVal = Val.getAs()) { + return State->set(LCVal->getRegion(), Cont); + } + return nullptr; +} + +static ProgramStateRef removeIteratorContainer(ProgramStateRef State, + const SVal &Val) { + if (const auto Reg = Val.getAsRegion()) { + return State->remove(Reg); + } else if (const auto Sym = Val.getAsSymbol()) { + return State->remove(Sym); + } else if (const auto LCVal = Val.getAs()) { + return State->remove(LCVal->getRegion()); + } + return nullptr; +} +} + +void ento::registerMismatchedIteratorChecker(CheckerManager &Mgr) { + Mgr.registerChecker(); +} Index: test/Analysis/Inputs/system-header-simulator-cxx.h =================================================================== --- test/Analysis/Inputs/system-header-simulator-cxx.h +++ test/Analysis/Inputs/system-header-simulator-cxx.h @@ -14,7 +14,8 @@ typedef __iterator iterator; typedef __iterator const_iterator; - __iterator(const Ptr p) : ptr(p) {} + __iterator(const Ptr p): ptr(p) {} + __iterator(const iterator &rhs): ptr(rhs.base()) {} __iterator operator++() { return *this; } __iterator operator++(int) { return *this; } @@ -29,6 +30,7 @@ bool operator!=(const iterator &rhs) const { return ptr != rhs.ptr; } bool operator!=(const const_iterator &rhs) const { return ptr != rhs.ptr; } + const Ptr& base() const { return ptr; } private: Ptr ptr; }; @@ -47,9 +49,13 @@ }; typedef __typeof__(sizeof(int)) size_t; + + template class initializer_list; template class vector { + typedef T value_type; + typedef size_t size_type; typedef __iterator iterator; typedef __iterator const_iterator; @@ -58,6 +64,8 @@ T *_end_of_storage; public: vector() : _start(0), _finish(0), _end_of_storage(0) {} + template + vector(InputIterator first, InputIterator last); ~vector(); size_t size() const { @@ -67,6 +75,22 @@ void push_back(); T pop_back(); + iterator insert(const_iterator position, const value_type& val); + iterator insert(const_iterator position, size_type n, + const value_type& val); + template + iterator insert(const_iterator position, InputIterator first, + InputIterator last); + iterator insert(const_iterator position, value_type&& val); + iterator insert(const_iterator position, initializer_list il); + + iterator erase(const_iterator position); + iterator erase(const_iterator first, const_iterator last); + + template + iterator emplace(const_iterator position, Args&&... args); + + T &operator[](size_t n) { return _start[n]; } @@ -77,8 +101,10 @@ iterator begin() { return iterator(_start); } const_iterator begin() const { return const_iterator(_start); } + const_iterator cbegin() const { return const_iterator(_start); } iterator end() { return iterator(_finish); } const_iterator end() const { return const_iterator(_finish); } + const_iterator cend() const { return const_iterator(_finish); } }; class exception { Index: test/Analysis/diagnostics/explicit-suppression.cpp =================================================================== --- test/Analysis/diagnostics/explicit-suppression.cpp +++ test/Analysis/diagnostics/explicit-suppression.cpp @@ -18,6 +18,6 @@ void testCopyNull(C *I, C *E) { std::copy(I, E, (C *)0); #ifndef SUPPRESSED - // expected-warning@../Inputs/system-header-simulator-cxx.h:191 {{Called C++ object pointer is null}} + // expected-warning@../Inputs/system-header-simulator-cxx.h:217 {{Called C++ object pointer is null}} #endif } Index: test/Analysis/mismatched-iterator.cpp =================================================================== --- /dev/null +++ test/Analysis/mismatched-iterator.cpp @@ -0,0 +1,121 @@ +// RUN: %clang_cc1 -std=c++11 -analyze -analyzer-checker=core,cplusplus,alpha.cplusplus.MismatchedIterator -analyzer-eagerly-assume %s -verify + +#include "Inputs/system-header-simulator-cxx.h" + +void good_insert1(std::vector &v, int n) { + v.insert(v.cbegin(), n); // no-warning +} + + +void good_insert2(std::vector &v, int len, int n) { + v.insert(v.cbegin(), len, n); // no-warning +} + +void good_insert3(std::vector &v1, std::vector &v2) { + v1.insert(v1.cbegin(), v2.cbegin(), v2.cend()); // no-warning +} + +void good_insert4(std::vector &v, int len, int n) { + v.insert(v.cbegin(), {n-1, n, n+1}); // no-warning +} + +void good_insert_find(std::vector &v, int n, int m) { + auto i = std::find(v.begin(), v.end(), n); + v.insert(i, m); // no-warning +} + +void good_erase1(std::vector &v) { + v.erase(v.cbegin()); // no-warning +} + +void good_erase2(std::vector &v) { + v.erase(v.cbegin(), v.cend()); // no-warning +} + +void good_emplace(std::vector &v, int n) { + v.emplace(v.cbegin(), n); // no-warning +} + +void good_ctor(std::vector &v) { + std::vector new_v(v.begin(), v.end()); // no-warning +} + +void good_find(std::vector &v, int n) { + std::find(v.begin(), v.end(), n); // no-warning +} + +void good_find_first_of(std::vector &v1, std::vector &v2) { + std::find_first_of(v1.begin(), v1.end(), v2.begin(), v2.end()); // no-warning +} + +void good_comparison(std::vector &v) { + if (v.begin() == v.end()) {} // no-warning +} + +void bad_insert1(std::vector &v1, std::vector &v2, int n) { + v2.insert(v1.cbegin(), n); // expected-warning{{Iterator access mismatched}} +} + +void bad_insert2(std::vector &v1, std::vector &v2, int len, int n) { + v2.insert(v1.cbegin(), len, n); // expected-warning{{Iterator access mismatched}} +} + +void bad_insert3(std::vector &v1, std::vector &v2) { + v2.insert(v1.cbegin(), v2.cbegin(), v2.cend()); // expected-warning{{Iterator access mismatched}} + v1.insert(v1.cbegin(), v1.cbegin(), v2.cend()); // expected-warning{{Iterator access mismatched}} + v1.insert(v1.cbegin(), v2.cbegin(), v1.cend()); // expected-warning{{Iterator access mismatched}} +} + +void bad_insert4(std::vector &v1, std::vector &v2, int len, int n) { + v2.insert(v1.cbegin(), {n-1, n, n+1}); // expected-warning{{Iterator access mismatched}} +} + +void bad_erase1(std::vector &v1, std::vector &v2) { + v2.erase(v1.cbegin()); // expected-warning{{Iterator access mismatched}} +} + +void bad_erase2(std::vector &v1, std::vector &v2) { + v2.erase(v2.cbegin(), v1.cend()); // expected-warning{{Iterator access mismatched}} + v2.erase(v1.cbegin(), v2.cend()); // expected-warning{{Iterator access mismatched}} + v2.erase(v1.cbegin(), v1.cend()); // expected-warning{{Iterator access mismatched}} +} + +void bad_emplace(std::vector &v1, std::vector &v2, int n) { + v2.emplace(v1.cbegin(), n); // expected-warning{{Iterator access mismatched}} +} + +void bad_ctor(std::vector &v1, std::vector &v2) { + std::vector new_v(v1.begin(), v2.end()); // expected-warning{{Iterator access mismatched}} +} + +void bad_find(std::vector &v1, std::vector &v2, int n) { + std::find(v1.begin(), v2.end(), n); // expected-warning{{Iterator access mismatched}} +} + +void bad_find_first_of(std::vector &v1, std::vector &v2) { + std::find_first_of(v1.begin(), v2.end(), v2.begin(), v2.end()); // expected-warning{{Iterator access mismatched}} + std::find_first_of(v1.begin(), v1.end(), v2.begin(), v1.end()); // expected-warning{{Iterator access mismatched}} +} + +void bad_comparison(std::vector &v1, std::vector &v2) { + if (v1.begin() != v2.end()) { // expected-warning{{Iterator access mismatched}} + *v1.begin(); + } +} + +void bad_insert_find(std::vector &v1, std::vector &v2, int n, int m) { + auto i = std::find(v1.begin(), v1.end(), n); + v2.insert(i, m); // expected-warning{{Iterator access mismatched}} +} + +void good_overwrite(std::vector &v1, std::vector &v2, int n) { + auto i = v1.cbegin(); + i = v2.cbegin(); + v2.insert(i, n); // no-warning +} + +void bad_overwrite(std::vector &v1, std::vector &v2, int n) { + auto i = v1.cbegin(); + i = v2.cbegin(); + v1.insert(i, n); // expected-warning{{Iterator access mismatched}} +}