mirror of
https://github.com/llvm/llvm-project.git
synced 2025-04-19 03:46:43 +00:00
RFC: [clang-tidy] [analyzer] Move nondeterministic pointer usage check to tidy (#110471)
This change moves the `alpha.nondeterministic.PointerSorting` and `alpha.nondeterministic.PointerIteration` static analyzer checkers to a single `clang-tidy` check. Those checkers were implemented as simple `clang-tidy` check-like code, wrapped in the static analyzer framework. The documentation was updated to describe what the checks can and cannot do, and testing was completed on a broad set of open-source projects. Co-authored-by: Vince Bridgers <vince.a.bridgers@ericsson.com>
This commit is contained in:
parent
14171b0b13
commit
3d6923dbac
@ -49,6 +49,7 @@
|
||||
#include "MultipleStatementMacroCheck.h"
|
||||
#include "NoEscapeCheck.h"
|
||||
#include "NonZeroEnumToBoolConversionCheck.h"
|
||||
#include "NondeterministicPointerIterationOrderCheck.h"
|
||||
#include "NotNullTerminatedResultCheck.h"
|
||||
#include "OptionalValueConversionCheck.h"
|
||||
#include "ParentVirtualCallCheck.h"
|
||||
@ -174,6 +175,8 @@ public:
|
||||
"bugprone-multiple-new-in-one-expression");
|
||||
CheckFactories.registerCheck<MultipleStatementMacroCheck>(
|
||||
"bugprone-multiple-statement-macro");
|
||||
CheckFactories.registerCheck<NondeterministicPointerIterationOrderCheck>(
|
||||
"bugprone-nondeterministic-pointer-iteration-order");
|
||||
CheckFactories.registerCheck<OptionalValueConversionCheck>(
|
||||
"bugprone-optional-value-conversion");
|
||||
CheckFactories.registerCheck<PointerArithmeticOnPolymorphicObjectCheck>(
|
||||
|
@ -45,6 +45,7 @@ add_clang_library(clangTidyBugproneModule STATIC
|
||||
MultipleNewInOneExpressionCheck.cpp
|
||||
MultipleStatementMacroCheck.cpp
|
||||
NoEscapeCheck.cpp
|
||||
NondeterministicPointerIterationOrderCheck.cpp
|
||||
NonZeroEnumToBoolConversionCheck.cpp
|
||||
NotNullTerminatedResultCheck.cpp
|
||||
OptionalValueConversionCheck.cpp
|
||||
|
@ -0,0 +1,79 @@
|
||||
//===----- NondeterministicPointerIterationOrderCheck.cpp - clang-tidy ----===//
|
||||
//
|
||||
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
|
||||
// See https://llvm.org/LICENSE.txt for license information.
|
||||
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#include "NondeterministicPointerIterationOrderCheck.h"
|
||||
#include "clang/AST/ASTContext.h"
|
||||
#include "clang/Lex/Lexer.h"
|
||||
|
||||
using namespace clang::ast_matchers;
|
||||
|
||||
namespace clang::tidy::bugprone {
|
||||
|
||||
void NondeterministicPointerIterationOrderCheck::registerMatchers(
|
||||
MatchFinder *Finder) {
|
||||
|
||||
auto LoopVariable = varDecl(hasType(
|
||||
qualType(hasCanonicalType(anyOf(referenceType(), pointerType())))));
|
||||
|
||||
auto RangeInit = declRefExpr(to(varDecl(
|
||||
hasType(recordDecl(hasAnyName("std::unordered_set", "std::unordered_map",
|
||||
"std::unordered_multiset",
|
||||
"std::unordered_multimap"))
|
||||
.bind("recorddecl")))));
|
||||
|
||||
Finder->addMatcher(cxxForRangeStmt(hasLoopVariable(LoopVariable),
|
||||
hasRangeInit(RangeInit.bind("rangeinit")))
|
||||
.bind("cxxForRangeStmt"),
|
||||
this);
|
||||
|
||||
auto SortFuncM = callee(functionDecl(hasAnyName(
|
||||
"std::is_sorted", "std::nth_element", "std::sort", "std::partial_sort",
|
||||
"std::partition", "std::stable_partition", "std::stable_sort")));
|
||||
|
||||
auto IteratesPointerEltsM = hasArgument(
|
||||
0,
|
||||
cxxMemberCallExpr(on(hasType(cxxRecordDecl(has(fieldDecl(hasType(qualType(
|
||||
hasCanonicalType(pointsTo(hasCanonicalType(pointerType()))))))))))));
|
||||
|
||||
Finder->addMatcher(
|
||||
callExpr(allOf(SortFuncM, IteratesPointerEltsM)).bind("sortsemantic"),
|
||||
this);
|
||||
}
|
||||
|
||||
void NondeterministicPointerIterationOrderCheck::check(
|
||||
const MatchFinder::MatchResult &Result) {
|
||||
const auto *ForRangePointers =
|
||||
Result.Nodes.getNodeAs<CXXForRangeStmt>("cxxForRangeStmt");
|
||||
|
||||
if ((ForRangePointers) && !(ForRangePointers->getBeginLoc().isMacroID())) {
|
||||
const auto *RangeInit = Result.Nodes.getNodeAs<Stmt>("rangeinit");
|
||||
if (const auto *ClassTemplate =
|
||||
Result.Nodes.getNodeAs<ClassTemplateSpecializationDecl>(
|
||||
"recorddecl")) {
|
||||
const TemplateArgumentList &TemplateArgs =
|
||||
ClassTemplate->getTemplateArgs();
|
||||
const llvm::StringRef AlgoName = ClassTemplate->getName();
|
||||
const bool IsAlgoArgPointer =
|
||||
TemplateArgs[0].getAsType()->isPointerType();
|
||||
|
||||
if (IsAlgoArgPointer) {
|
||||
SourceRange R = RangeInit->getSourceRange();
|
||||
diag(R.getBegin(), "iteration of pointers is nondeterministic") << R;
|
||||
}
|
||||
}
|
||||
return;
|
||||
}
|
||||
const auto *SortPointers = Result.Nodes.getNodeAs<Stmt>("sortsemantic");
|
||||
|
||||
if ((SortPointers) && !(SortPointers->getBeginLoc().isMacroID())) {
|
||||
SourceRange R = SortPointers->getSourceRange();
|
||||
diag(R.getBegin(), "sorting pointers is nondeterministic") << R;
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace clang::tidy::bugprone
|
@ -0,0 +1,39 @@
|
||||
//=== NondeterministicPointerIterationOrderCheck.h - clang-tidy -*- C++ -*-===//
|
||||
//
|
||||
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
|
||||
// See https://llvm.org/LICENSE.txt for license information.
|
||||
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#ifndef LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_BUGPRONE_NONDETERMINISTIC_POINTER_ITERATION_ORDER_CHECK_H
|
||||
#define LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_BUGPRONE_NONDETERMINISTIC_POINTER_ITERATION_ORDER_CHECK_H
|
||||
|
||||
#include "../ClangTidyCheck.h"
|
||||
|
||||
namespace clang::tidy::bugprone {
|
||||
|
||||
/// Finds nondeterministic usages of pointers in unordered containers. The
|
||||
/// check also finds calls to sorting-like algorithms on a container of
|
||||
/// pointers.
|
||||
///
|
||||
/// For the user-facing documentation see:
|
||||
/// http://clang.llvm.org/extra/clang-tidy/checks/bugprone/nondeterministic-pointer-iteration-order.html
|
||||
class NondeterministicPointerIterationOrderCheck : public ClangTidyCheck {
|
||||
public:
|
||||
NondeterministicPointerIterationOrderCheck(StringRef Name,
|
||||
ClangTidyContext *Context)
|
||||
: ClangTidyCheck(Name, Context) {}
|
||||
bool isLanguageVersionSupported(const LangOptions &LangOpts) const override {
|
||||
return LangOpts.CPlusPlus;
|
||||
}
|
||||
void registerMatchers(ast_matchers::MatchFinder *Finder) override;
|
||||
void check(const ast_matchers::MatchFinder::MatchResult &Result) override;
|
||||
std::optional<TraversalKind> getCheckTraversalKind() const override {
|
||||
return TK_IgnoreUnlessSpelledInSource;
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace clang::tidy::bugprone
|
||||
|
||||
#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_BUGPRONE_NONDETERMINISTIC_POINTER_ITERATION_ORDER_CHECK_H
|
@ -119,6 +119,12 @@ New checks
|
||||
Warns about code that tries to cast between pointers by means of
|
||||
``std::bit_cast`` or ``memcpy``.
|
||||
|
||||
- New :doc:`bugprone-nondeterministic-pointer-iteration-order
|
||||
<clang-tidy/checks/bugprone/nondeterministic-pointer-iteration-order>`
|
||||
check.
|
||||
|
||||
Finds nondeterministic usages of pointers in unordered containers.
|
||||
|
||||
- New :doc:`bugprone-tagged-union-member-count
|
||||
<clang-tidy/checks/bugprone/tagged-union-member-count>` check.
|
||||
|
||||
|
@ -0,0 +1,44 @@
|
||||
.. title:: clang-tidy - bugprone-nondeterministic-pointer-iteration-order
|
||||
|
||||
bugprone-nondeterministic-pointer-iteration-order
|
||||
=================================================
|
||||
|
||||
Finds nondeterministic usages of pointers in unordered containers.
|
||||
|
||||
One canonical example is iteration across a container of pointers.
|
||||
|
||||
.. code-block:: c++
|
||||
|
||||
{
|
||||
int a = 1, b = 2;
|
||||
std::unordered_set<int *> UnorderedPtrSet = {&a, &b};
|
||||
for (auto i : UnorderedPtrSet)
|
||||
f(i);
|
||||
}
|
||||
|
||||
Another such example is sorting a container of pointers.
|
||||
|
||||
.. code-block:: c++
|
||||
|
||||
{
|
||||
int a = 1, b = 2;
|
||||
std::vector<int *> VectorOfPtr = {&a, &b};
|
||||
std::sort(VectorOfPtr.begin(), VectorOfPtr.end());
|
||||
}
|
||||
|
||||
Iteration of a containers of pointers may present the order of different
|
||||
pointers differently across different runs of a program. In some cases this
|
||||
may be acceptable behavior, in others this may be unexpected behavior. This
|
||||
check is advisory for this reason.
|
||||
|
||||
This check only detects range-based for loops over unordered sets and maps. It
|
||||
also detects calls sorting-like algorithms on containers holding pointers.
|
||||
Other similar usages will not be found and are false negatives.
|
||||
|
||||
Limitations:
|
||||
|
||||
* This check currently does not check if a nondeterministic iteration order is
|
||||
likely to be a mistake, and instead marks all such iterations as bugprone.
|
||||
* std::reference_wrapper is not considered yet.
|
||||
* Only for loops are considered, other iterators can be included in
|
||||
improvements.
|
@ -115,6 +115,7 @@ Clang-Tidy Checks
|
||||
:doc:`bugprone-multiple-new-in-one-expression <bugprone/multiple-new-in-one-expression>`,
|
||||
:doc:`bugprone-multiple-statement-macro <bugprone/multiple-statement-macro>`,
|
||||
:doc:`bugprone-no-escape <bugprone/no-escape>`,
|
||||
:doc:`bugprone-nondeterministic-pointer-iteration-order <bugprone/nondeterministic-pointer-iteration-order>`,
|
||||
:doc:`bugprone-non-zero-enum-to-bool-conversion <bugprone/non-zero-enum-to-bool-conversion>`,
|
||||
:doc:`bugprone-not-null-terminated-result <bugprone/not-null-terminated-result>`, "Yes"
|
||||
:doc:`bugprone-optional-value-conversion <bugprone/optional-value-conversion>`, "Yes"
|
||||
|
@ -0,0 +1,31 @@
|
||||
#ifndef _SIM_ALGORITHM
|
||||
#define _SIM_ALGORITHM
|
||||
|
||||
#pragma clang system_header
|
||||
|
||||
namespace std {
|
||||
|
||||
template<class ForwardIt>
|
||||
bool is_sorted(ForwardIt first, ForwardIt last);
|
||||
|
||||
template <class RandomIt>
|
||||
void nth_element(RandomIt first, RandomIt nth, RandomIt last);
|
||||
|
||||
template<class RandomIt>
|
||||
void partial_sort(RandomIt first, RandomIt middle, RandomIt last);
|
||||
|
||||
template<class RandomIt>
|
||||
void sort (RandomIt first, RandomIt last);
|
||||
|
||||
template<class RandomIt>
|
||||
void stable_sort(RandomIt first, RandomIt last);
|
||||
|
||||
template<class BidirIt, class UnaryPredicate>
|
||||
BidirIt partition(BidirIt first, BidirIt last, UnaryPredicate p);
|
||||
|
||||
template<class BidirIt, class UnaryPredicate>
|
||||
BidirIt stable_partition(BidirIt first, BidirIt last, UnaryPredicate p);
|
||||
|
||||
} // namespace std
|
||||
|
||||
#endif // _SIM_ALGORITHM
|
@ -0,0 +1,11 @@
|
||||
#ifndef _SIM_CPP_CONFIG_H
|
||||
#define _SIM_CPP_CONFIG_H
|
||||
|
||||
#pragma clang system_header
|
||||
|
||||
typedef unsigned char uint8_t;
|
||||
|
||||
typedef __typeof__(sizeof(int)) size_t;
|
||||
typedef __typeof__((char*)0-(char*)0) ptrdiff_t;
|
||||
|
||||
#endif // _SIM_CPP_CONFIG_H
|
@ -0,0 +1,39 @@
|
||||
#ifndef _INITIALIZER_LIST
|
||||
#define _INITIALIZER_LIST
|
||||
|
||||
#pragma clang system_header
|
||||
#
|
||||
#include "sim_c++config.h" // size_t
|
||||
|
||||
namespace std {
|
||||
|
||||
template <class _E>
|
||||
class initializer_list {
|
||||
const _E* __begin_;
|
||||
size_t __size_;
|
||||
|
||||
initializer_list(const _E* __b, size_t __s)
|
||||
: __begin_(__b),
|
||||
__size_(__s)
|
||||
{}
|
||||
|
||||
public:
|
||||
typedef _E value_type;
|
||||
typedef const _E& reference;
|
||||
typedef const _E& const_reference;
|
||||
typedef size_t size_type;
|
||||
|
||||
typedef const _E* iterator;
|
||||
typedef const _E* const_iterator;
|
||||
|
||||
initializer_list() : __begin_(0), __size_(0) {}
|
||||
|
||||
size_t size() const {return __size_;}
|
||||
const _E* begin() const {return __begin_;}
|
||||
const _E* end() const {return __begin_ + __size_;}
|
||||
|
||||
}; // class initializer_list
|
||||
|
||||
} // namespace std
|
||||
|
||||
#endif // _INITIALIZER_LIST
|
@ -0,0 +1,22 @@
|
||||
#ifndef _SIM_ITERATOR_BASE
|
||||
#define _SIM_ITERATOR_BASE
|
||||
|
||||
namespace std {
|
||||
|
||||
struct input_iterator_tag { };
|
||||
struct output_iterator_tag { };
|
||||
struct forward_iterator_tag : public input_iterator_tag { };
|
||||
struct bidirectional_iterator_tag : public forward_iterator_tag { };
|
||||
struct random_access_iterator_tag : public bidirectional_iterator_tag { };
|
||||
|
||||
template <typename Iterator> struct iterator_traits {
|
||||
typedef typename Iterator::difference_type difference_type;
|
||||
typedef typename Iterator::value_type value_type;
|
||||
typedef typename Iterator::pointer pointer;
|
||||
typedef typename Iterator::reference reference;
|
||||
typedef typename Iterator::iterator_category iterator_category;
|
||||
};
|
||||
|
||||
} // namespace std
|
||||
|
||||
#endif // _SIM_ITERATOR_BASE
|
@ -0,0 +1,34 @@
|
||||
|
||||
#ifndef _SIM_MAP
|
||||
#define _SIM_MAP
|
||||
|
||||
#pragma clang system_header
|
||||
#include "sim_stl_pair"
|
||||
|
||||
namespace std {
|
||||
|
||||
template <typename Key, typename Value>
|
||||
class map {
|
||||
public:
|
||||
using value_type = pair<Key, Value>;
|
||||
map();
|
||||
map(initializer_list<pair<Key, Value>> initList);
|
||||
value_type& operator[](const Key& key);
|
||||
value_type& operator[](Key&& key);
|
||||
class iterator {
|
||||
public:
|
||||
iterator(Key *key): ptr(key) {}
|
||||
iterator& operator++() { ++ptr; return *this; }
|
||||
bool operator!=(const iterator &other) const { return ptr != other.ptr; }
|
||||
const Key &operator*() const { return *ptr; }
|
||||
private:
|
||||
Key *ptr;
|
||||
};
|
||||
Key *val;
|
||||
iterator begin() const { return iterator(val); }
|
||||
iterator end() const { return iterator(val + 1); }
|
||||
};
|
||||
|
||||
} // namespace std
|
||||
|
||||
#endif // _SIM_MAP
|
@ -0,0 +1,44 @@
|
||||
|
||||
#ifndef _SIM_SET
|
||||
#define _SIM_SET
|
||||
|
||||
#pragma clang system_header
|
||||
#include "sim_initializer_list"
|
||||
|
||||
namespace std {
|
||||
|
||||
template< class T = void >
|
||||
struct less;
|
||||
|
||||
template< class T >
|
||||
struct allocator;
|
||||
|
||||
template< class Key >
|
||||
struct hash;
|
||||
|
||||
template<
|
||||
class Key,
|
||||
class Compare = std::less<Key>,
|
||||
class Alloc = std::allocator<Key>
|
||||
> class set {
|
||||
public:
|
||||
set(initializer_list<Key> __list) {}
|
||||
|
||||
class iterator {
|
||||
public:
|
||||
iterator(Key *key): ptr(key) {}
|
||||
iterator& operator++() { ++ptr; return *this; }
|
||||
bool operator!=(const iterator &other) const { return ptr != other.ptr; }
|
||||
const Key &operator*() const { return *ptr; }
|
||||
private:
|
||||
Key *ptr;
|
||||
};
|
||||
|
||||
Key *val;
|
||||
iterator begin() const { return iterator(val); }
|
||||
iterator end() const { return iterator(val + 1); }
|
||||
};
|
||||
|
||||
} // namespace std
|
||||
|
||||
#endif // _SIM_SET
|
@ -0,0 +1,32 @@
|
||||
#ifndef _SIM_STL_PAIR
|
||||
#define _SIM_STL_PAIR
|
||||
|
||||
#pragma clang system_header
|
||||
|
||||
#include "sim_type_traits"
|
||||
|
||||
namespace std {
|
||||
|
||||
template <class T1, class T2>
|
||||
struct pair {
|
||||
T1 first;
|
||||
T2 second;
|
||||
|
||||
pair() : first(), second() {}
|
||||
pair(const T1 &a, const T2 &b) : first(a), second(b) {}
|
||||
|
||||
template<class U1, class U2>
|
||||
pair(const pair<U1, U2> &other) : first(other.first),
|
||||
second(other.second) {}
|
||||
};
|
||||
|
||||
template <typename T1, typename T2>
|
||||
pair<typename remove_reference<T1>::type, typename remove_reference<T2>::type>
|
||||
make_pair(T1 &&, T2 &&) {
|
||||
return {};
|
||||
};
|
||||
|
||||
} // namespace std
|
||||
|
||||
#endif // _SIM_STL_PAIR
|
||||
|
@ -0,0 +1,19 @@
|
||||
|
||||
#ifndef _SIM_TYPE_TRAITS
|
||||
#define _SIM_TYPE_TRAITS
|
||||
|
||||
#pragma clang system_header
|
||||
namespace std {
|
||||
|
||||
template< class T > struct remove_reference {typedef T type;};
|
||||
template< class T > struct remove_reference<T&> {typedef T type;};
|
||||
template< class T > struct remove_reference<T&&> {typedef T type;};
|
||||
|
||||
template<typename T> typename remove_reference<T>::type&& move(T&& a);
|
||||
|
||||
template< class T >
|
||||
using remove_reference_t = typename remove_reference<T>::type;
|
||||
|
||||
} // namespace std
|
||||
|
||||
#endif // _SIM_TYPE_TRAITS
|
@ -0,0 +1,33 @@
|
||||
#ifndef _SIM_UNORDERED_MAP
|
||||
#define _SIM_UNORDERED_MAP
|
||||
|
||||
#pragma clang system_header
|
||||
#include "sim_initializer_list"
|
||||
|
||||
namespace std {
|
||||
|
||||
template <typename Key, typename Value>
|
||||
class unordered_map {
|
||||
public:
|
||||
using value_type = pair<Key, Value>;
|
||||
unordered_map();
|
||||
unordered_map(initializer_list<pair<Key, Value>> initList);
|
||||
value_type& operator[](const Key& key);
|
||||
value_type& operator[](Key&& key);
|
||||
class iterator {
|
||||
public:
|
||||
iterator(Key *key): ptr(key) {}
|
||||
iterator& operator++() { ++ptr; return *this; }
|
||||
bool operator!=(const iterator &other) const { return ptr != other.ptr; }
|
||||
const Key &operator*() const { return *ptr; }
|
||||
private:
|
||||
Key *ptr;
|
||||
};
|
||||
Key *val;
|
||||
iterator begin() const { return iterator(val); }
|
||||
iterator end() const { return iterator(val + 1); }
|
||||
};
|
||||
|
||||
} // namespace std
|
||||
|
||||
#endif // _SIM_UNORDERED_MAP
|
@ -0,0 +1,35 @@
|
||||
#ifndef _SIM_UNORDERED_SET
|
||||
#define _SIM_UNORDERED_SET
|
||||
|
||||
#pragma clang system_header
|
||||
#include "sim_initializer_list"
|
||||
|
||||
namespace std {
|
||||
|
||||
template<
|
||||
class Key,
|
||||
class Hash = std::hash<Key>,
|
||||
class Compare = std::less<Key>,
|
||||
class Alloc = std::allocator<Key>
|
||||
> class unordered_set {
|
||||
public:
|
||||
unordered_set(initializer_list<Key> __list) {}
|
||||
|
||||
class iterator {
|
||||
public:
|
||||
iterator(Key *key): ptr(key) {}
|
||||
iterator& operator++() { ++ptr; return *this; }
|
||||
bool operator!=(const iterator &other) const { return ptr != other.ptr; }
|
||||
const Key &operator*() const { return *ptr; }
|
||||
private:
|
||||
Key *ptr;
|
||||
};
|
||||
|
||||
Key *val;
|
||||
iterator begin() const { return iterator(val); }
|
||||
iterator end() const { return iterator(val + 1); }
|
||||
};
|
||||
|
||||
} // namespace std
|
||||
|
||||
#endif // _SIM_UNORDERED_SET
|
@ -0,0 +1,150 @@
|
||||
#ifndef _SIM_VECTOR
|
||||
#define _SIM_VECTOR
|
||||
|
||||
#pragma clang system_header
|
||||
|
||||
#include "sim_iterator_base"
|
||||
|
||||
namespace std {
|
||||
|
||||
template <typename T, typename Ptr, typename Ref> struct __vector_iterator {
|
||||
typedef __vector_iterator<T, T *, T &> iterator;
|
||||
typedef __vector_iterator<T, const T *, const T &> const_iterator;
|
||||
|
||||
typedef ptrdiff_t difference_type;
|
||||
typedef T value_type;
|
||||
typedef Ptr pointer;
|
||||
typedef Ref reference;
|
||||
typedef std::random_access_iterator_tag iterator_category;
|
||||
|
||||
__vector_iterator(const Ptr p = 0) : ptr(p) {}
|
||||
__vector_iterator(const iterator &rhs): ptr(rhs.base()) {}
|
||||
__vector_iterator<T, Ptr, Ref>& operator++() { ++ ptr; return *this; }
|
||||
__vector_iterator<T, Ptr, Ref> operator++(int) {
|
||||
auto tmp = *this;
|
||||
++ ptr;
|
||||
return tmp;
|
||||
}
|
||||
__vector_iterator<T, Ptr, Ref> operator--() { -- ptr; return *this; }
|
||||
__vector_iterator<T, Ptr, Ref> operator--(int) {
|
||||
auto tmp = *this; -- ptr;
|
||||
return tmp;
|
||||
}
|
||||
__vector_iterator<T, Ptr, Ref> operator+(difference_type n) {
|
||||
return ptr + n;
|
||||
}
|
||||
friend __vector_iterator<T, Ptr, Ref> operator+(
|
||||
difference_type n,
|
||||
const __vector_iterator<T, Ptr, Ref> &iter) {
|
||||
return n + iter.ptr;
|
||||
}
|
||||
__vector_iterator<T, Ptr, Ref> operator-(difference_type n) {
|
||||
return ptr - n;
|
||||
}
|
||||
__vector_iterator<T, Ptr, Ref> operator+=(difference_type n) {
|
||||
return ptr += n;
|
||||
}
|
||||
__vector_iterator<T, Ptr, Ref> operator-=(difference_type n) {
|
||||
return ptr -= n;
|
||||
}
|
||||
|
||||
template<typename U, typename Ptr2, typename Ref2>
|
||||
difference_type operator-(const __vector_iterator<U, Ptr2, Ref2> &rhs);
|
||||
|
||||
Ref operator*() const { return *ptr; }
|
||||
Ptr operator->() const { return ptr; }
|
||||
|
||||
Ref operator[](difference_type n) {
|
||||
return *(ptr+n);
|
||||
}
|
||||
|
||||
bool operator==(const iterator &rhs) const { return ptr == rhs.ptr; }
|
||||
bool operator==(const const_iterator &rhs) const { return ptr == rhs.ptr; }
|
||||
|
||||
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;
|
||||
};
|
||||
|
||||
template<typename T>
|
||||
class vector {
|
||||
T *_start;
|
||||
T *_finish;
|
||||
T *_end_of_storage;
|
||||
|
||||
public:
|
||||
typedef T value_type;
|
||||
typedef size_t size_type;
|
||||
typedef __vector_iterator<T, T *, T &> iterator;
|
||||
typedef __vector_iterator<T, const T *, const T &> const_iterator;
|
||||
|
||||
vector() : _start(0), _finish(0), _end_of_storage(0) {}
|
||||
template <typename InputIterator>
|
||||
vector(InputIterator first, InputIterator last);
|
||||
vector(const vector &other);
|
||||
vector(vector &&other);
|
||||
~vector();
|
||||
|
||||
size_t size() const {
|
||||
return size_t(_finish - _start);
|
||||
}
|
||||
|
||||
vector& operator=(const vector &other);
|
||||
vector& operator=(vector &&other);
|
||||
vector& operator=(std::initializer_list<T> ilist);
|
||||
|
||||
void assign(size_type count, const T &value);
|
||||
template <typename InputIterator >
|
||||
void assign(InputIterator first, InputIterator last);
|
||||
void assign(std::initializer_list<T> ilist);
|
||||
|
||||
void clear();
|
||||
|
||||
void push_back(const T &value);
|
||||
void push_back(T &&value);
|
||||
template<class... Args>
|
||||
void emplace_back(Args&&... args);
|
||||
void 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);
|
||||
|
||||
template <class... Args>
|
||||
iterator emplace(const_iterator position, Args&&... args);
|
||||
|
||||
iterator erase(const_iterator position);
|
||||
iterator erase(const_iterator first, const_iterator last);
|
||||
|
||||
T &operator[](size_t n) {
|
||||
return _start[n];
|
||||
}
|
||||
|
||||
const T &operator[](size_t n) const {
|
||||
return _start[n];
|
||||
}
|
||||
|
||||
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); }
|
||||
T& front() { return *begin(); }
|
||||
const T& front() const { return *begin(); }
|
||||
T& back() { return *(end() - 1); }
|
||||
const T& back() const { return *(end() - 1); }
|
||||
};
|
||||
|
||||
} // namespace std
|
||||
|
||||
#endif // _SIM_VECTOR
|
@ -0,0 +1,84 @@
|
||||
// RUN: %check_clang_tidy %s bugprone-nondeterministic-pointer-iteration-order %t -- -- -I%S -std=c++!4
|
||||
|
||||
#include "Inputs/system-header-simulator/sim_set"
|
||||
#include "Inputs/system-header-simulator/sim_unordered_set"
|
||||
#include "Inputs/system-header-simulator/sim_map"
|
||||
#include "Inputs/system-header-simulator/sim_unordered_map"
|
||||
#include "Inputs/system-header-simulator/sim_vector"
|
||||
#include "Inputs/system-header-simulator/sim_algorithm"
|
||||
|
||||
template<class T>
|
||||
void f(T x);
|
||||
|
||||
void PointerIteration() {
|
||||
int a = 1, b = 2;
|
||||
std::set<int> OrderedIntSet = {a, b};
|
||||
std::set<int *> OrderedPtrSet = {&a, &b};
|
||||
std::unordered_set<int> UnorderedIntSet = {a, b};
|
||||
std::unordered_set<int *> UnorderedPtrSet = {&a, &b};
|
||||
std::map<int, int> IntMap = { std::make_pair(a,a), std::make_pair(b,b) };
|
||||
std::map<int*, int*> PtrMap = { std::make_pair(&a,&a), std::make_pair(&b,&b) };
|
||||
std::unordered_map<int, int> IntUnorderedMap = { std::make_pair(a,a), std::make_pair(b,b) };
|
||||
std::unordered_map<int*, int*> PtrUnorderedMap = { std::make_pair(&a,&a), std::make_pair(&b,&b) };
|
||||
|
||||
for (auto i : OrderedIntSet) // no-warning
|
||||
f(i);
|
||||
|
||||
for (auto i : OrderedPtrSet) // no-warning
|
||||
f(i);
|
||||
|
||||
for (auto i : UnorderedIntSet) // no-warning
|
||||
f(i);
|
||||
|
||||
for (auto i : UnorderedPtrSet)
|
||||
f(i);
|
||||
// CHECK-MESSAGES: :[[@LINE-2]]:17: warning: iteration of pointers is nondeterministic
|
||||
|
||||
for (auto &i : UnorderedPtrSet)
|
||||
f(i);
|
||||
// CHECK-MESSAGES: :[[@LINE-2]]:18: warning: iteration of pointers is nondeterministic
|
||||
|
||||
for (auto &i : IntMap) // no-warning
|
||||
f(i);
|
||||
|
||||
for (auto &i : PtrMap) // no-warning
|
||||
f(i);
|
||||
|
||||
for (auto &i : IntUnorderedMap) // no-warning
|
||||
f(i);
|
||||
|
||||
for (auto &i : PtrUnorderedMap)
|
||||
f(i);
|
||||
// CHECK-MESSAGES: :[[@LINE-2]]:18: warning: iteration of pointers is nondeterministic
|
||||
}
|
||||
|
||||
bool g (int *x) { return true; }
|
||||
bool h (int x) { return true; }
|
||||
|
||||
void PointerSorting() {
|
||||
int a = 1, b = 2, c = 3;
|
||||
std::vector<int> V1 = {a, b};
|
||||
std::vector<int *> V2 = {&a, &b};
|
||||
|
||||
std::is_sorted(V1.begin(), V1.end()); // no-warning
|
||||
std::nth_element(V1.begin(), V1.begin() + 1, V1.end()); // no-warning
|
||||
std::partial_sort(V1.begin(), V1.begin() + 1, V1.end()); // no-warning
|
||||
std::sort(V1.begin(), V1.end()); // no-warning
|
||||
std::stable_sort(V1.begin(), V1.end()); // no-warning
|
||||
std::partition(V1.begin(), V1.end(), h); // no-warning
|
||||
std::stable_partition(V1.begin(), V1.end(), h); // no-warning
|
||||
std::is_sorted(V2.begin(), V2.end());
|
||||
// CHECK-MESSAGES: :[[@LINE-1]]:3: warning: sorting pointers is nondeterministic
|
||||
std::nth_element(V2.begin(), V2.begin() + 1, V2.end());
|
||||
// CHECK-MESSAGES: :[[@LINE-1]]:3: warning: sorting pointers is nondeterministic
|
||||
std::partial_sort(V2.begin(), V2.begin() + 1, V2.end());
|
||||
// CHECK-MESSAGES: :[[@LINE-1]]:3: warning: sorting pointers is nondeterministic
|
||||
std::sort(V2.begin(), V2.end());
|
||||
// CHECK-MESSAGES: :[[@LINE-1]]:3: warning: sorting pointers is nondeterministic
|
||||
std::stable_sort(V2.begin(), V2.end());
|
||||
// CHECK-MESSAGES: :[[@LINE-1]]:3: warning: sorting pointers is nondeterministic
|
||||
std::partition(V2.begin(), V2.end(), g);
|
||||
// CHECK-MESSAGES: :[[@LINE-1]]:3: warning: sorting pointers is nondeterministic
|
||||
std::stable_partition(V2.begin(), V2.end(), g);
|
||||
// CHECK-MESSAGES: :[[@LINE-1]]:3: warning: sorting pointers is nondeterministic
|
||||
}
|
@ -786,6 +786,12 @@ Moved checkers
|
||||
To detect too large arguments passed to malloc, consider using the checker
|
||||
``alpha.taint.TaintedAlloc``.
|
||||
|
||||
- The checkers ``alpha.nondeterministic.PointerSorting`` and
|
||||
``alpha.nondeterministic.PointerIteration`` were moved to a new bugprone
|
||||
checker named ``bugprone-nondeterministic-pointer-iteration-order``. The
|
||||
original checkers were implemented only using AST matching and make more
|
||||
sense as a single clang-tidy check.
|
||||
|
||||
.. _release-notes-sanitizers:
|
||||
|
||||
Sanitizers
|
||||
|
@ -3447,37 +3447,6 @@ Limitations:
|
||||
|
||||
More details at the corresponding `GitHub issue <https://github.com/llvm/llvm-project/issues/43459>`_.
|
||||
|
||||
.. _alpha-nondeterminism-PointerIteration:
|
||||
|
||||
alpha.nondeterminism.PointerIteration (C++)
|
||||
"""""""""""""""""""""""""""""""""""""""""""
|
||||
Check for non-determinism caused by iterating unordered containers of pointers.
|
||||
|
||||
.. code-block:: c
|
||||
|
||||
void test() {
|
||||
int a = 1, b = 2;
|
||||
std::unordered_set<int *> UnorderedPtrSet = {&a, &b};
|
||||
|
||||
for (auto i : UnorderedPtrSet) // warn
|
||||
f(i);
|
||||
}
|
||||
|
||||
.. _alpha-nondeterminism-PointerSorting:
|
||||
|
||||
alpha.nondeterminism.PointerSorting (C++)
|
||||
"""""""""""""""""""""""""""""""""""""""""
|
||||
Check for non-determinism caused by sorting of pointers.
|
||||
|
||||
.. code-block:: c
|
||||
|
||||
void test() {
|
||||
int a = 1, b = 2;
|
||||
std::vector<int *> V = {&a, &b};
|
||||
std::sort(V.begin(), V.end()); // warn
|
||||
}
|
||||
|
||||
|
||||
alpha.WebKit
|
||||
^^^^^^^^^^^^
|
||||
|
||||
|
@ -118,8 +118,6 @@ def Debug : Package<"debug">, Hidden;
|
||||
|
||||
def CloneDetectionAlpha : Package<"clone">, ParentPackage<Alpha>;
|
||||
|
||||
def NonDeterminismAlpha : Package<"nondeterminism">, ParentPackage<Alpha>;
|
||||
|
||||
def Fuchsia : Package<"fuchsia">;
|
||||
def FuchsiaAlpha : Package<"fuchsia">, ParentPackage<Alpha>;
|
||||
|
||||
@ -1711,22 +1709,6 @@ def TaintedDivChecker: Checker<"TaintedDiv">,
|
||||
|
||||
} // end "optin.taint"
|
||||
|
||||
//===----------------------------------------------------------------------===//
|
||||
// NonDeterminism checkers.
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
let ParentPackage = NonDeterminismAlpha in {
|
||||
|
||||
def PointerIterationChecker : Checker<"PointerIteration">,
|
||||
HelpText<"Checks for non-determinism caused by iteration of unordered containers of pointers">,
|
||||
Documentation<HasDocumentation>;
|
||||
|
||||
def PointerSortingChecker : Checker<"PointerSorting">,
|
||||
HelpText<"Check for non-determinism caused by sorting of pointers">,
|
||||
Documentation<HasDocumentation>;
|
||||
|
||||
} // end alpha.nondeterminism
|
||||
|
||||
//===----------------------------------------------------------------------===//
|
||||
// Fuchsia checkers.
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
@ -91,8 +91,6 @@ add_clang_library(clangStaticAnalyzerCheckers
|
||||
OSObjectCStyleCast.cpp
|
||||
PaddingChecker.cpp
|
||||
PointerArithChecker.cpp
|
||||
PointerIterationChecker.cpp
|
||||
PointerSortingChecker.cpp
|
||||
PointerSubChecker.cpp
|
||||
PthreadLockChecker.cpp
|
||||
PutenvStackArrayChecker.cpp
|
||||
|
@ -1,101 +0,0 @@
|
||||
//== PointerIterationChecker.cpp ------------------------------- -*- C++ -*--=//
|
||||
//
|
||||
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
|
||||
// See https://llvm.org/LICENSE.txt for license information.
|
||||
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
//
|
||||
// This file defines PointerIterationChecker which checks for non-determinism
|
||||
// caused due to iteration of unordered containers of pointer elements.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#include "clang/ASTMatchers/ASTMatchFinder.h"
|
||||
#include "clang/StaticAnalyzer/Checkers/BuiltinCheckerRegistration.h"
|
||||
#include "clang/StaticAnalyzer/Core/Checker.h"
|
||||
#include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h"
|
||||
|
||||
using namespace clang;
|
||||
using namespace ento;
|
||||
using namespace ast_matchers;
|
||||
|
||||
namespace {
|
||||
|
||||
// ID of a node at which the diagnostic would be emitted.
|
||||
constexpr llvm::StringLiteral WarnAtNode = "iter";
|
||||
|
||||
class PointerIterationChecker : public Checker<check::ASTCodeBody> {
|
||||
public:
|
||||
void checkASTCodeBody(const Decl *D,
|
||||
AnalysisManager &AM,
|
||||
BugReporter &BR) const;
|
||||
};
|
||||
|
||||
static void emitDiagnostics(const BoundNodes &Match, const Decl *D,
|
||||
BugReporter &BR, AnalysisManager &AM,
|
||||
const PointerIterationChecker *Checker) {
|
||||
auto *ADC = AM.getAnalysisDeclContext(D);
|
||||
|
||||
const auto *MarkedStmt = Match.getNodeAs<Stmt>(WarnAtNode);
|
||||
assert(MarkedStmt);
|
||||
|
||||
auto Range = MarkedStmt->getSourceRange();
|
||||
auto Location = PathDiagnosticLocation::createBegin(MarkedStmt,
|
||||
BR.getSourceManager(),
|
||||
ADC);
|
||||
std::string Diagnostics;
|
||||
llvm::raw_string_ostream OS(Diagnostics);
|
||||
OS << "Iteration of pointer-like elements "
|
||||
<< "can result in non-deterministic ordering";
|
||||
|
||||
BR.EmitBasicReport(ADC->getDecl(), Checker,
|
||||
"Iteration of pointer-like elements", "Non-determinism",
|
||||
Diagnostics, Location, Range);
|
||||
}
|
||||
|
||||
// Assumption: Iteration of ordered containers of pointers is deterministic.
|
||||
|
||||
// TODO: Currently, we only check for std::unordered_set. Other unordered
|
||||
// containers like std::unordered_map also need to be handled.
|
||||
|
||||
// TODO: Currently, we do not check what the for loop does with the iterated
|
||||
// pointer values. Not all iterations may cause non-determinism. For example,
|
||||
// counting or summing up the elements should not be non-deterministic.
|
||||
|
||||
auto matchUnorderedIterWithPointers() -> decltype(decl()) {
|
||||
|
||||
auto UnorderedContainerM = declRefExpr(to(varDecl(hasType(
|
||||
recordDecl(hasName("std::unordered_set")
|
||||
)))));
|
||||
|
||||
auto PointerTypeM = varDecl(hasType(hasCanonicalType(pointerType())));
|
||||
|
||||
auto PointerIterM = stmt(cxxForRangeStmt(
|
||||
hasLoopVariable(PointerTypeM),
|
||||
hasRangeInit(UnorderedContainerM)
|
||||
)).bind(WarnAtNode);
|
||||
|
||||
return decl(forEachDescendant(PointerIterM));
|
||||
}
|
||||
|
||||
void PointerIterationChecker::checkASTCodeBody(const Decl *D,
|
||||
AnalysisManager &AM,
|
||||
BugReporter &BR) const {
|
||||
auto MatcherM = matchUnorderedIterWithPointers();
|
||||
|
||||
auto Matches = match(MatcherM, *D, AM.getASTContext());
|
||||
for (const auto &Match : Matches)
|
||||
emitDiagnostics(Match, D, BR, AM, this);
|
||||
}
|
||||
|
||||
} // end of anonymous namespace
|
||||
|
||||
void ento::registerPointerIterationChecker(CheckerManager &Mgr) {
|
||||
Mgr.registerChecker<PointerIterationChecker>();
|
||||
}
|
||||
|
||||
bool ento::shouldRegisterPointerIterationChecker(const CheckerManager &mgr) {
|
||||
const LangOptions &LO = mgr.getLangOpts();
|
||||
return LO.CPlusPlus;
|
||||
}
|
@ -1,115 +0,0 @@
|
||||
//== PointerSortingChecker.cpp --------------------------------- -*- C++ -*--=//
|
||||
//
|
||||
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
|
||||
// See https://llvm.org/LICENSE.txt for license information.
|
||||
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
//
|
||||
// This file defines PointerSortingChecker which checks for non-determinism
|
||||
// caused due to sorting containers with pointer-like elements.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#include "clang/ASTMatchers/ASTMatchFinder.h"
|
||||
#include "clang/StaticAnalyzer/Checkers/BuiltinCheckerRegistration.h"
|
||||
#include "clang/StaticAnalyzer/Core/Checker.h"
|
||||
#include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h"
|
||||
|
||||
using namespace clang;
|
||||
using namespace ento;
|
||||
using namespace ast_matchers;
|
||||
|
||||
namespace {
|
||||
|
||||
// ID of a node at which the diagnostic would be emitted.
|
||||
constexpr llvm::StringLiteral WarnAtNode = "sort";
|
||||
|
||||
class PointerSortingChecker : public Checker<check::ASTCodeBody> {
|
||||
public:
|
||||
void checkASTCodeBody(const Decl *D,
|
||||
AnalysisManager &AM,
|
||||
BugReporter &BR) const;
|
||||
};
|
||||
|
||||
static void emitDiagnostics(const BoundNodes &Match, const Decl *D,
|
||||
BugReporter &BR, AnalysisManager &AM,
|
||||
const PointerSortingChecker *Checker) {
|
||||
auto *ADC = AM.getAnalysisDeclContext(D);
|
||||
|
||||
const auto *MarkedStmt = Match.getNodeAs<CallExpr>(WarnAtNode);
|
||||
assert(MarkedStmt);
|
||||
|
||||
auto Range = MarkedStmt->getSourceRange();
|
||||
auto Location = PathDiagnosticLocation::createBegin(MarkedStmt,
|
||||
BR.getSourceManager(),
|
||||
ADC);
|
||||
std::string Diagnostics;
|
||||
llvm::raw_string_ostream OS(Diagnostics);
|
||||
OS << "Sorting pointer-like elements "
|
||||
<< "can result in non-deterministic ordering";
|
||||
|
||||
BR.EmitBasicReport(ADC->getDecl(), Checker,
|
||||
"Sorting of pointer-like elements", "Non-determinism",
|
||||
OS.str(), Location, Range);
|
||||
}
|
||||
|
||||
decltype(auto) callsName(const char *FunctionName) {
|
||||
return callee(functionDecl(hasName(FunctionName)));
|
||||
}
|
||||
|
||||
// FIXME: Currently we simply check if std::sort is used with pointer-like
|
||||
// elements. This approach can have a big false positive rate. Using std::sort,
|
||||
// std::unique and then erase is common technique for deduplicating a container
|
||||
// (which in some cases might even be quicker than using, let's say std::set).
|
||||
// In case a container contains arbitrary memory addresses (e.g. multiple
|
||||
// things give different stuff but might give the same thing multiple times)
|
||||
// which we don't want to do things with more than once, we might use
|
||||
// sort-unique-erase and the sort call will emit a report.
|
||||
auto matchSortWithPointers() -> decltype(decl()) {
|
||||
// Match any of these function calls.
|
||||
auto SortFuncM = anyOf(
|
||||
callsName("std::is_sorted"),
|
||||
callsName("std::nth_element"),
|
||||
callsName("std::partial_sort"),
|
||||
callsName("std::partition"),
|
||||
callsName("std::sort"),
|
||||
callsName("std::stable_partition"),
|
||||
callsName("std::stable_sort")
|
||||
);
|
||||
|
||||
// Match only if the container has pointer-type elements.
|
||||
auto IteratesPointerEltsM = hasArgument(0,
|
||||
hasType(cxxRecordDecl(has(
|
||||
fieldDecl(hasType(hasCanonicalType(
|
||||
pointsTo(hasCanonicalType(pointerType()))
|
||||
)))
|
||||
))));
|
||||
|
||||
auto PointerSortM = traverse(
|
||||
TK_AsIs,
|
||||
stmt(callExpr(allOf(SortFuncM, IteratesPointerEltsM))).bind(WarnAtNode));
|
||||
|
||||
return decl(forEachDescendant(PointerSortM));
|
||||
}
|
||||
|
||||
void PointerSortingChecker::checkASTCodeBody(const Decl *D,
|
||||
AnalysisManager &AM,
|
||||
BugReporter &BR) const {
|
||||
auto MatcherM = matchSortWithPointers();
|
||||
|
||||
auto Matches = match(MatcherM, *D, AM.getASTContext());
|
||||
for (const auto &Match : Matches)
|
||||
emitDiagnostics(Match, D, BR, AM, this);
|
||||
}
|
||||
|
||||
} // end of anonymous namespace
|
||||
|
||||
void ento::registerPointerSortingChecker(CheckerManager &Mgr) {
|
||||
Mgr.registerChecker<PointerSortingChecker>();
|
||||
}
|
||||
|
||||
bool ento::shouldRegisterPointerSortingChecker(const CheckerManager &mgr) {
|
||||
const LangOptions &LO = mgr.getLangOpts();
|
||||
return LO.CPlusPlus;
|
||||
}
|
@ -1,28 +0,0 @@
|
||||
// RUN: %clang_analyze_cc1 %s -std=c++14 -analyzer-output=text -verify \
|
||||
// RUN: -analyzer-checker=core,alpha.nondeterminism.PointerIteration
|
||||
|
||||
#include "Inputs/system-header-simulator-cxx.h"
|
||||
|
||||
template<class T>
|
||||
void f(T x);
|
||||
|
||||
void PointerIteration() {
|
||||
int a = 1, b = 2;
|
||||
std::set<int> OrderedIntSet = {a, b};
|
||||
std::set<int *> OrderedPtrSet = {&a, &b};
|
||||
std::unordered_set<int> UnorderedIntSet = {a, b};
|
||||
std::unordered_set<int *> UnorderedPtrSet = {&a, &b};
|
||||
|
||||
for (auto i : OrderedIntSet) // no-warning
|
||||
f(i);
|
||||
|
||||
for (auto i : OrderedPtrSet) // no-warning
|
||||
f(i);
|
||||
|
||||
for (auto i : UnorderedIntSet) // no-warning
|
||||
f(i);
|
||||
|
||||
for (auto i : UnorderedPtrSet) // expected-warning {{Iteration of pointer-like elements can result in non-deterministic ordering}} [alpha.nondeterminism.PointerIteration]
|
||||
// expected-note@-1 {{Iteration of pointer-like elements can result in non-deterministic ordering}} [alpha.nondeterminism.PointerIteration]
|
||||
f(i);
|
||||
}
|
@ -1,36 +0,0 @@
|
||||
// RUN: %clang_analyze_cc1 %s -std=c++14 -analyzer-output=text -verify \
|
||||
// RUN: -analyzer-checker=core,alpha.nondeterminism.PointerSorting
|
||||
|
||||
#include "Inputs/system-header-simulator-cxx.h"
|
||||
|
||||
bool f(int x) { return true; }
|
||||
bool g(int *x) { return true; }
|
||||
|
||||
void PointerSorting() {
|
||||
int a = 1, b = 2;
|
||||
std::vector<int> V1 = {a, b};
|
||||
std::vector<int *> V2 = {&a, &b};
|
||||
|
||||
std::is_sorted(V1.begin(), V1.end()); // no-warning
|
||||
std::nth_element(V1.begin(), V1.begin() + 1, V1.end()); // no-warning
|
||||
std::partial_sort(V1.begin(), V1.begin() + 1, V1.end()); // no-warning
|
||||
std::sort(V1.begin(), V1.end()); // no-warning
|
||||
std::stable_sort(V1.begin(), V1.end()); // no-warning
|
||||
std::partition(V1.begin(), V1.end(), f); // no-warning
|
||||
std::stable_partition(V1.begin(), V1.end(), g); // no-warning
|
||||
|
||||
std::is_sorted(V2.begin(), V2.end()); // expected-warning {{Sorting pointer-like elements can result in non-deterministic ordering}} [alpha.nondeterminism.PointerSorting]
|
||||
// expected-note@-1 {{Sorting pointer-like elements can result in non-deterministic ordering}} [alpha.nondeterminism.PointerSorting]
|
||||
std::nth_element(V2.begin(), V2.begin() + 1, V2.end()); // expected-warning {{Sorting pointer-like elements can result in non-deterministic ordering}} [alpha.nondeterminism.PointerSorting]
|
||||
// expected-note@-1 {{Sorting pointer-like elements can result in non-deterministic ordering}} [alpha.nondeterminism.PointerSorting]
|
||||
std::partial_sort(V2.begin(), V2.begin() + 1, V2.end()); // expected-warning {{Sorting pointer-like elements can result in non-deterministic ordering}} [alpha.nondeterminism.PointerSorting]
|
||||
// expected-note@-1 {{Sorting pointer-like elements can result in non-deterministic ordering}} [alpha.nondeterminism.PointerSorting]
|
||||
std::sort(V2.begin(), V2.end()); // expected-warning {{Sorting pointer-like elements can result in non-deterministic ordering}} [alpha.nondeterminism.PointerSorting]
|
||||
// expected-note@-1 {{Sorting pointer-like elements can result in non-deterministic ordering}} [alpha.nondeterminism.PointerSorting]
|
||||
std::stable_sort(V2.begin(), V2.end()); // expected-warning {{Sorting pointer-like elements can result in non-deterministic ordering}} [alpha.nondeterminism.PointerSorting]
|
||||
// expected-note@-1 {{Sorting pointer-like elements can result in non-deterministic ordering}} [alpha.nondeterminism.PointerSorting]
|
||||
std::partition(V2.begin(), V2.end(), f); // expected-warning {{Sorting pointer-like elements can result in non-deterministic ordering}} [alpha.nondeterminism.PointerSorting]
|
||||
// expected-note@-1 {{Sorting pointer-like elements can result in non-deterministic ordering}} [alpha.nondeterminism.PointerSorting]
|
||||
std::stable_partition(V2.begin(), V2.end(), g); // expected-warning {{Sorting pointer-like elements can result in non-deterministic ordering}} [alpha.nondeterminism.PointerSorting]
|
||||
// expected-note@-1 {{Sorting pointer-like elements can result in non-deterministic ordering}} [alpha.nondeterminism.PointerSorting]
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user