Range errors (dereferencing or incrementing the past-the-end iterator or decrementing the iterator of the first element of the range) and access of invalidated iterators lead to undefined behavior. There is no point to continue the analysis after such an error on the same execution path, but terminate it by a sink node (fatal error). This also improves the performance and helps avoiding double reports (e.g. in case of nested iterators). Differential Revision: https://reviews.llvm.org/D62893 llvm-svn: 370314
245 lines
6.1 KiB
C++
245 lines
6.1 KiB
C++
// RUN: %clang_analyze_cc1 -std=c++11 -analyzer-checker=core,cplusplus,alpha.cplusplus.IteratorRange -analyzer-config aggressive-binary-operation-simplification=true -analyzer-config c++-container-inlining=false %s -verify
|
|
// RUN: %clang_analyze_cc1 -std=c++11 -analyzer-checker=core,cplusplus,alpha.cplusplus.IteratorRange -analyzer-config aggressive-binary-operation-simplification=true -analyzer-config c++-container-inlining=true -DINLINE=1 %s -verify
|
|
|
|
#include "Inputs/system-header-simulator-cxx.h"
|
|
|
|
void clang_analyzer_warnIfReached();
|
|
|
|
void simple_good_end(const std::vector<int> &v) {
|
|
auto i = v.end();
|
|
if (i != v.end()) {
|
|
clang_analyzer_warnIfReached();
|
|
*i; // no-warning
|
|
}
|
|
}
|
|
|
|
void simple_good_end_negated(const std::vector<int> &v) {
|
|
auto i = v.end();
|
|
if (!(i == v.end())) {
|
|
clang_analyzer_warnIfReached();
|
|
*i; // no-warning
|
|
}
|
|
}
|
|
|
|
void simple_bad_end(const std::vector<int> &v) {
|
|
auto i = v.end();
|
|
*i; // expected-warning{{Past-the-end iterator dereferenced}}
|
|
clang_analyzer_warnIfReached();
|
|
}
|
|
|
|
void copy(const std::vector<int> &v) {
|
|
auto i1 = v.end();
|
|
auto i2 = i1;
|
|
*i2; // expected-warning{{Past-the-end iterator dereferenced}}
|
|
}
|
|
|
|
void decrease(const std::vector<int> &v) {
|
|
auto i = v.end();
|
|
--i;
|
|
*i; // no-warning
|
|
}
|
|
|
|
void copy_and_decrease1(const std::vector<int> &v) {
|
|
auto i1 = v.end();
|
|
auto i2 = i1;
|
|
--i1;
|
|
*i1; // no-warning
|
|
}
|
|
|
|
void copy_and_decrease2(const std::vector<int> &v) {
|
|
auto i1 = v.end();
|
|
auto i2 = i1;
|
|
--i1;
|
|
*i2; // expected-warning{{Past-the-end iterator dereferenced}}
|
|
}
|
|
|
|
void copy_and_increase1(const std::vector<int> &v) {
|
|
auto i1 = v.begin();
|
|
auto i2 = i1;
|
|
++i1;
|
|
if (i1 == v.end())
|
|
*i2; // no-warning
|
|
}
|
|
|
|
void copy_and_increase2(const std::vector<int> &v) {
|
|
auto i1 = v.begin();
|
|
auto i2 = i1;
|
|
++i1;
|
|
if (i2 == v.end())
|
|
*i2; // expected-warning{{Past-the-end iterator dereferenced}}
|
|
}
|
|
|
|
void copy_and_increase3(const std::vector<int> &v) {
|
|
auto i1 = v.begin();
|
|
auto i2 = i1;
|
|
++i1;
|
|
if (v.end() == i2)
|
|
*i2; // expected-warning{{Past-the-end iterator dereferenced}}
|
|
}
|
|
|
|
template <class InputIterator, class T>
|
|
InputIterator nonStdFind(InputIterator first, InputIterator last,
|
|
const T &val) {
|
|
for (auto i = first; i != last; ++i) {
|
|
if (*i == val) {
|
|
return i;
|
|
}
|
|
}
|
|
return last;
|
|
}
|
|
|
|
void good_non_std_find(std::vector<int> &V, int e) {
|
|
auto first = nonStdFind(V.begin(), V.end(), e);
|
|
if (V.end() != first)
|
|
*first; // no-warning
|
|
}
|
|
|
|
void bad_non_std_find(std::vector<int> &V, int e) {
|
|
auto first = nonStdFind(V.begin(), V.end(), e);
|
|
*first; // expected-warning{{Past-the-end iterator dereferenced}}
|
|
}
|
|
|
|
void tricky(std::vector<int> &V, int e) {
|
|
const auto first = V.begin();
|
|
const auto comp1 = (first != V.end()), comp2 = (first == V.end());
|
|
if (comp1)
|
|
*first; // no-warning
|
|
}
|
|
|
|
void loop(std::vector<int> &V, int e) {
|
|
auto start = V.begin();
|
|
while (true) {
|
|
auto item = std::find(start, V.end(), e);
|
|
if (item == V.end())
|
|
break;
|
|
*item; // no-warning
|
|
start = ++item; // no-warning
|
|
}
|
|
}
|
|
|
|
void good_push_back(std::list<int> &L, int n) {
|
|
auto i0 = --L.cend();
|
|
L.push_back(n);
|
|
*++i0; // no-warning
|
|
}
|
|
|
|
void bad_push_back(std::list<int> &L, int n) {
|
|
auto i0 = --L.cend();
|
|
L.push_back(n);
|
|
++i0;
|
|
*++i0; // expected-warning{{Past-the-end iterator dereferenced}}
|
|
}
|
|
|
|
void good_pop_back(std::list<int> &L, int n) {
|
|
auto i0 = --L.cend(); --i0;
|
|
L.pop_back();
|
|
*i0; // no-warning
|
|
}
|
|
|
|
void bad_pop_back(std::list<int> &L, int n) {
|
|
auto i0 = --L.cend(); --i0;
|
|
L.pop_back();
|
|
*++i0; // expected-warning{{Past-the-end iterator dereferenced}}
|
|
}
|
|
|
|
void good_push_front(std::list<int> &L, int n) {
|
|
auto i0 = L.cbegin();
|
|
L.push_front(n);
|
|
*--i0; // no-warning
|
|
}
|
|
|
|
void bad_push_front(std::list<int> &L, int n) {
|
|
auto i0 = L.cbegin();
|
|
L.push_front(n);
|
|
--i0;
|
|
--i0; // expected-warning{{Iterator decremented ahead of its valid range}}
|
|
}
|
|
|
|
void good_pop_front(std::list<int> &L, int n) {
|
|
auto i0 = ++L.cbegin();
|
|
L.pop_front();
|
|
*i0; // no-warning
|
|
}
|
|
|
|
void bad_pop_front(std::list<int> &L, int n) {
|
|
auto i0 = ++L.cbegin();
|
|
L.pop_front();
|
|
--i0; // expected-warning{{Iterator decremented ahead of its valid range}}
|
|
}
|
|
|
|
void bad_move(std::list<int> &L1, std::list<int> &L2) {
|
|
auto i0 = --L2.cend();
|
|
L1 = std::move(L2);
|
|
*++i0; // expected-warning{{Past-the-end iterator dereferenced}}
|
|
}
|
|
|
|
void bad_move_push_back(std::list<int> &L1, std::list<int> &L2, int n) {
|
|
auto i0 = --L2.cend();
|
|
L2.push_back(n);
|
|
L1 = std::move(L2);
|
|
++i0;
|
|
*++i0; // expected-warning{{Past-the-end iterator dereferenced}}
|
|
}
|
|
|
|
void good_incr_begin(const std::list<int> &L) {
|
|
auto i0 = L.begin();
|
|
++i0; // no-warning
|
|
}
|
|
|
|
void bad_decr_begin(const std::list<int> &L) {
|
|
auto i0 = L.begin();
|
|
--i0; // expected-warning{{Iterator decremented ahead of its valid range}}
|
|
}
|
|
|
|
void good_decr_end(const std::list<int> &L) {
|
|
auto i0 = L.end();
|
|
--i0; // no-warning
|
|
}
|
|
|
|
void bad_incr_end(const std::list<int> &L) {
|
|
auto i0 = L.end();
|
|
++i0; // expected-warning{{Iterator incremented behind the past-the-end iterator}}
|
|
}
|
|
|
|
struct simple_iterator_base {
|
|
simple_iterator_base();
|
|
simple_iterator_base(const simple_iterator_base& rhs);
|
|
simple_iterator_base &operator=(const simple_iterator_base& rhs);
|
|
virtual ~simple_iterator_base();
|
|
bool friend operator==(const simple_iterator_base &lhs,
|
|
const simple_iterator_base &rhs);
|
|
bool friend operator!=(const simple_iterator_base &lhs,
|
|
const simple_iterator_base &rhs);
|
|
private:
|
|
int *ptr;
|
|
};
|
|
|
|
struct simple_derived_iterator: public simple_iterator_base {
|
|
int& operator*();
|
|
int* operator->();
|
|
simple_iterator_base &operator++();
|
|
simple_iterator_base operator++(int);
|
|
simple_iterator_base &operator--();
|
|
simple_iterator_base operator--(int);
|
|
};
|
|
|
|
struct simple_container {
|
|
typedef simple_derived_iterator iterator;
|
|
|
|
iterator begin();
|
|
iterator end();
|
|
};
|
|
|
|
void good_derived(simple_container c) {
|
|
auto i0 = c.end();
|
|
if (i0 != c.end()) {
|
|
clang_analyzer_warnIfReached();
|
|
*i0; // no-warning
|
|
}
|
|
}
|
|
|
|
void iter_diff(std::vector<int> &V) {
|
|
auto i0 = V.begin(), i1 = V.end();
|
|
ptrdiff_t len = i1 - i0; // no-crash
|
|
}
|