[clang] [clang-tools-extra] RFC: [clang-tidy] [analyzer] Move nondeterministic pointer usage check to tidy (PR #110471)
via cfe-commits
cfe-commits at lists.llvm.org
Wed Oct 23 06:59:46 PDT 2024
https://github.com/vabridgers updated https://github.com/llvm/llvm-project/pull/110471
>From f46757d56f5205b76664b8e31d9a34acd396d08c Mon Sep 17 00:00:00 2001
From: Vince Bridgers <vince.a.bridgers at ericsson.com>
Date: Thu, 26 Sep 2024 16:24:59 +0200
Subject: [PATCH] [clang-tidy] [analyzer] Move nondeterministic pointer usage
check to tidy
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 clang-tidy
checks 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.
---
.../bugprone/BugproneTidyModule.cpp | 3 +
.../clang-tidy/bugprone/CMakeLists.txt | 1 +
...eterministicPointerIterationOrderCheck.cpp | 85 ++++++++++
...ndeterministicPointerIterationOrderCheck.h | 37 +++++
clang-tools-extra/docs/ReleaseNotes.rst | 6 +
...ndeterministic-pointer-iteration-order.rst | 35 ++++
.../system-header-simulator/sim_algorithm | 31 ++++
.../system-header-simulator/sim_c++config.h | 11 ++
.../sim_initializer_list | 39 +++++
.../system-header-simulator/sim_iterator_base | 22 +++
.../Inputs/system-header-simulator/sim_map | 34 ++++
.../Inputs/system-header-simulator/sim_set | 44 +++++
.../system-header-simulator/sim_stl_pair | 32 ++++
.../system-header-simulator/sim_type_traits | 19 +++
.../system-header-simulator/sim_unordered_map | 33 ++++
.../system-header-simulator/sim_unordered_set | 35 ++++
.../Inputs/system-header-simulator/sim_vector | 150 ++++++++++++++++++
...ndeterministic-pointer-iteration-order.cpp | 84 ++++++++++
clang/docs/ReleaseNotes.rst | 6 +
clang/docs/analyzer/checkers.rst | 31 ----
.../clang/StaticAnalyzer/Checkers/Checkers.td | 18 ---
.../StaticAnalyzer/Checkers/CMakeLists.txt | 2 -
.../Checkers/PointerIterationChecker.cpp | 101 ------------
.../Checkers/PointerSortingChecker.cpp | 115 --------------
clang/test/Analysis/ptr-iter.cpp | 28 ----
clang/test/Analysis/ptr-sort.cpp | 36 -----
26 files changed, 707 insertions(+), 331 deletions(-)
create mode 100644 clang-tools-extra/clang-tidy/bugprone/NondeterministicPointerIterationOrderCheck.cpp
create mode 100644 clang-tools-extra/clang-tidy/bugprone/NondeterministicPointerIterationOrderCheck.h
create mode 100644 clang-tools-extra/docs/clang-tidy/checks/bugprone/nondeterministic-pointer-iteration-order.rst
create mode 100644 clang-tools-extra/test/clang-tidy/checkers/bugprone/Inputs/system-header-simulator/sim_algorithm
create mode 100644 clang-tools-extra/test/clang-tidy/checkers/bugprone/Inputs/system-header-simulator/sim_c++config.h
create mode 100644 clang-tools-extra/test/clang-tidy/checkers/bugprone/Inputs/system-header-simulator/sim_initializer_list
create mode 100644 clang-tools-extra/test/clang-tidy/checkers/bugprone/Inputs/system-header-simulator/sim_iterator_base
create mode 100644 clang-tools-extra/test/clang-tidy/checkers/bugprone/Inputs/system-header-simulator/sim_map
create mode 100644 clang-tools-extra/test/clang-tidy/checkers/bugprone/Inputs/system-header-simulator/sim_set
create mode 100644 clang-tools-extra/test/clang-tidy/checkers/bugprone/Inputs/system-header-simulator/sim_stl_pair
create mode 100644 clang-tools-extra/test/clang-tidy/checkers/bugprone/Inputs/system-header-simulator/sim_type_traits
create mode 100644 clang-tools-extra/test/clang-tidy/checkers/bugprone/Inputs/system-header-simulator/sim_unordered_map
create mode 100644 clang-tools-extra/test/clang-tidy/checkers/bugprone/Inputs/system-header-simulator/sim_unordered_set
create mode 100644 clang-tools-extra/test/clang-tidy/checkers/bugprone/Inputs/system-header-simulator/sim_vector
create mode 100644 clang-tools-extra/test/clang-tidy/checkers/bugprone/nondeterministic-pointer-iteration-order.cpp
delete mode 100644 clang/lib/StaticAnalyzer/Checkers/PointerIterationChecker.cpp
delete mode 100644 clang/lib/StaticAnalyzer/Checkers/PointerSortingChecker.cpp
delete mode 100644 clang/test/Analysis/ptr-iter.cpp
delete mode 100644 clang/test/Analysis/ptr-sort.cpp
diff --git a/clang-tools-extra/clang-tidy/bugprone/BugproneTidyModule.cpp b/clang-tools-extra/clang-tidy/bugprone/BugproneTidyModule.cpp
index 9120c4b6c0d9ae..33ac65e715ce81 100644
--- a/clang-tools-extra/clang-tidy/bugprone/BugproneTidyModule.cpp
+++ b/clang-tools-extra/clang-tidy/bugprone/BugproneTidyModule.cpp
@@ -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 @@ class BugproneModule : public ClangTidyModule {
"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>(
diff --git a/clang-tools-extra/clang-tidy/bugprone/CMakeLists.txt b/clang-tools-extra/clang-tidy/bugprone/CMakeLists.txt
index f0667bbfdd87f7..b0a2318acc0597 100644
--- a/clang-tools-extra/clang-tidy/bugprone/CMakeLists.txt
+++ b/clang-tools-extra/clang-tidy/bugprone/CMakeLists.txt
@@ -45,6 +45,7 @@ add_clang_library(clangTidyBugproneModule STATIC
MultipleNewInOneExpressionCheck.cpp
MultipleStatementMacroCheck.cpp
NoEscapeCheck.cpp
+ NondeterministicPointerIterationOrderCheck.cpp
NonZeroEnumToBoolConversionCheck.cpp
NotNullTerminatedResultCheck.cpp
OptionalValueConversionCheck.cpp
diff --git a/clang-tools-extra/clang-tidy/bugprone/NondeterministicPointerIterationOrderCheck.cpp b/clang-tools-extra/clang-tidy/bugprone/NondeterministicPointerIterationOrderCheck.cpp
new file mode 100644
index 00000000000000..974ae4c3afbf38
--- /dev/null
+++ b/clang-tools-extra/clang-tidy/bugprone/NondeterministicPointerIterationOrderCheck.cpp
@@ -0,0 +1,85 @@
+//===----- 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 Unordered = AlgoName.contains("unordered");
+ const bool SetLike = AlgoName.contains("set");
+ const bool MapLike = AlgoName.contains("map");
+ const bool IsAlgoArgPointer =
+ TemplateArgs[0].getAsType()->isPointerType();
+ assert(Unordered && "Expecting unordered algo!");
+ assert((SetLike ^ MapLike) &&
+ "Expecting either SetLike or Maplike Unordered Algo!");
+
+ 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
diff --git a/clang-tools-extra/clang-tidy/bugprone/NondeterministicPointerIterationOrderCheck.h b/clang-tools-extra/clang-tidy/bugprone/NondeterministicPointerIterationOrderCheck.h
new file mode 100644
index 00000000000000..716dd5bfcb3bc4
--- /dev/null
+++ b/clang-tools-extra/clang-tidy/bugprone/NondeterministicPointerIterationOrderCheck.h
@@ -0,0 +1,37 @@
+//=== 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.
+///
+/// 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
diff --git a/clang-tools-extra/docs/ReleaseNotes.rst b/clang-tools-extra/docs/ReleaseNotes.rst
index 9afe497fb3d8bf..adbcb2b4b5d7ba 100644
--- a/clang-tools-extra/docs/ReleaseNotes.rst
+++ b/clang-tools-extra/docs/ReleaseNotes.rst
@@ -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.
diff --git a/clang-tools-extra/docs/clang-tidy/checks/bugprone/nondeterministic-pointer-iteration-order.rst b/clang-tools-extra/docs/clang-tidy/checks/bugprone/nondeterministic-pointer-iteration-order.rst
new file mode 100644
index 00000000000000..f7bd6fd1263858
--- /dev/null
+++ b/clang-tools-extra/docs/clang-tidy/checks/bugprone/nondeterministic-pointer-iteration-order.rst
@@ -0,0 +1,35 @@
+.. 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.
+Other similar usages will not be found and are false negatives.
diff --git a/clang-tools-extra/test/clang-tidy/checkers/bugprone/Inputs/system-header-simulator/sim_algorithm b/clang-tools-extra/test/clang-tidy/checkers/bugprone/Inputs/system-header-simulator/sim_algorithm
new file mode 100644
index 00000000000000..6dbca55a8e365f
--- /dev/null
+++ b/clang-tools-extra/test/clang-tidy/checkers/bugprone/Inputs/system-header-simulator/sim_algorithm
@@ -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
diff --git a/clang-tools-extra/test/clang-tidy/checkers/bugprone/Inputs/system-header-simulator/sim_c++config.h b/clang-tools-extra/test/clang-tidy/checkers/bugprone/Inputs/system-header-simulator/sim_c++config.h
new file mode 100644
index 00000000000000..ba98e0cc2208ba
--- /dev/null
+++ b/clang-tools-extra/test/clang-tidy/checkers/bugprone/Inputs/system-header-simulator/sim_c++config.h
@@ -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
diff --git a/clang-tools-extra/test/clang-tidy/checkers/bugprone/Inputs/system-header-simulator/sim_initializer_list b/clang-tools-extra/test/clang-tidy/checkers/bugprone/Inputs/system-header-simulator/sim_initializer_list
new file mode 100644
index 00000000000000..e4d9d534b3bd78
--- /dev/null
+++ b/clang-tools-extra/test/clang-tidy/checkers/bugprone/Inputs/system-header-simulator/sim_initializer_list
@@ -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
diff --git a/clang-tools-extra/test/clang-tidy/checkers/bugprone/Inputs/system-header-simulator/sim_iterator_base b/clang-tools-extra/test/clang-tidy/checkers/bugprone/Inputs/system-header-simulator/sim_iterator_base
new file mode 100644
index 00000000000000..3b205d1722c9dd
--- /dev/null
+++ b/clang-tools-extra/test/clang-tidy/checkers/bugprone/Inputs/system-header-simulator/sim_iterator_base
@@ -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
diff --git a/clang-tools-extra/test/clang-tidy/checkers/bugprone/Inputs/system-header-simulator/sim_map b/clang-tools-extra/test/clang-tidy/checkers/bugprone/Inputs/system-header-simulator/sim_map
new file mode 100644
index 00000000000000..8c57f5c71f8814
--- /dev/null
+++ b/clang-tools-extra/test/clang-tidy/checkers/bugprone/Inputs/system-header-simulator/sim_map
@@ -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
diff --git a/clang-tools-extra/test/clang-tidy/checkers/bugprone/Inputs/system-header-simulator/sim_set b/clang-tools-extra/test/clang-tidy/checkers/bugprone/Inputs/system-header-simulator/sim_set
new file mode 100644
index 00000000000000..f2f70095538925
--- /dev/null
+++ b/clang-tools-extra/test/clang-tidy/checkers/bugprone/Inputs/system-header-simulator/sim_set
@@ -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
diff --git a/clang-tools-extra/test/clang-tidy/checkers/bugprone/Inputs/system-header-simulator/sim_stl_pair b/clang-tools-extra/test/clang-tidy/checkers/bugprone/Inputs/system-header-simulator/sim_stl_pair
new file mode 100644
index 00000000000000..d244bb363b861a
--- /dev/null
+++ b/clang-tools-extra/test/clang-tidy/checkers/bugprone/Inputs/system-header-simulator/sim_stl_pair
@@ -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
+
diff --git a/clang-tools-extra/test/clang-tidy/checkers/bugprone/Inputs/system-header-simulator/sim_type_traits b/clang-tools-extra/test/clang-tidy/checkers/bugprone/Inputs/system-header-simulator/sim_type_traits
new file mode 100644
index 00000000000000..f066767c4d9858
--- /dev/null
+++ b/clang-tools-extra/test/clang-tidy/checkers/bugprone/Inputs/system-header-simulator/sim_type_traits
@@ -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
diff --git a/clang-tools-extra/test/clang-tidy/checkers/bugprone/Inputs/system-header-simulator/sim_unordered_map b/clang-tools-extra/test/clang-tidy/checkers/bugprone/Inputs/system-header-simulator/sim_unordered_map
new file mode 100644
index 00000000000000..fabd8e7fd2d748
--- /dev/null
+++ b/clang-tools-extra/test/clang-tidy/checkers/bugprone/Inputs/system-header-simulator/sim_unordered_map
@@ -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
diff --git a/clang-tools-extra/test/clang-tidy/checkers/bugprone/Inputs/system-header-simulator/sim_unordered_set b/clang-tools-extra/test/clang-tidy/checkers/bugprone/Inputs/system-header-simulator/sim_unordered_set
new file mode 100644
index 00000000000000..a077507bbdcbcb
--- /dev/null
+++ b/clang-tools-extra/test/clang-tidy/checkers/bugprone/Inputs/system-header-simulator/sim_unordered_set
@@ -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
diff --git a/clang-tools-extra/test/clang-tidy/checkers/bugprone/Inputs/system-header-simulator/sim_vector b/clang-tools-extra/test/clang-tidy/checkers/bugprone/Inputs/system-header-simulator/sim_vector
new file mode 100644
index 00000000000000..dfa9abfb8863ec
--- /dev/null
+++ b/clang-tools-extra/test/clang-tidy/checkers/bugprone/Inputs/system-header-simulator/sim_vector
@@ -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
diff --git a/clang-tools-extra/test/clang-tidy/checkers/bugprone/nondeterministic-pointer-iteration-order.cpp b/clang-tools-extra/test/clang-tidy/checkers/bugprone/nondeterministic-pointer-iteration-order.cpp
new file mode 100644
index 00000000000000..91853874d0afc9
--- /dev/null
+++ b/clang-tools-extra/test/clang-tidy/checkers/bugprone/nondeterministic-pointer-iteration-order.cpp
@@ -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
+}
diff --git a/clang/docs/ReleaseNotes.rst b/clang/docs/ReleaseNotes.rst
index 28bb83a1c9d60f..1336903348b7f5 100644
--- a/clang/docs/ReleaseNotes.rst
+++ b/clang/docs/ReleaseNotes.rst
@@ -752,6 +752,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
diff --git a/clang/docs/analyzer/checkers.rst b/clang/docs/analyzer/checkers.rst
index 58dbd686a6dc9f..87b03438e6e0b9 100644
--- a/clang/docs/analyzer/checkers.rst
+++ b/clang/docs/analyzer/checkers.rst
@@ -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
^^^^^^^^^^^^
diff --git a/clang/include/clang/StaticAnalyzer/Checkers/Checkers.td b/clang/include/clang/StaticAnalyzer/Checkers/Checkers.td
index 349040c15eeb83..9a6b35c1b9f774 100644
--- a/clang/include/clang/StaticAnalyzer/Checkers/Checkers.td
+++ b/clang/include/clang/StaticAnalyzer/Checkers/Checkers.td
@@ -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.
//===----------------------------------------------------------------------===//
diff --git a/clang/lib/StaticAnalyzer/Checkers/CMakeLists.txt b/clang/lib/StaticAnalyzer/Checkers/CMakeLists.txt
index 6da3665ab9a4df..62aa5ff7f002a9 100644
--- a/clang/lib/StaticAnalyzer/Checkers/CMakeLists.txt
+++ b/clang/lib/StaticAnalyzer/Checkers/CMakeLists.txt
@@ -91,8 +91,6 @@ add_clang_library(clangStaticAnalyzerCheckers
OSObjectCStyleCast.cpp
PaddingChecker.cpp
PointerArithChecker.cpp
- PointerIterationChecker.cpp
- PointerSortingChecker.cpp
PointerSubChecker.cpp
PthreadLockChecker.cpp
PutenvStackArrayChecker.cpp
diff --git a/clang/lib/StaticAnalyzer/Checkers/PointerIterationChecker.cpp b/clang/lib/StaticAnalyzer/Checkers/PointerIterationChecker.cpp
deleted file mode 100644
index 895b2160b76a7b..00000000000000
--- a/clang/lib/StaticAnalyzer/Checkers/PointerIterationChecker.cpp
+++ /dev/null
@@ -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;
-}
diff --git a/clang/lib/StaticAnalyzer/Checkers/PointerSortingChecker.cpp b/clang/lib/StaticAnalyzer/Checkers/PointerSortingChecker.cpp
deleted file mode 100644
index 25d87f4acfc910..00000000000000
--- a/clang/lib/StaticAnalyzer/Checkers/PointerSortingChecker.cpp
+++ /dev/null
@@ -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;
-}
diff --git a/clang/test/Analysis/ptr-iter.cpp b/clang/test/Analysis/ptr-iter.cpp
deleted file mode 100644
index a94288cd1c8ccc..00000000000000
--- a/clang/test/Analysis/ptr-iter.cpp
+++ /dev/null
@@ -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 at -1 {{Iteration of pointer-like elements can result in non-deterministic ordering}} [alpha.nondeterminism.PointerIteration]
- f(i);
-}
diff --git a/clang/test/Analysis/ptr-sort.cpp b/clang/test/Analysis/ptr-sort.cpp
deleted file mode 100644
index d238b390bdc235..00000000000000
--- a/clang/test/Analysis/ptr-sort.cpp
+++ /dev/null
@@ -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 at -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 at -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 at -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 at -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 at -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 at -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 at -1 {{Sorting pointer-like elements can result in non-deterministic ordering}} [alpha.nondeterminism.PointerSorting]
-}
More information about the cfe-commits
mailing list