baloghadamsoftware updated this revision to Diff 89123.
baloghadamsoftware added a comment.
Updated according to the comments.
https://reviews.llvm.org/D29419
Files:
include/clang/StaticAnalyzer/Checkers/Checkers.td
lib/StaticAnalyzer/Checkers/CMakeLists.txt
lib/StaticAnalyzer/Checkers/MismatchedIteratorChecker.cpp
test/Analysis/Inputs/system-header-simulator-cxx.h
test/Analysis/diagnostics/explicit-suppression.cpp
test/Analysis/mismatched-iterator.cpp
Index: test/Analysis/mismatched-iterator.cpp
===================================================================
--- /dev/null
+++ test/Analysis/mismatched-iterator.cpp
@@ -0,0 +1,134 @@
+// 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<int> &v, int n) {
+ v.insert(v.cbegin(), n); // no-warning
+}
+
+
+void good_insert2(std::vector<int> &v, int len, int n) {
+ v.insert(v.cbegin(), len, n); // no-warning
+}
+
+void good_insert3(std::vector<int> &v1, std::vector<int> &v2) {
+ v1.insert(v1.cbegin(), v2.cbegin(), v2.cend()); // no-warning
+}
+
+void good_insert4(std::vector<int> &v, int len, int n) {
+ v.insert(v.cbegin(), {n-1, n, n+1}); // no-warning
+}
+
+void good_insert_find(std::vector<int> &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<int> &v) {
+ v.erase(v.cbegin()); // no-warning
+}
+
+void good_erase2(std::vector<int> &v) {
+ v.erase(v.cbegin(), v.cend()); // no-warning
+}
+
+void good_emplace(std::vector<int> &v, int n) {
+ v.emplace(v.cbegin(), n); // no-warning
+}
+
+void good_ctor(std::vector<int> &v) {
+ std::vector<int> new_v(v.begin(), v.end()); // no-warning
+}
+
+void good_find(std::vector<int> &v, int n) {
+ std::find(v.begin(), v.end(), n); // no-warning
+}
+
+void good_find_first_of(std::vector<int> &v1, std::vector<int> &v2) {
+ std::find_first_of(v1.begin(), v1.end(), v2.begin(), v2.end()); // no-warning
+}
+
+void good_comparison(std::vector<int> &v) {
+ if (v.begin() == v.end()) {} // no-warning
+}
+
+void bad_insert1(std::vector<int> &v1, std::vector<int> &v2, int n) {
+ v2.insert(v1.cbegin(), n); // expected-warning{{Iterator access mismatched}}
+}
+
+void bad_insert2(std::vector<int> &v1, std::vector<int> &v2, int len, int n) {
+ v2.insert(v1.cbegin(), len, n); // expected-warning{{Iterator access mismatched}}
+}
+
+void bad_insert3(std::vector<int> &v1, std::vector<int> &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<int> &v1, std::vector<int> &v2, int len, int n) {
+ v2.insert(v1.cbegin(), {n-1, n, n+1}); // expected-warning{{Iterator access mismatched}}
+}
+
+void bad_erase1(std::vector<int> &v1, std::vector<int> &v2) {
+ v2.erase(v1.cbegin()); // expected-warning{{Iterator access mismatched}}
+}
+
+void bad_erase2(std::vector<int> &v1, std::vector<int> &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<int> &v1, std::vector<int> &v2, int n) {
+ v2.emplace(v1.cbegin(), n); // expected-warning{{Iterator access mismatched}}
+}
+
+void bad_ctor(std::vector<int> &v1, std::vector<int> &v2) {
+ std::vector<int> new_v(v1.begin(), v2.end()); // expected-warning{{Iterator access mismatched}}
+}
+
+void bad_find(std::vector<int> &v1, std::vector<int> &v2, int n) {
+ std::find(v1.begin(), v2.end(), n); // expected-warning{{Iterator access mismatched}}
+}
+
+void bad_find_first_of(std::vector<int> &v1, std::vector<int> &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<int> &v1, std::vector<int> &v2) {
+ if (v1.begin() != v2.end()) { // expected-warning{{Iterator access mismatched}}
+ *v1.begin();
+ }
+}
+
+void bad_insert_find(std::vector<int> &v1, std::vector<int> &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<int> &v1, std::vector<int> &v2, int n) {
+ auto i = v1.cbegin();
+ i = v2.cbegin();
+ v2.insert(i, n); // no-warning
+}
+
+void bad_overwrite(std::vector<int> &v1, std::vector<int> &v2, int n) {
+ auto i = v1.cbegin();
+ i = v2.cbegin();
+ v1.insert(i, n); // expected-warning{{Iterator access mismatched}}
+}
+
+template<typename Container, typename Iterator>
+bool is_end(Container cont, Iterator it) {
+ return it == cont.end();
+}
+
+void good_empty(std::vector<int> &v) {
+ is_end(v, v.begin()); // no-warning
+}
+
+void bad_empty(std::vector<int> &v1, std::vector<int> &v2) {
+ is_end(v1, v2.begin()); // expected-warning@125{{Iterator access mismatched}}
+}
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/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<T, T *, T &> iterator;
typedef __iterator<T, const T *, const T &> const_iterator;
- __iterator(const Ptr p) : ptr(p) {}
+ __iterator(const Ptr p): ptr(p) {}
+ __iterator(const iterator &rhs): ptr(rhs.base()) {}
__iterator<T, Ptr, Ref> operator++() { return *this; }
__iterator<T, Ptr, Ref> 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,17 +49,23 @@
};
typedef __typeof__(sizeof(int)) size_t;
+
+ template <class T> class initializer_list;
template<typename T>
class vector {
+ typedef T value_type;
+ typedef size_t size_type;
typedef __iterator<T, T *, T &> iterator;
typedef __iterator<T, const T *, const T &> const_iterator;
T *_start;
T *_finish;
T *_end_of_storage;
public:
vector() : _start(0), _finish(0), _end_of_storage(0) {}
+ template <typename InputIterator>
+ 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 <typename InputIterator>
+ iterator insert(const_iterator position, InputIterator first,
+ InputIterator last);
+ iterator insert(const_iterator position, value_type&& val);
+ iterator insert(const_iterator position, initializer_list<value_type> il);
+
+ iterator erase(const_iterator position);
+ iterator erase(const_iterator first, const_iterator last);
+
+ template <class... Args>
+ 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: lib/StaticAnalyzer/Checkers/MismatchedIteratorChecker.cpp
===================================================================
--- /dev/null
+++ lib/StaticAnalyzer/Checkers/MismatchedIteratorChecker.cpp
@@ -0,0 +1,542 @@
+//===-- 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 <utility>
+
+using namespace clang;
+using namespace ento;
+
+namespace {
+class MismatchedIteratorChecker
+ : public Checker<check::PreCall, check::PostCall,
+ check::PreStmt<CXXConstructExpr>,
+ check::PreStmt<CXXOperatorCallExpr>,
+ check::PostStmt<MaterializeTemporaryExpr>, check::Bind,
+ check::BeginFunction, check::DeadSymbols> {
+
+ std::unique_ptr<BugType> 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 CXXOperatorCallExpr *COCE, CheckerContext &C) const;
+ void checkPreStmt(const CXXConstructExpr *CCE, CheckerContext &C) const;
+ void checkBind(SVal Loc, SVal Val, const Stmt *S, CheckerContext &C) const;
+ void checkBeginFunction(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<FunctionDecl>(Call.getDecl());
+ if (!Func)
+ return;
+
+ if (Func->isOverloadedOperator()) {
+ if (!isComparisonOperator(Func->getOverloadedOperator()))
+ return;
+
+ if (const auto *InstCall = dyn_cast<CXXInstanceCall>(&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<CXXInstanceCall>(&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<CXXConstructorCall>(&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<TypeDecl>(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<SubstTemplateTypeParmType>();
+ 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<FunctionDecl>(Call.getDecl());
+ if (!Func)
+ return;
+
+ 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<CXXInstanceCall>(&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::checkPreStmt(const CXXOperatorCallExpr *COCE,
+ CheckerContext &C) const {
+ const auto *ThisExpr = COCE->getArg(0);
+
+ auto State = C.getState();
+ const auto *LCtx = C.getPredecessor()->getLocationContext();
+
+ const auto CurrentThis = State->getSVal(ThisExpr, LCtx);
+ if (const auto *Reg = CurrentThis.getAsRegion()) {
+ if (!Reg->getAs<CXXTempObjectRegion>())
+ return;
+ const auto OldState = C.getPredecessor()->getFirstPred()->getState();
+ const auto OldThis = OldState->getSVal(ThisExpr, LCtx);
+ const auto *Cont = getIteratorContainer(OldState, OldThis);
+ if (!Cont)
+ return;
+ State = setIteratorContainer(State, CurrentThis, *Cont);
+ C.addTransition(State);
+ }
+}
+
+void MismatchedIteratorChecker::checkBind(SVal Loc, SVal Val, const Stmt *S,
+ CheckerContext &C) const {
+ auto State = C.getState();
+ const auto *const *Cont = getIteratorContainer(State, Val);
+ if (Cont) {
+ State = setIteratorContainer(State, Loc, *Cont);
+ C.addTransition(State);
+ } else {
+ const auto *const *OldCont = getIteratorContainer(State, Loc);
+ if (OldCont) {
+ State = removeIteratorContainer(State, Loc);
+ C.addTransition(State);
+ }
+ }
+}
+
+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<StackFrameContext>(LCtx)->getCallSite();
+ if (!Site)
+ return;
+
+ const auto *FD = dyn_cast<FunctionDecl>(LCtx->getDecl());
+ if (!FD)
+ return;
+
+ const auto *CE = dyn_cast<CallExpr>(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 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<IteratorRegionMap>();
+ for (auto I = RegionMap.begin(), E = RegionMap.end(); I != E; ++I) {
+ if (!SR.isLiveRegion(I->first)) {
+ State = State->remove<IteratorRegionMap>(I->first);
+ }
+ }
+
+ auto SymbolMap = State->get<IteratorSymbolMap>();
+ for (auto I = SymbolMap.begin(), E = SymbolMap.end(); I != E; ++I) {
+ if (SR.isDead(I->first)) {
+ State = State->remove<IteratorSymbolMap>(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) {
+ // If I do not put a tag here, the comparison test will fail
+ static CheckerProgramPointTag Tag("MismatchedIteratorChecker",
+ "MismatchedIterator");
+ auto *N = C.generateNonFatalErrorNode(State, &Tag);
+ 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::reportMismatchedBug(
+ const StringRef &Message, const SVal &Val, CheckerContext &C,
+ ExplodedNode *ErrNode) const {
+ auto R = llvm::make_unique<BugReport>(*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<CXXConstructorDecl>(M)) {
+ if (C->isCopyConstructor()) {
+ CopyC = !C->isDeleted() && C->getAccess() == AS_public;
+ }
+ continue;
+ }
+ if (const auto *D = dyn_cast<CXXDestructorDecl>(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<IteratorRegionMap>(Reg);
+ } else if (const auto Sym = Val.getAsSymbol()) {
+ return State->get<IteratorSymbolMap>(Sym);
+ } else if (const auto LCVal = Val.getAs<nonloc::LazyCompoundVal>()) {
+ return State->get<IteratorRegionMap>(LCVal->getRegion());
+ }
+ return nullptr;
+}
+
+static ProgramStateRef setIteratorContainer(ProgramStateRef State,
+ const SVal &Val,
+ const MemRegion *Cont) {
+ if (const auto Reg = Val.getAsRegion()) {
+ return State->set<IteratorRegionMap>(Reg, Cont);
+ } else if (const auto Sym = Val.getAsSymbol()) {
+ return State->set<IteratorSymbolMap>(Sym, Cont);
+ } else if (const auto LCVal = Val.getAs<nonloc::LazyCompoundVal>()) {
+ return State->set<IteratorRegionMap>(LCVal->getRegion(), Cont);
+ }
+ return nullptr;
+}
+
+static ProgramStateRef removeIteratorContainer(ProgramStateRef State,
+ const SVal &Val) {
+ if (const auto Reg = Val.getAsRegion()) {
+ return State->remove<IteratorRegionMap>(Reg);
+ } else if (const auto Sym = Val.getAsSymbol()) {
+ return State->remove<IteratorSymbolMap>(Sym);
+ } else if (const auto LCVal = Val.getAs<nonloc::LazyCompoundVal>()) {
+ return State->remove<IteratorRegionMap>(LCVal->getRegion());
+ }
+ return nullptr;
+}
+}
+
+void ento::registerMismatchedIteratorChecker(CheckerManager &Mgr) {
+ Mgr.registerChecker<MismatchedIteratorChecker>();
+}
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: 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"
_______________________________________________
cfe-commits mailing list
[email protected]
http://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits