whisperity updated this revision to Diff 227254.
whisperity added a comment.

- Removed //`This check`// from the documentation comment of the check's class 
too.


CHANGES SINCE LAST ACTION
  https://reviews.llvm.org/D69560/new/

https://reviews.llvm.org/D69560

Files:
  
clang-tools-extra/clang-tidy/cppcoreguidelines/AdjacentArgumentsOfSameTypeCheck.cpp
  
clang-tools-extra/clang-tidy/cppcoreguidelines/AdjacentArgumentsOfSameTypeCheck.h
  clang-tools-extra/clang-tidy/cppcoreguidelines/CMakeLists.txt
  clang-tools-extra/clang-tidy/cppcoreguidelines/CppCoreGuidelinesTidyModule.cpp
  clang-tools-extra/docs/ReleaseNotes.rst
  
clang-tools-extra/docs/clang-tidy/checks/cppcoreguidelines-avoid-adjacent-arguments-of-same-type.rst
  clang-tools-extra/docs/clang-tidy/checks/list.rst
  
clang-tools-extra/test/clang-tidy/checkers/cppcoreguidelines-avoid-adjacent-arguments-of-same-type-cvr-on.cpp
  
clang-tools-extra/test/clang-tidy/checkers/cppcoreguidelines-avoid-adjacent-arguments-of-same-type-default.cpp
  
clang-tools-extra/test/clang-tidy/checkers/cppcoreguidelines-avoid-adjacent-arguments-of-same-type-verbose.cpp
  
clang-tools-extra/test/clang-tidy/checkers/cppcoreguidelines-avoid-adjacent-arguments-of-same-type.c

Index: clang-tools-extra/test/clang-tidy/checkers/cppcoreguidelines-avoid-adjacent-arguments-of-same-type.c
===================================================================
--- /dev/null
+++ clang-tools-extra/test/clang-tidy/checkers/cppcoreguidelines-avoid-adjacent-arguments-of-same-type.c
@@ -0,0 +1,31 @@
+// RUN: %check_clang_tidy %s \
+// RUN:   cppcoreguidelines-avoid-adjacent-arguments-of-same-type %t \
+// RUN:   -config='{CheckOptions: [ \
+// RUN:     {key: cppcoreguidelines-avoid-adjacent-arguments-of-same-type.MinimumLength, value: 2}, \
+// RUN:     {key: cppcoreguidelines-avoid-adjacent-arguments-of-same-type.CVRMixPossible, value: 1} \
+// RUN:   ]}' --
+
+struct T {};
+
+void memcpy(struct T *restrict dest, const struct T *restrict src) {}
+// CHECK-MESSAGES: :[[@LINE-1]]:13: warning: 2 adjacent arguments for 'memcpy' of similar type are
+// CHECK-MESSAGES: :[[@LINE-2]]:63: note: last argument in the adjacent range
+// CHECK-MESSAGES: :[[@LINE-3]]:38: note: at a call site, 'const struct T * restrict' might bind with same force as 'struct T *restrict'
+
+void merge(struct T *dst, const struct T *src1, const struct T *src2) {}
+// CHECK-MESSAGES: :[[@LINE-1]]:12: warning: 3 adjacent arguments for 'merge' of similar type are
+// CHECK-MESSAGES: :[[@LINE-2]]:65: note: last argument in the adjacent range
+// CHECK-MESSAGES: :[[@LINE-3]]:27: note: at a call site, 'const struct T *' might bind with same force as 'struct T *'
+// CHECK-MESSAGES: :[[@LINE-4]]:49: note: at a call site, 'const struct T *' might bind with same force as 'struct T *'
+
+int equals(struct T a, struct T b) {}
+// CHECK-MESSAGES: :[[@LINE-1]]:12: warning: 2 adjacent arguments for 'equals' of similar type ('struct T') are
+// CHECK-MESSAGES: :[[@LINE-2]]:33: note: last argument in the adjacent range
+
+typedef struct {
+  int x;
+} S;
+
+int equals2(S l, S r) { return l.x == r.x; }
+// CHECK-MESSAGES: :[[@LINE-1]]:13: warning: 2 adjacent arguments for 'equals2' of similar type ('S') are
+// CHECK-MESSAGES: :[[@LINE-2]]:20: note: last argument in the adjacent range
Index: clang-tools-extra/test/clang-tidy/checkers/cppcoreguidelines-avoid-adjacent-arguments-of-same-type-verbose.cpp
===================================================================
--- /dev/null
+++ clang-tools-extra/test/clang-tidy/checkers/cppcoreguidelines-avoid-adjacent-arguments-of-same-type-verbose.cpp
@@ -0,0 +1,420 @@
+// RUN: %check_clang_tidy %s \
+// RUN:   cppcoreguidelines-avoid-adjacent-arguments-of-same-type %t \
+// RUN:   -config='{CheckOptions: [ \
+// RUN:     {key: cppcoreguidelines-avoid-adjacent-arguments-of-same-type.MinimumLength, value: 2}, \
+// RUN:     {key: cppcoreguidelines-avoid-adjacent-arguments-of-same-type.CVRMixPossible, value: 1} \
+// RUN:   ]}' --
+
+void library(void *vp, void *vp2, void *vp3, int n, int m);
+// NO-WARN: The user has no chance to change only declared (usually library)
+// functions, so no diagnostic is made.
+
+struct T {};
+
+void create(T **out_t) {} // NO-WARN
+
+void copy(T *p, T *q, int n) {}
+// CHECK-MESSAGES: :[[@LINE-1]]:11: warning: 2 adjacent arguments for 'copy' of similar type ('T *') are easily swapped by mistake [cppcoreguidelines-avoid-adjacent-arguments-of-same-type]
+// CHECK-MESSAGES: :[[@LINE-2]]:20: note: last argument in the adjacent range here
+
+void mov(T *dst, const T *src) {}
+// CHECK-MESSAGES: :[[@LINE-1]]:10: warning: 2 adjacent arguments for 'mov' of similar type are
+// CHECK-MESSAGES: :[[@LINE-2]]:27: note: last argument in the adjacent range here
+// CHECK-MESSAGES: :[[@LINE-3]]:18: note: at a call site, 'const T *' might bind with same force as 'T *'
+
+void compare01(T t1, T t2) {}
+// CHECK-MESSAGES: :[[@LINE-1]]:16: warning: 2 adjacent arguments for 'compare01' of similar type ('T') are
+// CHECK-MESSAGES: :[[@LINE-2]]:24: note: last argument in the adjacent range
+
+void compare02(const T t1, T t2) {}
+// CHECK-MESSAGES: :[[@LINE-1]]:16: warning: 2 adjacent arguments for 'compare02' of similar type are
+// CHECK-MESSAGES: :[[@LINE-2]]:30: note: last argument in the adjacent range
+// CHECK-MESSAGES: :[[@LINE-3]]:28: note: at a call site, 'T' might bind with same force as 'const T'
+
+void compare03(T t1, const T t2) {}
+// CHECK-MESSAGES: :[[@LINE-1]]:16: warning: 2 adjacent arguments for 'compare03' of similar type are
+// CHECK-MESSAGES: :[[@LINE-2]]:30: note: last argument in the adjacent range
+// CHECK-MESSAGES: :[[@LINE-3]]:22: note: at a call site, 'const T' might bind with same force as 'T'
+
+void compare04(const T &t1, T t2) {}
+// CHECK-MESSAGES: :[[@LINE-1]]:16: warning: 2 adjacent arguments for 'compare04' of similar type are
+// CHECK-MESSAGES: :[[@LINE-2]]:31: note: last argument in the adjacent range
+// CHECK-MESSAGES: :[[@LINE-3]]:29: note: at a call site, 'T' might bind with same force as 'const T &'
+
+void compare05(T t1, const T &t2) {}
+// CHECK-MESSAGES: :[[@LINE-1]]:16: warning: 2 adjacent arguments for 'compare05' of similar type are
+// CHECK-MESSAGES: :[[@LINE-2]]:31: note: last argument in the adjacent range
+// CHECK-MESSAGES: :[[@LINE-3]]:22: note: at a call site, 'const T &' might bind with same force as 'T'
+
+void compare06(const T &t1, const T &t2) {}
+// CHECK-MESSAGES: :[[@LINE-1]]:16: warning: 2 adjacent arguments for 'compare06' of similar type ('const T &') are
+// CHECK-MESSAGES: :[[@LINE-2]]:38: note: last argument in the adjacent range
+
+void compare07(const T &t1, const T t2) {}
+// CHECK-MESSAGES: :[[@LINE-1]]:16: warning: 2 adjacent arguments for 'compare07' of similar type are
+// CHECK-MESSAGES: :[[@LINE-2]]:37: note: last argument in the adjacent range
+// CHECK-MESSAGES: :[[@LINE-3]]:29: note: at a call site, 'const T' might bind with same force as 'const T &'
+
+void compare08(const T t1, const T &t2) {}
+// CHECK-MESSAGES: :[[@LINE-1]]:16: warning: 2 adjacent arguments for 'compare08' of similar type are
+// CHECK-MESSAGES: :[[@LINE-2]]:37: note: last argument in the adjacent range
+// CHECK-MESSAGES: :[[@LINE-3]]:28: note: at a call site, 'const T &' might bind with same force as 'const T'
+
+// In general, rvalue references and copies are hard to mess up at call sites.
+void compare09(T &&t1, T t2) {} // NO-WARN.
+void compare0A(T t1, T &&t2) {} // NO-WARN.
+
+void compare0B(T &&t1, T &&t2) {}
+// CHECK-MESSAGES: :[[@LINE-1]]:16: warning: 2 adjacent arguments for 'compare0B' of similar type ('T &&') are
+// CHECK-MESSAGES: :[[@LINE-2]]:28: note: last argument in the adjacent range
+
+struct U {};
+void compare0C(T t, U u) {}               // NO-WARN.
+void compare0D(const T t, U u) {}         // NO-WARN.
+void compare0E(T t, const U u) {}         // NO-WARN.
+void compare0F(const U &u, T t) {}        // NO-WARN.
+void compare10(T t, const U &u) {}        // NO-WARN.
+void compare11(const T &t, const U &u) {} // NO-WARN.
+void compare12(const T &t, const U u) {}  // NO-WARN.
+void compare13(const U u, const T &t) {}  // NO-WARN.
+void compare14(T &&t, U u) {}             // NO-WARN.
+void compare15(T t, U &&u) {}             // NO-WARN.
+
+void multi_bind(T t, const T t2, const T &t3, T &&t4) {}
+// CHECK-MESSAGES: :[[@LINE-1]]:17: warning: 3 adjacent arguments for 'multi_bind' of similar type are
+// CHECK-MESSAGES: :[[@LINE-2]]:43: note: last argument in the adjacent range
+// CHECK-MESSAGES: :[[@LINE-3]]:22: note: at a call site, 'const T' might bind with same force as 'T'
+// CHECK-MESSAGES: :[[@LINE-4]]:34: note: at a call site, 'const T &' might bind with same force as 'T'
+
+void ptr_quals(int *ip, const int *cip, volatile int *vip, int *const ipc, volatile int *volatile vipv) {}
+// CHECK-MESSAGES: :[[@LINE-1]]:16: warning: 5 adjacent arguments for 'ptr_quals' of similar type are
+// CHECK-MESSAGES: :[[@LINE-2]]:99: note: last argument in the adjacent range
+// CHECK-MESSAGES: :[[@LINE-3]]:25: note: at a call site, 'const int *' might bind with same force as 'int *'
+// CHECK-MESSAGES: :[[@LINE-4]]:41: note: at a call site, 'volatile int *' might bind with same force as 'int *'
+// CHECK-MESSAGES: :[[@LINE-5]]:60: note: at a call site, 'int * const' might bind with same force as 'int *'
+// CHECK-MESSAGES: :[[@LINE-6]]:76: note: at a call site, 'volatile int * volatile' might bind with same force as 'int *'
+
+void value_quals(T t, const T ct, volatile T vt, const volatile T cvt) {}
+// CHECK-MESSAGES: :[[@LINE-1]]:18: warning: 4 adjacent arguments for 'value_quals' of similar type are
+// CHECK-MESSAGES: :[[@LINE-2]]:67: note: last argument in the adjacent range
+// CHECK-MESSAGES: :[[@LINE-3]]:23: note: at a call site, 'const T' might bind with same force as 'T'
+// CHECK-MESSAGES: :[[@LINE-4]]:35: note: at a call site, 'volatile T' might bind with same force as 'T'
+// CHECK-MESSAGES: :[[@LINE-5]]:50: note: at a call site, 'const volatile T' might bind with same force as 'T'
+
+void value_ptr_quals(T *tp, const T *ctp, volatile T *vtp, const volatile T *cvtp, T *const tpc, T *volatile tpv) {}
+// CHECK-MESSAGES: :[[@LINE-1]]:22: warning: 6 adjacent arguments for 'value_ptr_quals' of similar type are
+// CHECK-MESSAGES: :[[@LINE-2]]:110: note: last argument in the adjacent range
+// CHECK-MESSAGES: :[[@LINE-3]]:29: note: at a call site, 'const T *' might bind with same force as 'T *'
+// CHECK-MESSAGES: :[[@LINE-4]]:43: note: at a call site, 'volatile T *' might bind with same force as 'T *'
+// CHECK-MESSAGES: :[[@LINE-5]]:60: note: at a call site, 'const volatile T *' might bind with same force as 'T *'
+// CHECK-MESSAGES: :[[@LINE-6]]:84: note: at a call site, 'T * const' might bind with same force as 'T *'
+// CHECK-MESSAGES: :[[@LINE-7]]:98: note: at a call site, 'T * volatile' might bind with same force as 'T *'
+
+typedef int I1;
+typedef int I2;
+void multi_through_typedef(I1 i1, I2 i2) {}
+// CHECK-MESSAGES: :[[@LINE-1]]:28: warning: 2 adjacent arguments for 'multi_through_typedef' of similar type are
+// CHECK-MESSAGES: :[[@LINE-2]]:38: note: last argument in the adjacent range
+// CHECK-MESSAGES: :[[@LINE-3]]:28: note: after resolving type aliases, type of argument 'i1' is 'int'
+// CHECK-MESSAGES: :[[@LINE-4]]:35: note: after resolving type aliases, type of argument 'i2' is 'int'
+
+void multi_ptr_through_typedef(I1 *p1, I2 *p2) {}
+// CHECK-MESSAGES: :[[@LINE-1]]:32: warning: 2 adjacent arguments for 'multi_ptr_through_typedef' of similar type are
+// CHECK-MESSAGES: :[[@LINE-2]]:44: note: last argument in the adjacent range
+// CHECK-MESSAGES: :[[@LINE-3]]:32: note: after resolving type aliases, type of argument 'p1' is 'int *'
+// CHECK-MESSAGES: :[[@LINE-4]]:40: note: after resolving type aliases, type of argument 'p2' is 'int *'
+
+typedef I2 myInt;
+typedef I2 myOtherInt;
+void typedef_chain(myInt mi, myOtherInt moi) {}
+// CHECK-MESSAGES: :[[@LINE-1]]:20: warning: 2 adjacent arguments for 'typedef_chain' of similar type are
+// CHECK-MESSAGES: :[[@LINE-2]]:41: note: last argument in the adjacent range
+// CHECK-MESSAGES: :[[@LINE-3]]:20: note: after resolving type aliases, type of argument 'mi' is 'int'
+// CHECK-MESSAGES: :[[@LINE-4]]:30: note: after resolving type aliases, type of argument 'moi' is 'int'
+
+void bind_power_and_typedef(int Read, const myOtherInt &Write) {}
+// CHECK-MESSAGES: :[[@LINE-1]]:29: warning: 2 adjacent arguments for 'bind_power_and_typedef' of similar type are
+// CHECK-MESSAGES: :[[@LINE-2]]:57: note: last argument in the adjacent range
+// CHECK-MESSAGES: :[[@LINE-3]]:39: note: after resolving type aliases, type of argument 'Write' is 'const int &'
+// CHECK-MESSAGES: :[[@LINE-4]]:39: note: at a call site, 'const int &' might bind with same force as 'int'
+
+typedef myOtherInt *IP;
+typedef const IP &cipr;
+void bind_power_and_multi_layer_typedef(int *RP, cipr WP) {}
+// CHECK-MESSAGES: :[[@LINE-1]]:41: warning: 2 adjacent arguments for 'bind_power_and_multi_layer_typedef' of similar type are
+// CHECK-MESSAGES: :[[@LINE-2]]:55: note: last argument in the adjacent range
+// CHECK-MESSAGES: :[[@LINE-3]]:50: note: after resolving type aliases, type of argument 'WP' is 'const int * &'
+// CHECK-MESSAGES: :[[@LINE-4]]:50: note: at a call site, 'const int * &' might bind with same force as 'int *'
+
+void entirely_different(int i, long l) {} // NO-WARN.
+
+struct StringRef {
+  char *Data;
+};
+typedef bool Toggle;
+
+void multi_pack(StringRef Path, StringRef Name, int Size, Toggle Read, Toggle Write) {}
+// CHECK-MESSAGES: :[[@LINE-1]]:17: warning: 2 adjacent arguments for 'multi_pack' of similar type ('StringRef') are
+// CHECK-MESSAGES: :[[@LINE-2]]:43: note: last argument in the adjacent range
+// CHECK-MESSAGES: :[[@LINE-3]]:59: warning: 2 adjacent arguments for 'multi_pack' of similar type ('Toggle') are
+// CHECK-MESSAGES: :[[@LINE-4]]:79: note: last argument in the adjacent range
+
+void protochain(int, int, int);
+void protochain(int i, int, int);
+void protochain(int, int j, int);
+void protochain(int i, int j, int k) {}
+// CHECK-MESSAGES: :[[@LINE-1]]:17: warning: 3 adjacent arguments for 'protochain' of similar type ('int') are
+// CHECK-MESSAGES: :[[@LINE-2]]:35: note: last argument in the adjacent range
+
+void protochain2(I1, I2, myInt);
+void protochain2(I1 i, I2, myInt);
+void protochain2(I1, I2 j, int);
+void protochain2(I1 i, I2 j, myOtherInt k) {}
+// CHECK-MESSAGES: :[[@LINE-1]]:18: warning: 3 adjacent arguments for 'protochain2' of similar type are
+// CHECK-MESSAGES: :[[@LINE-2]]:41: note: last argument in the adjacent range
+// CHECK-MESSAGES: :[[@LINE-3]]:18: note: after resolving type aliases, type of argument 'i' is 'int'
+// CHECK-MESSAGES: :[[@LINE-4]]:24: note: after resolving type aliases, type of argument 'j' is 'int'
+// CHECK-MESSAGES: :[[@LINE-5]]:30: note: after resolving type aliases, type of argument 'k' is 'int'
+
+void defined(int a, int b);
+void defined(int i, int j) {}
+// CHECK-MESSAGES: :[[@LINE-1]]:14: warning: 2 adjacent arguments for 'defined' of similar type ('int') are
+// CHECK-MESSAGES: :[[@LINE-2]]:25: note: last argument in the adjacent range
+
+template <typename T>
+void ptr_pair(T *p, T *q) {}
+// CHECK-MESSAGES: :[[@LINE-1]]:15: warning: 2 adjacent arguments for 'ptr_pair' of similar type ('T *') are
+// CHECK-MESSAGES: :[[@LINE-2]]:24: note: last argument in the adjacent range
+
+template <typename T>
+void cptr_pair(const T *p, const T *q) {}
+// CHECK-MESSAGES: :[[@LINE-1]]:16: warning: 2 adjacent arguments for 'cptr_pair' of similar type ('const T *') are
+// CHECK-MESSAGES: :[[@LINE-2]]:37: note: last argument in the adjacent range
+
+void ref_and_typedef(int i, I2 i2) {}
+// CHECK-MESSAGES: :[[@LINE-1]]:22: warning: 2 adjacent arguments for 'ref_and_typedef' of similar type are
+// CHECK-MESSAGES: :[[@LINE-2]]:32: note: last argument in the adjacent range
+// CHECK-MESSAGES: :[[@LINE-3]]:22: note: after resolving type aliases, type of argument 'i' is 'int'
+// CHECK-MESSAGES: :[[@LINE-4]]:29: note: after resolving type aliases, type of argument 'i2' is 'int'
+
+void ref_and_typedef2(I1 i, int i2) {}
+// CHECK-MESSAGES: :[[@LINE-1]]:23: warning: 2 adjacent arguments for 'ref_and_typedef2' of similar type are
+// CHECK-MESSAGES: :[[@LINE-2]]:33: note: last argument in the adjacent range
+// CHECK-MESSAGES: :[[@LINE-3]]:23: note: after resolving type aliases, type of argument 'i' is 'int'
+// CHECK-MESSAGES: :[[@LINE-4]]:29: note: after resolving type aliases, type of argument 'i2' is 'int'
+
+int strcmp(const char *A, const char *B) {}
+// CHECK-MESSAGES: :[[@LINE-1]]:12: warning: 2 adjacent arguments for 'strcmp' of similar type ('const char *') are
+// CHECK-MESSAGES: :[[@LINE-2]]:39: note: last argument in the adjacent range
+
+using str = char *;
+using strliteral = const char *;
+int str_compare(strliteral a, strliteral b) {}
+// CHECK-MESSAGES: :[[@LINE-1]]:17: warning: 2 adjacent arguments for 'str_compare' of similar type ('strliteral') are
+// CHECK-MESSAGES: :[[@LINE-2]]:42: note: last argument in the adjacent range
+
+void append(str s, char *s2) {}
+// CHECK-MESSAGES: :[[@LINE-1]]:13: warning: 2 adjacent arguments for 'append' of similar type are
+// CHECK-MESSAGES: :[[@LINE-2]]:26: note: last argument in the adjacent range
+// CHECK-MESSAGES: :[[@LINE-3]]:13: note: after resolving type aliases, type of argument 's' is 'char *'
+// CHECK-MESSAGES: :[[@LINE-4]]:20: note: after resolving type aliases, type of argument 's2' is 'char *'
+
+void append_lit(str s, strliteral s2) {}
+// CHECK-MESSAGES: :[[@LINE-1]]:17: warning: 2 adjacent arguments for 'append_lit' of similar type are
+// CHECK-MESSAGES: :[[@LINE-2]]:35: note: last argument in the adjacent range
+// CHECK-MESSAGES: :[[@LINE-3]]:17: note: after resolving type aliases, type of argument 's' is 'char *'
+// CHECK-MESSAGES: :[[@LINE-4]]:24: note: after resolving type aliases, type of argument 's2' is 'const char *'
+
+void swap(str *a, char **b) {}
+// CHECK-MESSAGES: :[[@LINE-1]]:11: warning: 2 adjacent arguments for 'swap' of similar type are
+// CHECK-MESSAGES: :[[@LINE-2]]:26: note: last argument in the adjacent range
+// CHECK-MESSAGES: :[[@LINE-3]]:11: note: after resolving type aliases, type of argument 'a' is 'char * *'
+// CHECK-MESSAGES: :[[@LINE-4]]:19: note: after resolving type aliases, type of argument 'b' is 'char * *'
+
+template <typename I> // 'I' as template name not on ignore list.
+void shortthing(I a, I b) {}
+// CHECK-MESSAGES: :[[@LINE-1]]:17: warning: 2 adjacent arguments for 'shortthing' of similar type ('I') are
+// CHECK-MESSAGES: :[[@LINE-2]]:24: note: last argument in the adjacent range
+
+// FIXME: Suggest Ranges library alternative?
+// NO-WARN: Ignore common type packs, like iterators.
+template <typename InputIt>
+void something(InputIt a, InputIt b) {}
+
+// NO-WARN: Ignore common names, like for iterators.
+template <typename InputIt, typename E>
+void find(InputIt first, InputIt last, const E &Element) {}
+
+template <typename T>
+struct vector {
+  T *Data;
+  unsigned N;
+  T operator[](unsigned Idx) { return *(Data + N); }
+
+  typedef T *iterator;
+  typedef T element;
+  typedef const T &element_cref;
+};
+
+// NO-WARN: Argument has "iterator" as type.
+template <typename T>
+typename vector<T>::iterator find2(
+    typename vector<T>::iterator from,
+    typename vector<T>::iterator to,
+    const T &E) {}
+
+template <typename K, typename V>
+struct map {
+  K *Keys;
+  V *Values;
+
+  struct map_iterator {
+    K *first;
+    V *second;
+  };
+  typedef map_iterator iterator;
+};
+
+// NO-WARN: Argument has "iterator" as type.
+template <typename K, typename V>
+typename map<K, V>::iterator find_m(
+    typename map<K, V>::iterator left,  // Purposefully named as such so param
+    typename map<K, V>::iterator right, // name doesn't match, only type name.
+    const K &k, const V &v) {}
+
+// NO-WARN: Arguments are dependent types, it could be that for a certain
+// 'T' they refer to (essentially) the same thing, and for other T, they don't.
+// (Think of std::vector<bool>, perhaps?)
+// It would be very expensive and tangy logic to deduce that in this small
+// test example, one argument is 'T' and the 'const T &'.
+template <typename T>
+vector<typename vector<T>::iterator> findOccurrenceCases(
+    typename vector<T>::element E, typename vector<T>::element_cref ER) {}
+
+// FIXME: This case should be diagnosed (are `DependentNameTypes` equatable?)
+template <typename T>
+vector<vector<T>> equalRangesBetween(
+    const typename vector<T>::element &F,
+    typename vector<T>::element L) {}
+
+// NO-WARN: Totally different types.
+struct Decl;
+struct Expr;
+struct Stmt;
+void HandleASTNodes(Decl *D, Expr *E, Stmt *S) {}
+
+// NO-WARN: Different template specialisations of the same template.
+void concatenate_many_strings(StringRef Out,
+                              vector<StringRef> InRef,
+                              vector<str> InStr,
+                              vector<strliteral> InLiterals) {}
+
+// NO-WARN: Different template arguments.
+template <typename X, typename Y, typename Comp>
+int compare(const X &x, const Y &y, Comp C) { return C(x, y); }
+
+// WARN: Explicit specialisation's definition with similar arguments.
+template <typename Comp>
+int compare(int x, int y, Comp C) { return C(x, y); }
+// CHECK-MESSAGES: :[[@LINE-1]]:13: warning: 2 adjacent arguments for 'compare' of similar type ('int') are
+// CHECK-MESSAGES: :[[@LINE-2]]:24: note: last argument in the adjacent range
+
+void compare_test() {
+  // NO-WARN: Function call, not defintion.
+  compare("A", "B", &strcmp);
+}
+
+int definition_inside_another(vector<int> V) {
+  struct Sum {
+    Sum(int _i, int _j) : i(_i), j(_j) {}
+    // CHECK-MESSAGES: :[[@LINE-1]]:9: warning: 2 adjacent arguments for 'Sum' of similar type ('int') are
+    // CHECK-MESSAGES: :[[@LINE-2]]:21: note: last argument in the adjacent range
+
+    int operator()() const { return i + j; }
+
+    int i, j;
+  };
+  Sum S{V[0], V[1]};
+
+  return S();
+}
+
+void len3(const StringRef Drive, const StringRef Dirs, const StringRef FileName) {}
+// CHECK-MESSAGES: :[[@LINE-1]]:11: warning: 3 adjacent arguments for 'len3' of similar type ('const StringRef') are
+// CHECK-MESSAGES: :[[@LINE-2]]:72: note: last argument in the adjacent range
+
+void len4(const StringRef Drive, const StringRef Dirs, const StringRef FileName, const StringRef Extension) {}
+// CHECK-MESSAGES: :[[@LINE-1]]:11: warning: 4 adjacent arguments for 'len4' of similar type ('const StringRef') are
+// CHECK-MESSAGES: :[[@LINE-2]]:98: note: last argument in the adjacent range
+
+using MainT = int(int argc, strliteral argv, strliteral envp);
+int call_two_programs(MainT one, MainT two) {}
+// CHECK-MESSAGES: :[[@LINE-1]]:23: warning: 2 adjacent arguments for 'call_two_programs' of similar type ('MainT *') are
+// CHECK-MESSAGES: :[[@LINE-2]]:40: note: last argument in the adjacent range
+
+using MainT2 = int(int argc, strliteral argv, strliteral envp);
+int call_two_programs2(MainT p1, MainT2 p2) {}
+// CHECK-MESSAGES: :[[@LINE-1]]:24: warning: 2 adjacent arguments for 'call_two_programs2' of similar type are
+// CHECK-MESSAGES: :[[@LINE-2]]:41: note: last argument in the adjacent range
+// CHECK-MESSAGES: :[[@LINE-3]]:24: note: after resolving type aliases, type of argument 'p1' is 'int (int, const char *, const char *)'
+// CHECK-MESSAGES: :[[@LINE-4]]:34: note: after resolving type aliases, type of argument 'p2' is 'int (int, const char *, const char *)'
+
+typedef struct {
+  int i;
+} Integer;
+bool int_equals(Integer i1, Integer i2) { return i1.i == i2.i; }
+// CHECK-MESSAGES: :[[@LINE-1]]:17: warning: 2 adjacent arguments for 'int_equals' of similar type ('Integer') are
+// CHECK-MESSAGES: :[[@LINE-2]]:37: note: last argument in the adjacent range
+
+typedef enum {
+  Exclamation,
+  Question,
+  Dot
+} Token;
+bool tok_equals(Token t1, Token t2) {}
+// CHECK-MESSAGES: :[[@LINE-1]]:17: warning: 2 adjacent arguments for 'tok_equals' of similar type ('Token') are
+// CHECK-MESSAGES: :[[@LINE-2]]:33: note: last argument in the adjacent range
+
+enum E : char {
+  A = 'A',
+  B = 'B',
+  C = 'C'
+};
+
+// QUESTION: Should this warn?
+// Conversion checks in general may not belong to this check in particular.
+bool isSameLetter(E e, const char &c) {}
+
+template <typename T>
+struct doubly_linked_list_node {
+  T val;
+  doubly_linked_list_node<T> *prev;
+  doubly_linked_list_node<T> *next;
+};
+
+// FIXME: doubly_linked_list_node<T> is a dependent type, and thus doesn't warn.
+template <typename T>
+doubly_linked_list_node<T> *list_insert(const T &element,
+                                        const doubly_linked_list_node<T> *prev,
+                                        const doubly_linked_list_node<T> *next) {}
+
+template <class... Args>
+void fwd(Args &&... args, Args &&... args2) {}
+// CHECK-MESSAGES: :[[@LINE-1]]:10: warning: 2 adjacent arguments for 'fwd' of similar type ('Args &&...') are
+// CHECK-MESSAGES: :[[@LINE-2]]:38: note: last argument in the adjacent range
+
+// Make sure the logic for the check doesn't crash...
+
+// Matched case from '/usr/include/c++/7/bits/gthr-default.h'.
+typedef unsigned long int gthread_t;
+static inline int gthread_create(gthread_t *threadid,
+                                 void *(*func)(void *),
+                                 void *args) {}
+
+// A modification of the above function that should warn.
+int gthread_create_random(gthread_t *threadid,
+                          void *(*func_1)(void *),
+                          void *(*func_2)(void *),
+                          void *args) {}
+// CHECK-MESSAGES: :[[@LINE-3]]:27: warning: 2 adjacent arguments for 'gthread_create_random' of similar type ('void *(*)(void *)') are
+// CHECK-MESSAGES: :[[@LINE-3]]:35: note: last argument in the adjacent range
Index: clang-tools-extra/test/clang-tidy/checkers/cppcoreguidelines-avoid-adjacent-arguments-of-same-type-default.cpp
===================================================================
--- /dev/null
+++ clang-tools-extra/test/clang-tidy/checkers/cppcoreguidelines-avoid-adjacent-arguments-of-same-type-default.cpp
@@ -0,0 +1,193 @@
+// RUN: %check_clang_tidy %s \
+// RUN:   cppcoreguidelines-avoid-adjacent-arguments-of-same-type %t \
+// RUN:   -config='{CheckOptions: [ \
+// RUN:     {key: cppcoreguidelines-avoid-adjacent-arguments-of-same-type.MinimumLength, value: 3}, \
+// RUN:     {key: cppcoreguidelines-avoid-adjacent-arguments-of-same-type.CVRMixPossible, value: 0} \
+// RUN:   ]}' --
+
+void library(void *vp, void *vp2, void *vp3, int n, int m);
+// NO-WARN: The user has no chance to change only declared (usually library)
+// functions, so no diagnostic is made.
+
+// NO-WARN for any of these: only 2 arguments present (default is 3).
+
+struct T {};
+
+void create(T **out_t) {}
+void copy(T *p, T *q, int n) {}
+void mov(T *dst, const T *src) {}
+
+void compare01(T t1, T t2) {}
+void compare02(const T t1, T t2) {}
+void compare03(T t1, const T t2) {}
+void compare04(const T &t1, T t2) {}
+void compare05(T t1, const T &t2) {}
+void compare06(const T &t1, const T &t2) {}
+void compare07(const T &t1, const T t2) {}
+void compare08(const T t1, const T &t2) {}
+
+void compare09(T &&t1, T t2) {}
+void compare0A(T t1, T &&t2) {}
+void compare0B(T &&t1, T &&t2) {}
+
+struct U {};
+void compare0C(T t, U u) {}
+void compare0D(const T t, U u) {}
+void compare0E(T t, const U u) {}
+void compare0F(const U &u, T t) {}
+void compare10(T t, const U &u) {}
+void compare11(const T &t, const U &u) {}
+void compare12(const T &t, const U u) {}
+void compare13(const U u, const T &t) {}
+void compare14(T &&t, U u) {}
+void compare15(T t, U &&u) {}
+
+typedef int I1;
+typedef int I2;
+void multi_through_typedef(I1 i1, I2 i2) {}
+
+void multi_ptr_through_typedef(I1 *p1, I2 *p2) {}
+
+typedef I2 myInt;
+typedef I2 myOtherInt;
+void typedef_chain(myInt mi, myOtherInt moi) {}
+
+void bind_power_and_typedef(int Read, const myOtherInt &Write) {}
+
+typedef myOtherInt *IP;
+typedef const IP &cipr;
+void bind_power_and_multi_layer_typedef(int *RP, cipr WP) {}
+
+void entirely_different(int i, long l) {}
+
+struct StringRef {
+  char *Data;
+};
+typedef bool Toggle;
+
+void multi_pack(StringRef Path, StringRef Name, int Size, Toggle Read, Toggle Write) {}
+
+void defined(int a, int b);
+void defined(int i, int j) {}
+
+template <typename T>
+void ptr_pair(T *p, T *q) {}
+
+template <typename T>
+void cptr_pair(const T *p, const T *q) {}
+
+void ref_and_typedef(int i, I2 i2) {}
+void ref_and_typedef2(I1 i, int i2) {}
+
+int strcmp(const char *A, const char *B) {}
+
+using str = char *;
+using strlit = const str;
+int str_compare(strlit a, strlit b) {}
+
+void append(str s, char *s2) {}
+
+void append_lit(str s, strlit s2) {}
+
+void swap(str *a, char **b) {}
+
+// END of NO-WARN.
+
+// NO-WARN: CVR-qualification-mixup is turned off in this test file.
+
+void merge(T *dst, const T *src1, const T *src2) {}
+
+void multi_bind(T t, const T t2, const T &t3, T &&t4) {}
+
+void ptr_quals(int *ip, const int *cip, volatile int *vip, int *const ipc, volatile int *volatile vipv) {}
+
+void value_quals(T t, const T ct, volatile T vt, const volatile T cvt) {}
+
+void value_ptr_quals(T *tp, const T *ctp, volatile T *vtp, const volatile T *cvtp, T *const tpc, T *volatile tpv) {}
+
+// NO-WARN: const T* and T* should not mix, even if coming through a typedef.
+// (min and max could be mixed up, but length requirement in this test is 3.)
+typedef int Numeral;
+void set_lerped(const Numeral *const min,
+                const Numeral *const max,
+                Numeral *const value) {}
+
+// END of NO-WARN.
+
+void protochain(int, int, int);
+void protochain(int i, int, int);
+void protochain(int, int j, int);
+void protochain(int i, int j, int k) {}
+// CHECK-MESSAGES: :[[@LINE-1]]:17: warning: 3 adjacent arguments for 'protochain' of similar type ('int') are
+// CHECK-MESSAGES: :[[@LINE-2]]:35: note: last argument in the adjacent range
+
+void protochain2(I1, I2, myInt);
+void protochain2(I1 i, I2, myInt);
+void protochain2(I1, I2 j, int);
+void protochain2(I1 i, I2 j, myOtherInt k) {}
+// CHECK-MESSAGES: :[[@LINE-1]]:18: warning: 3 adjacent arguments for 'protochain2' of similar type are
+// CHECK-MESSAGES: :[[@LINE-2]]:41: note: last argument in the adjacent range
+// CHECK-MESSAGES: :[[@LINE-3]]:18: note: after resolving type aliases, type of argument 'i' is 'int'
+// CHECK-MESSAGES: :[[@LINE-4]]:24: note: after resolving type aliases, type of argument 'j' is 'int'
+// CHECK-MESSAGES: :[[@LINE-5]]:30: note: after resolving type aliases, type of argument 'k' is 'int'
+
+void len3(const StringRef Drive, const StringRef Dirs, const StringRef FileName) {}
+// CHECK-MESSAGES: :[[@LINE-1]]:11: warning: 3 adjacent arguments for 'len3' of similar type ('const StringRef') are
+// CHECK-MESSAGES: :[[@LINE-2]]:72: note: last argument in the adjacent range
+
+void len4(const StringRef Drive, const StringRef Dirs, const StringRef FileName, const StringRef Extension) {}
+// CHECK-MESSAGES: :[[@LINE-1]]:11: warning: 4 adjacent arguments for 'len4' of similar type ('const StringRef') are
+// CHECK-MESSAGES: :[[@LINE-2]]:98: note: last argument in the adjacent range
+
+void multi_packs(StringRef Path, StringRef Name, StringRef Occupation,
+                 int Size, int Width,
+                 Toggle Read, Toggle Write, Toggle Execute) {}
+// CHECK-MESSAGES: :[[@LINE-3]]:18: warning: 3 adjacent arguments for 'multi_packs' of similar type ('StringRef') are
+// CHECK-MESSAGES: :[[@LINE-4]]:60: note: last argument in the adjacent range
+// CHECK-MESSAGES: :[[@LINE-3]]:18: warning: 3 adjacent arguments for 'multi_packs' of similar type ('Toggle') are
+// CHECK-MESSAGES: :[[@LINE-4]]:52: note: last argument in the adjacent range
+
+// CVRMixupPossible only concerns 'T' and 'c/v/r T', or pointers thereof, not
+// references.
+void lifetime_extension(T t, const T &tr1, const T &t2) {}
+// CHECK-MESSAGES: :[[@LINE-1]]:25: warning: 3 adjacent arguments for 'lifetime_extension' of similar type are
+// CHECK-MESSAGES: :[[@LINE-2]]:53: note: last argument in the adjacent range
+// CHECK-MESSAGES: :[[@LINE-3]]:30: note: at a call site, 'const T &' might bind with same force as 'T'
+// CHECK-MESSAGES: :[[@LINE-4]]:44: note: at a call site, 'const T &' might bind with same force as 'T'
+
+enum OldSchoolTermColour {
+  Black,
+  Blue,
+  Green,
+  Cyan,
+  Red,
+  Purple,
+  BrownOrange,
+  LightGreyWhite,
+  Gray,
+  LightBlue,
+  LightGreen,
+  LightCyan,
+  LightRed,
+  LightPurple,
+  YellowLightOrange,
+  WhiteLightWhite
+};
+
+void setColouredOutput(OldSchoolTermColour Background,
+                       OldSchoolTermColour Foreground,
+                       OldSchoolTermColour Border) {}
+// CHECK-MESSAGES: :[[@LINE-3]]:24: warning: 3 adjacent arguments for 'setColouredOutput' of similar type ('OldSchoolTermColour') are
+// CHECK-MESSAGES: :[[@LINE-2]]:44: note: last argument in the adjacent range
+
+typedef OldSchoolTermColour BackColour;
+typedef OldSchoolTermColour ForeColour;
+typedef OldSchoolTermColour BorderColour;
+void setColouredOutput2(BackColour Background,
+                        ForeColour Foreground,
+                        BorderColour Border) {}
+// CHECK-MESSAGES: :[[@LINE-3]]:25: warning: 3 adjacent arguments for 'setColouredOutput2' of similar type are
+// CHECK-MESSAGES: :[[@LINE-2]]:38: note: last argument in the adjacent range
+// CHECK-MESSAGES: :[[@LINE-5]]:25: note: after resolving type aliases, type of argument 'Background' is 'OldSchoolTermColour'
+// CHECK-MESSAGES: :[[@LINE-5]]:25: note: after resolving type aliases, type of argument 'Foreground' is 'OldSchoolTermColour'
+// CHECK-MESSAGES: :[[@LINE-5]]:25: note: after resolving type aliases, type of argument 'Border' is 'OldSchoolTermColour'
Index: clang-tools-extra/test/clang-tidy/checkers/cppcoreguidelines-avoid-adjacent-arguments-of-same-type-cvr-on.cpp
===================================================================
--- /dev/null
+++ clang-tools-extra/test/clang-tidy/checkers/cppcoreguidelines-avoid-adjacent-arguments-of-same-type-cvr-on.cpp
@@ -0,0 +1,56 @@
+// RUN: %check_clang_tidy %s \
+// RUN:   cppcoreguidelines-avoid-adjacent-arguments-of-same-type %t \
+// RUN:   -config='{CheckOptions: [ \
+// RUN:     {key: cppcoreguidelines-avoid-adjacent-arguments-of-same-type.MinimumLength, value: 3}, \
+// RUN:     {key: cppcoreguidelines-avoid-adjacent-arguments-of-same-type.CVRMixPossible, value: 1} \
+// RUN:   ]}' --
+
+struct T {};
+
+void mov(T *dst, const T *src) {} // NO-WARN: Length requirement is 3.
+
+void merge(T *dst, const T *src1, const T *src2) {}
+// CHECK-MESSAGES: :[[@LINE-1]]:12: warning: 3 adjacent arguments for 'merge' of similar type are
+// CHECK-MESSAGES: :[[@LINE-2]]:44: note: last argument in the adjacent range
+// CHECK-MESSAGES: :[[@LINE-3]]:20: note: at a call site, 'const T *' might bind with same force as 'T *'
+// CHECK-MESSAGES: :[[@LINE-4]]:35: note: at a call site, 'const T *' might bind with same force as 'T *'
+
+void multi_bind(T t, const T t2, const T &t3, T &&t4) {}
+// CHECK-MESSAGES: :[[@LINE-1]]:17: warning: 3 adjacent arguments for 'multi_bind' of similar type are
+// CHECK-MESSAGES: :[[@LINE-2]]:43: note: last argument in the adjacent range
+// CHECK-MESSAGES: :[[@LINE-3]]:22: note: at a call site, 'const T' might bind with same force as 'T'
+// CHECK-MESSAGES: :[[@LINE-4]]:34: note: at a call site, 'const T &' might bind with same force as 'T'
+
+void ptr_quals(int *ip, const int *cip, volatile int *vip, int *const ipc, volatile int *volatile vipv) {}
+// CHECK-MESSAGES: :[[@LINE-1]]:16: warning: 5 adjacent arguments for 'ptr_quals' of similar type are
+// CHECK-MESSAGES: :[[@LINE-2]]:99: note: last argument in the adjacent range
+// CHECK-MESSAGES: :[[@LINE-3]]:25: note: at a call site, 'const int *' might bind with same force as 'int *'
+// CHECK-MESSAGES: :[[@LINE-4]]:41: note: at a call site, 'volatile int *' might bind with same force as 'int *'
+// CHECK-MESSAGES: :[[@LINE-5]]:60: note: at a call site, 'int * const' might bind with same force as 'int *'
+// CHECK-MESSAGES: :[[@LINE-6]]:76: note: at a call site, 'volatile int * volatile' might bind with same force as 'int *'
+
+void value_quals(T t, const T ct, volatile T vt, const volatile T cvt) {}
+// CHECK-MESSAGES: :[[@LINE-1]]:18: warning: 4 adjacent arguments for 'value_quals' of similar type are
+// CHECK-MESSAGES: :[[@LINE-2]]:67: note: last argument in the adjacent range
+// CHECK-MESSAGES: :[[@LINE-3]]:23: note: at a call site, 'const T' might bind with same force as 'T'
+// CHECK-MESSAGES: :[[@LINE-4]]:35: note: at a call site, 'volatile T' might bind with same force as 'T'
+// CHECK-MESSAGES: :[[@LINE-5]]:50: note: at a call site, 'const volatile T' might bind with same force as 'T'
+
+void value_ptr_quals(T *tp, const T *ctp, volatile T *vtp, const volatile T *cvtp, T *const tpc, T *volatile tpv) {}
+// CHECK-MESSAGES: :[[@LINE-1]]:22: warning: 6 adjacent arguments for 'value_ptr_quals' of similar type are
+// CHECK-MESSAGES: :[[@LINE-2]]:110: note: last argument in the adjacent range
+// CHECK-MESSAGES: :[[@LINE-3]]:29: note: at a call site, 'const T *' might bind with same force as 'T *'
+// CHECK-MESSAGES: :[[@LINE-4]]:43: note: at a call site, 'volatile T *' might bind with same force as 'T *'
+// CHECK-MESSAGES: :[[@LINE-5]]:60: note: at a call site, 'const volatile T *' might bind with same force as 'T *'
+// CHECK-MESSAGES: :[[@LINE-6]]:84: note: at a call site, 'T * const' might bind with same force as 'T *'
+// CHECK-MESSAGES: :[[@LINE-7]]:98: note: at a call site, 'T * volatile' might bind with same force as 'T *'
+
+typedef int Numeral;
+void set_lerped(const Numeral *const min,
+                const Numeral *const max,
+                Numeral *const value) {}
+// CHECK-MESSAGES: :[[@LINE-3]]:17: warning: 3 adjacent arguments for 'set_lerped' of similar type are
+// CHECK-MESSAGES: :[[@LINE-2]]:32: note: last argument in the adjacent range
+// CHECK-MESSAGES: :[[@LINE-5]]:17: note: after resolving type aliases, type of argument 'min' is 'const int * const'
+// CHECK-MESSAGES: :[[@LINE-4]]:17: note: after resolving type aliases, type of argument 'value' is 'int * const'
+// CHECK-MESSAGES: :[[@LINE-5]]:17: note: at a call site, 'int * const' might bind with same force as 'const Numeral *const'
Index: clang-tools-extra/docs/clang-tidy/checks/list.rst
===================================================================
--- clang-tools-extra/docs/clang-tidy/checks/list.rst
+++ clang-tools-extra/docs/clang-tidy/checks/list.rst
@@ -190,6 +190,7 @@
    clang-analyzer-valist.CopyToSelf
    clang-analyzer-valist.Uninitialized
    clang-analyzer-valist.Unterminated
+   cppcoreguidelines-avoid-adjacent-arguments-of-same-type
    cppcoreguidelines-avoid-c-arrays (redirects to modernize-avoid-c-arrays) <cppcoreguidelines-avoid-c-arrays>
    cppcoreguidelines-avoid-goto
    cppcoreguidelines-avoid-magic-numbers (redirects to readability-magic-numbers) <cppcoreguidelines-avoid-magic-numbers>
Index: clang-tools-extra/docs/clang-tidy/checks/cppcoreguidelines-avoid-adjacent-arguments-of-same-type.rst
===================================================================
--- /dev/null
+++ clang-tools-extra/docs/clang-tidy/checks/cppcoreguidelines-avoid-adjacent-arguments-of-same-type.rst
@@ -0,0 +1,125 @@
+.. title:: clang-tidy - cppcoreguidelines-avoid-adjacent-arguments-of-same-type
+
+cppcoreguidelines-avoid-adjacent-arguments-of-same-type
+=======================================================
+
+Finds function definitions where arguments of the same type follow each other
+directly, making call sites prone to calling the function with swapped or badly
+ordered arguments.
+
+The rule is part of the "Interfaces" profile of the C++ Core Guidelines, see
+`I.24 <https://github.com/isocpp/CppCoreGuidelines/blob/master/CppCoreGuidelines.md#i24-avoid-adjacent-unrelated-parameters-of-the-same-type>`_.
+
+.. code-block:: c++
+
+  void draw_point(int X, int Y) {}
+  FILE *open_path(const char *Dir, const char *Name, Flags Mode) {}
+
+A potential call like ``draw(-2, 5)`` or
+``open_path("a.txt", "/tmp", Read | Write)`` is perfectly legal from the
+language's perspective, but might not be what the developer of the function
+intended.
+
+The C++ Core Guidelines recommend using more elaborate types for arguments,
+such as
+
+.. code-block:: c++
+
+    struct Coords2D { int X; int Y; };
+    void draw_point(const Coords2D Point) {}
+
+    FILE *open_path(const Path &Dir, const Filename &Name, Flags Mode) {}
+
+Due to the elaborate refactoring and API-breaking requirements posed by fixing
+the issues diagnosed by this check, **no automatic fix-its** are proposed.
+
+Currently, functions that take pairs of iterators (where the suggested option
+would be moving to, e.g., the
+`Ranges <http://en.cppreference.com/w/cpp/ranges>`_ library) are hard-coded to
+not produce a diagnostic.
+
+ - The following *argument names* and their Uppercase-initial variants are
+   ignored:
+   ``iterator``, ``begin``, ``end``, ``first``, ``last``.
+ - The following *type names* and their lowercase-initial variants (for types
+   of any argument found) are ignored:
+   ``It``, ``Iterator``, ``InputIt``, ``ForwardIt``, ``BidirIt``,
+   ``const_iterator``, ``ConstIterator``
+
+Options
+-------
+
+.. option:: MinimumLength
+
+    The minimum length of the adjacent argument sequence required before a
+    diagnostic is emitted.
+    Defaults to `3` for sake of user-friendliness.
+    Should be any integer number that's at least 2.
+    If `0` or `1` is given, the check will run as if `3` was specified.
+    To get the strict requirement present in C++ Core Guidelines, set to `2`.
+
+.. option:: CVRMixPossible
+
+    Whether to consider arguments of type ``T`` and the qualified
+    ``const T``/``volatile T`` counterpart forming a common "adjacent argument"
+    sequence.
+    If `0` (default value), the check assumes such arguments cannot be mixed
+    up at a potential call site.
+    A non-zero value turns this assumption **off**.
+    A non-zero value is generally expected to produce a broader set of results.
+
+    The following example will not produce a diagnostic unless
+    *CVRMixPossible* is set to a non-zero value.
+
+    .. code-block:: c++
+
+        struct T {};
+        void f(T *tp, const T *ctp) {}
+
+Limitations
+-----------
+
+This check does not investigate functions that were instantiated from function
+templates, only the primary template definitions and explicit specialisations
+are checked.
+
+Furthermore, this check is not able to find if some arguments of
+template-dependent types might end up referring to the same type in the end,
+creating an adjacent range that could be mixed up.
+
+The following example will not be warned about.
+
+.. code-block:: c++
+
+    template <typename T>
+    struct list_node {
+        T value;
+        list_node<T> *prev, *next;
+    };
+
+    template <typename T>
+    list_node<T> *create(const T& value,
+                         const list_node<T> *prev,
+                         const list_node<T> *prev) { /* ... */ }
+
+The following example will not be warned about, despite it should be, as both
+arguments to the function in the example are of type ``const T &``.
+
+.. code-block:: c++
+
+    template <typename T>
+    struct vector {
+        typename       T         element_type;
+        typename const T   const_element_type;
+        typename       T &       reference_type;
+        typename const T & const_reference_type;
+    };
+
+    // Finds the longest occurrence's length between elements "RightEnd"
+    // and "LeftBegin". For example, in a vector of
+    //     <1, 2, 3, 4, 2, 8, 8, 8, 8, 4>
+    // findLongestOccurrenceLengthBetween(2, 4) should return 4 (the 4 8s).
+    template <typename T>
+    unsigned int findLongestOccurrenceLengthBetween(
+              typename vector<T>::const_reference_type   RightEnd,
+        const typename vector<T>::element_type         & LeftBegin) { /* ... */ }
Index: clang-tools-extra/docs/ReleaseNotes.rst
===================================================================
--- clang-tools-extra/docs/ReleaseNotes.rst
+++ clang-tools-extra/docs/ReleaseNotes.rst
@@ -88,6 +88,13 @@
   Without the null terminator it can result in undefined behaviour when the
   string is read.
 
+- New :doc:`cppcoreguidelines-avoid-adjacent-arguments-of-same-type
+  <clang-tidy/checks/cppcoreguidelines-avoid-adjacent-arguments-of-same-type>` check.
+
+  Finds function definitions where arguments of the same type follow each other
+  directly, making call sites prone to calling the function with swapped or badly
+  ordered arguments.
+
 - New :doc:`cppcoreguidelines-init-variables
   <clang-tidy/checks/cppcoreguidelines-init-variables>` check.
 
Index: clang-tools-extra/clang-tidy/cppcoreguidelines/CppCoreGuidelinesTidyModule.cpp
===================================================================
--- clang-tools-extra/clang-tidy/cppcoreguidelines/CppCoreGuidelinesTidyModule.cpp
+++ clang-tools-extra/clang-tidy/cppcoreguidelines/CppCoreGuidelinesTidyModule.cpp
@@ -14,6 +14,7 @@
 #include "../modernize/AvoidCArraysCheck.h"
 #include "../modernize/UseOverrideCheck.h"
 #include "../readability/MagicNumbersCheck.h"
+#include "AdjacentArgumentsOfSameTypeCheck.h"
 #include "AvoidGotoCheck.h"
 #include "InitVariablesCheck.h"
 #include "InterfacesGlobalInitCheck.h"
@@ -42,6 +43,8 @@
 class CppCoreGuidelinesModule : public ClangTidyModule {
 public:
   void addCheckFactories(ClangTidyCheckFactories &CheckFactories) override {
+    CheckFactories.registerCheck<AdjacentArgumentsOfSameTypeCheck>(
+        "cppcoreguidelines-avoid-adjacent-arguments-of-same-type");
     CheckFactories.registerCheck<modernize::AvoidCArraysCheck>(
         "cppcoreguidelines-avoid-c-arrays");
     CheckFactories.registerCheck<AvoidGotoCheck>(
Index: clang-tools-extra/clang-tidy/cppcoreguidelines/CMakeLists.txt
===================================================================
--- clang-tools-extra/clang-tidy/cppcoreguidelines/CMakeLists.txt
+++ clang-tools-extra/clang-tidy/cppcoreguidelines/CMakeLists.txt
@@ -1,6 +1,7 @@
 set(LLVM_LINK_COMPONENTS support)
 
 add_clang_library(clangTidyCppCoreGuidelinesModule
+  AdjacentArgumentsOfSameTypeCheck.cpp
   AvoidGotoCheck.cpp
   CppCoreGuidelinesTidyModule.cpp
   InitVariablesCheck.cpp
Index: clang-tools-extra/clang-tidy/cppcoreguidelines/AdjacentArgumentsOfSameTypeCheck.h
===================================================================
--- /dev/null
+++ clang-tools-extra/clang-tidy/cppcoreguidelines/AdjacentArgumentsOfSameTypeCheck.h
@@ -0,0 +1,45 @@
+//===--- AvoidAdjacentArgumentsOfSameTypeCheck.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_CPPCOREGUIDELINES_ADJACENTARGUMENTSOFSAMETYPECHECK_H
+#define LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_CPPCOREGUIDELINES_ADJACENTARGUMENTSOFSAMETYPECHECK_H
+
+#include "../ClangTidyCheck.h"
+
+namespace clang {
+namespace tidy {
+namespace cppcoreguidelines {
+
+/// Finds function definitions where arguments of the same type follow each
+/// other directly, making call sites prone to calling the function with swapped
+/// or badly ordered arguments.
+///
+/// For the user-facing documentation see:
+/// http://clang.llvm.org/extra/clang-tidy/checks/cppcoreguidelines-avoid-adjacent-arguments-of-same-type.html
+class AdjacentArgumentsOfSameTypeCheck : public ClangTidyCheck {
+public:
+  AdjacentArgumentsOfSameTypeCheck(StringRef Name, ClangTidyContext *Context);
+  void registerMatchers(ast_matchers::MatchFinder *Finder) override;
+  void check(const ast_matchers::MatchFinder::MatchResult &Result) override;
+  void storeOptions(ClangTidyOptions::OptionMap &Opts) override;
+
+private:
+  /// The minimum length of an adjacent range required to have to produce
+  /// a diagnostic.
+  const unsigned MinimumLength;
+
+  /// Whether to consider 'T' and 'const T'/'volatile T'/etc. arguments to be
+  /// possible mixup.
+  const bool CVRMixPossible;
+};
+
+} // namespace cppcoreguidelines
+} // namespace tidy
+} // namespace clang
+
+#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_CPPCOREGUIDELINES_ADJACENTARGUMENTSOFSAMETYPECHECK_H
Index: clang-tools-extra/clang-tidy/cppcoreguidelines/AdjacentArgumentsOfSameTypeCheck.cpp
===================================================================
--- /dev/null
+++ clang-tools-extra/clang-tidy/cppcoreguidelines/AdjacentArgumentsOfSameTypeCheck.cpp
@@ -0,0 +1,612 @@
+//===------- AvoidAdjacentArgumentsOfSameTypeCheck.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 "AdjacentArgumentsOfSameTypeCheck.h"
+#include "clang/AST/ASTContext.h"
+#include "clang/ASTMatchers/ASTMatchFinder.h"
+#include "llvm/ADT/BitmaskEnum.h"
+#include "llvm/ADT/SmallVector.h"
+
+#include <string>
+#include <utility>
+
+// clang-format off
+// FIXME: Remove debug things.
+#define  DEBUG_TYPE "AdjacentArguments"
+#include "llvm/Support/Debug.h"
+// clang-format on
+
+using namespace clang::ast_matchers;
+
+namespace clang {
+namespace tidy {
+namespace cppcoreguidelines {
+
+namespace {
+
+/// Annotates how a mixup might happen. This is a flag enumeration.
+enum MixupTag : unsigned char {
+  MIXUP_Invalid = 0, //< Sentinel 0 bit pattern value for masking. DO NOT USE!
+  MIXUP_None = 1,    //< Mixup is not possible.
+  MIXUP_Trivial = 2, //< No extra information needed.
+  MIXUP_Typedef = 4, //< Argument of a typedef which resolves to an
+                     //< effective desugared type same as the other arg.
+  MIXUP_RefBind = 8, //< Argument mixes with another due to reference binding.
+  MIXUP_CVR = 16,    //< Argument mixes with another through implicit
+                     //< qualification.
+
+  LLVM_MARK_AS_BITMASK_ENUM(/* LargestValue = */ MIXUP_CVR)
+};
+LLVM_ENABLE_BITMASK_ENUMS_IN_NAMESPACE();
+
+using AdjacentParamsMixup =
+    llvm::SmallVector<std::pair<const ParmVarDecl *, MixupTag>, 4>;
+
+} // namespace
+
+/// Attempts to strip away as many qualification, sugar and spice from the
+/// type as possible to reach the innermost purest the user eventually uses.
+/// This method attempts to discard everything as much as possible.
+static const Type *stripType(const Type *T);
+
+/// Returns whether an lvalue reference refers to the same type as T.
+static MixupTag refBindsToSameType(const LValueReferenceType *LRef,
+                                   const Type *T, const bool CVRMixPossible);
+
+/// Sanitises the MixupTag so it doesn't contain contradictory bits set.
+static MixupTag sanitiseMixup(MixupTag Tag) {
+  assert(Tag != MIXUP_Invalid && "Mixup tag had full zero bit pattern value!");
+
+  if (Tag & MIXUP_None)
+    // If at any point the check recursion marks the mixup impossible, it is
+    // just impossible.
+    return MIXUP_None;
+
+  if (Tag == MIXUP_Trivial)
+    return Tag;
+
+  if (Tag ^ MIXUP_Trivial)
+    // If any other bits than Trivial is set, unset Trivial, so only the
+    // annotation bits warranting extra diagnostic are set.
+    return Tag & ~MIXUP_Trivial;
+
+  return Tag;
+}
+
+/// Returns whether LType and RType refer to the same type in a sense that at a
+/// call site it is possible to mix the types up if the actual arguments are
+/// specified in opposite order.
+/// \returns MixupTag indicating how a mixup between the arguments happens.
+/// The final output of this potentially recursive function must be sanitised.
+static MixupTag howPossibleToMixUpAtCallSite(const QualType LType,
+                                             const QualType RType,
+                                             const ASTContext &Ctx,
+                                             const bool CVRMixPossible) {
+  LLVM_DEBUG(llvm::dbgs() << "isPossibleToMixUpAtCallSite?\n Left:";
+             LType.dump(llvm::dbgs()); llvm::dbgs() << "\n\t and \n Right: ";
+             RType.dump(llvm::dbgs()); llvm::dbgs() << '\n';);
+
+  if (LType == RType) {
+    LLVM_DEBUG(llvm::dbgs() << "!!! Both Left and Right are same type.\n";);
+    return MIXUP_Trivial;
+  }
+
+  // Remove certain sugars that don't affect mixability from the types.
+  if (dyn_cast<const ParenType>(LType.getTypePtr()))
+    return howPossibleToMixUpAtCallSite(LType.getSingleStepDesugaredType(Ctx),
+                                        RType, Ctx, CVRMixPossible);
+  if (dyn_cast<const ParenType>(RType.getTypePtr()))
+    return howPossibleToMixUpAtCallSite(
+        LType, RType.getSingleStepDesugaredType(Ctx), Ctx, CVRMixPossible);
+
+  {
+    const auto *LTypedef = LType->getAs<TypedefType>();
+    const auto *RTypedef = RType->getAs<TypedefType>();
+    if (LTypedef && RTypedef) {
+      LLVM_DEBUG(llvm::dbgs()
+                     << "!!! Both Left and Right are typedef types.\n";);
+      return MIXUP_Typedef | howPossibleToMixUpAtCallSite(LTypedef->desugar(),
+                                                          RTypedef->desugar(),
+                                                          Ctx, CVRMixPossible);
+    }
+    if (LTypedef) {
+      LLVM_DEBUG(llvm::dbgs() << "!!! Left is typedef type.\n";);
+      return MIXUP_Typedef |
+             howPossibleToMixUpAtCallSite(LTypedef->desugar(), RType, Ctx,
+                                          CVRMixPossible);
+    }
+    if (RTypedef) {
+      LLVM_DEBUG(llvm::dbgs() << "!!! Right is typedef type.\n";);
+      return MIXUP_Typedef |
+             howPossibleToMixUpAtCallSite(LType, RTypedef->desugar(), Ctx,
+                                          CVRMixPossible);
+    }
+  }
+
+  if (LType->isPointerType() && RType->isPointerType()) {
+    // (Both types being the exact same pointer is handled by LType == RType.)
+    LLVM_DEBUG(llvm::dbgs() << "!!! Both Left and Right are pointer types.\n";);
+    MixupTag Mixup = howPossibleToMixUpAtCallSite(
+        LType->getPointeeType(), RType->getPointeeType(), Ctx, CVRMixPossible);
+
+    if (LType.isLocalConstQualified() || LType.isLocalVolatileQualified() ||
+        LType.isLocalRestrictQualified() || RType.isLocalConstQualified() ||
+        RType.isLocalVolatileQualified() || RType.isLocalRestrictQualified()) {
+      // A qualified ptr might be mixed up with an unqualified one at call, but
+      // this is up to user configuration to report about.
+      if (CVRMixPossible)
+        Mixup |= MIXUP_CVR;
+      else
+        Mixup |= MIXUP_None;
+    }
+
+    return Mixup;
+  }
+
+  // An argument of type 'T' and 'const T' may bind with the same power.
+  // Case for both types being const qualified (for the same type) is handled
+  // by LType == RType.
+  if (CVRMixPossible &&
+      (LType.isLocalConstQualified() || LType.isLocalVolatileQualified())) {
+    LLVM_DEBUG(llvm::dbgs() << "!!! Left is CV qualified.";
+               LType.dump(llvm::dbgs()); llvm::dbgs() << '\n';);
+    return MIXUP_CVR | howPossibleToMixUpAtCallSite(LType.getUnqualifiedType(),
+                                                    RType, Ctx, CVRMixPossible);
+  }
+  if (CVRMixPossible &&
+      (RType.isLocalConstQualified() || RType.isLocalVolatileQualified())) {
+    LLVM_DEBUG(llvm::dbgs() << "!!! Right is CV qualified\n";
+               RType.dump(llvm::dbgs()); llvm::dbgs() << '\n';);
+    return MIXUP_CVR |
+           howPossibleToMixUpAtCallSite(LType, RType.getUnqualifiedType(), Ctx,
+                                        CVRMixPossible);
+  }
+
+  // An argument of type 'T' and 'const T &' may bind with the same power.
+  // (Note this is a different case, as 'const T &' is a '&' on the top level,
+  // and only then a const.)
+  if (LType->isLValueReferenceType() || RType->isLValueReferenceType()) {
+    // (If both is the same reference type, earlier a return happened.)
+
+    if (LType->isLValueReferenceType()) {
+      LLVM_DEBUG(llvm::dbgs() << "!!! Left is an lvalue reference type\n";);
+      MixupTag RefBind = refBindsToSameType(LType->getAs<LValueReferenceType>(),
+                                            RType.getTypePtr(), CVRMixPossible);
+      // RefBind may or may not have given us a tag (e.g. reference was to a
+      // typedef) via a recursive chain back to this function. Apply the
+      // "bind power" tag here to indicate a reference binding happened.
+      // (If RefBind was MIXUP_None, a later sanitise step will undo every bit
+      // except for None.)
+      return RefBind | MIXUP_RefBind;
+    }
+    if (RType->isLValueReferenceType()) {
+      LLVM_DEBUG(llvm::dbgs() << "!!! Right is an lvalue reference type.\n";);
+      MixupTag RefBind = refBindsToSameType(RType->getAs<LValueReferenceType>(),
+                                            LType.getTypePtr(), CVRMixPossible);
+      return RefBind | MIXUP_RefBind;
+    }
+  }
+
+  LLVM_DEBUG(llvm::dbgs() << "<<< End of logic reached, types don't match.";);
+  return MIXUP_None;
+}
+
+static MixupTag refBindsToSameType(const LValueReferenceType *LRef,
+                                   const Type *T, const bool CVRMixPossible) {
+  LLVM_DEBUG(llvm::dbgs() << "refBindsToSameType?\n"; LRef->dump(llvm::dbgs());
+             llvm::dbgs() << "\n\t and \n"; T->dump(llvm::dbgs());
+             llvm::dbgs() << '\n';);
+
+  const QualType ReferredType = LRef->getPointeeType();
+  LLVM_DEBUG(llvm::dbgs() << "Referred type:\n";
+             ReferredType->dump(llvm::dbgs()););
+  if (!ReferredType.isLocalConstQualified()) {
+    // A non-const reference doesn't bind with the same power as a "normal"
+    // by-value argument.
+    LLVM_DEBUG(llvm::dbgs() << "referred type is not const qualified.\n";);
+    return MIXUP_None;
+  }
+
+  if (const auto *TypedefTy = ReferredType.getTypePtr()->getAs<TypedefType>()) {
+    // If the referred type is a typedef, try checking the mixup-chance on the
+    // desugared type.
+    LLVM_DEBUG(llvm::dbgs() << "Reference to a typedef, calling back...\n";);
+    return howPossibleToMixUpAtCallSite(TypedefTy->desugar(), QualType{T, 0},
+                                        TypedefTy->getDecl()->getASTContext(),
+                                        CVRMixPossible);
+  }
+
+  LLVM_DEBUG(llvm::dbgs() << "is referred type type_ptr() and T the same? "
+                          << (ReferredType.getTypePtr() == T) << '\n';);
+  return ReferredType.getTypePtr() == T ? MIXUP_Trivial : MIXUP_None;
+}
+
+/// Gets the type-equal range of the parameters of F starting with the param
+/// at index i. The return value will always have at least 1 element, the ith
+/// parameter itself.
+/// \returns In the returned param mixup vector, each mixup tag refers to the
+/// reasoning behind mixing up the nth parameter with the first in the adjacent
+/// range.
+static AdjacentParamsMixup paramEqualTypeRange(const FunctionDecl *F,
+                                               unsigned int i,
+                                               const bool CVRMixPossible) {
+  assert(i <= F->getNumParams() && "invalid index given");
+
+  AdjacentParamsMixup APR;
+  const ParmVarDecl *First = F->getParamDecl(i);
+  LLVM_DEBUG(llvm::dbgs() << "Start " << i << "th param:\n";
+             First->dump(llvm::dbgs()); llvm::dbgs() << '\n';);
+  APR.emplace_back(First, MIXUP_Trivial);
+
+  const ASTContext &Ctx = F->getASTContext();
+  const unsigned int ParamCount = F->getNumParams();
+  for (++i; i < ParamCount; ++i) {
+    const ParmVarDecl *Ith = F->getParamDecl(i);
+    LLVM_DEBUG(llvm::dbgs() << "Check " << i << "th param:\n";
+               Ith->dump(llvm::dbgs()); llvm::dbgs() << '\n';);
+    MixupTag Mixup = howPossibleToMixUpAtCallSite(
+        First->getType(), Ith->getType(), Ctx, CVRMixPossible);
+    LLVM_DEBUG(llvm::dbgs() << "\tMixup? " << static_cast<int>(Mixup) << "\n";);
+    Mixup = sanitiseMixup(Mixup);
+    LLVM_DEBUG(llvm::dbgs() << "\tMixup (after sanitise)? "
+                            << static_cast<int>(Mixup) << "\n";);
+
+    if (Mixup == MIXUP_None)
+      break;
+
+    APR.emplace_back(Ith, Mixup);
+  }
+
+  if (APR.size() > 1 &&
+      llvm::any_of(APR, [](const AdjacentParamsMixup::value_type &E) {
+        return E.second & MIXUP_Typedef;
+      })) {
+    // If any of the mixups for argument pairs (1, 2), (1, 3), ... involve a
+    // typedef match, the first argument might be a typedef too. Set a bit to
+    // emit a "type alias resolution" diagnostic for the first argument of the
+    // range.
+    APR.front().second = MIXUP_Typedef;
+  }
+
+  return APR;
+}
+
+static const Type *stripType(const Type *T) {
+  LLVM_DEBUG(llvm::dbgs() << "Stripper handling:\n"; T->dump(llvm::dbgs());
+             llvm::dbgs() << '\n';);
+  if (const auto *PointerTy = T->getAs<PointerType>())
+    return stripType(PointerTy->getPointeeOrArrayElementType());
+  if (const auto *RefTy = T->getAs<ReferenceType>())
+    return stripType(RefTy->getPointeeType().getUnqualifiedType().getTypePtr());
+  if (const auto *TagTy = T->getAs<TagType>())
+    return TagTy;
+  if (const auto *Builtin = T->getAs<BuiltinType>())
+    return Builtin;
+  if (const auto *TypedefTy = T->getAs<TypedefType>())
+    return stripType(TypedefTy->desugar().getTypePtr());
+  if (const auto *ParenTy = T->getAs<ParenType>())
+    return stripType(ParenTy->desugar().getTypePtr());
+  if (const auto *PackTy = T->getAs<PackExpansionType>())
+    return stripType(PackTy->getPattern().getTypePtr());
+
+  // In any other case, assume we can't reasonably strip any further.
+  return T;
+}
+
+/// Prints the type's textual representation to the output stream. This printer
+/// discards as many sugar as it can, for example removing typedefs and printing
+/// the underlying type.
+static void putTypeName(const QualType QT, llvm::raw_ostream &OS,
+                        const PrintingPolicy &PP) {
+  LLVM_DEBUG(llvm::dbgs() << "putTypeName called for\n"; QT.dump(llvm::dbgs());
+             llvm::dbgs() << '\n';);
+  SplitQualType SQT = QT.split();
+  const Type *Ty = SQT.Ty;
+
+  if (const auto *DecayTy = dyn_cast<DecayedType>(Ty))
+    return putTypeName(DecayTy->getPointeeType(), OS, PP);
+
+  if (const auto *PointerTy = dyn_cast<PointerType>(Ty)) {
+    putTypeName(PointerTy->getPointeeType(), OS, PP);
+    // Note: this might print types with weird grammar (function pointers, etc.)
+    // in a weird way.
+    OS << " *";
+
+    // Qualifications of a pointer has to be after the '*'.
+    // (The member is qualified in a subsequent recursive call.)
+    if (SQT.Quals.hasConst())
+      OS << " const";
+    if (SQT.Quals.hasVolatile())
+      OS << " volatile";
+    if (SQT.Quals.hasRestrict())
+      OS << " restrict";
+    return;
+  }
+
+  // Qualifications of everything else can be written at the front.
+  if (SQT.Quals.hasConst())
+    OS << "const ";
+  if (SQT.Quals.hasVolatile())
+    OS << "volatile ";
+  if (SQT.Quals.hasRestrict())
+    OS << "restrict ";
+
+  if (const auto *RefTy = dyn_cast<ReferenceType>(Ty)) {
+    putTypeName(RefTy->getPointeeType(), OS, PP);
+    if (RefTy->isLValueReferenceType())
+      OS << " &";
+    else if (RefTy->isRValueReferenceType())
+      OS << " &&";
+  } else if (const auto *BuiltinTy = dyn_cast<BuiltinType>(Ty))
+    OS << BuiltinTy->getName(PP);
+  else if (const TagDecl *TagDeclTy = Ty->getAsTagDecl()) {
+    std::string Name = TagDeclTy->getName();
+    if (!Name.empty() && !TagDeclTy->getASTContext().getLangOpts().CPlusPlus)
+      // Prepend "struct" before "T" in C mode.
+      Name = TagDeclTy->getKindName().str().append(" ").append(Name);
+
+    if (Name.empty())
+      Name = TagDeclTy->getTypedefNameForAnonDecl()->getName();
+
+    if (!Name.empty())
+      OS << Name;
+    else
+      OS << "(unknown "
+         << static_cast<const DeclContext *>(TagDeclTy)->getDeclKindName()
+         << ")";
+  } else if (const auto *TypedefTy = dyn_cast<TypedefType>(Ty))
+    putTypeName(TypedefTy->desugar(), OS, PP);
+  else if (const auto *TemplateTy = dyn_cast<TemplateTypeParmType>(Ty))
+    OS << TemplateTy->getDecl()->getName();
+  else if (const auto *DependentNTy = dyn_cast<DependentNameType>(Ty)) {
+    OS << DependentNTy->getIdentifier()->getName();
+    LLVM_DEBUG(llvm::dbgs() << "Dependent Name Type\n";);
+  } else if (const auto *FunctionPtrTy = dyn_cast<FunctionProtoType>(Ty)) {
+    LLVM_DEBUG(llvm::dbgs() << "Printing function prototype...";
+               FunctionPtrTy->dump(llvm::dbgs()); llvm::dbgs() << '\n';);
+
+    putTypeName(FunctionPtrTy->getReturnType(), OS, PP);
+    OS << " (";
+    for (unsigned Idx = 0; Idx < FunctionPtrTy->getNumParams(); ++Idx) {
+      putTypeName(FunctionPtrTy->getParamType(Idx), OS, PP);
+      if (Idx < FunctionPtrTy->getNumParams() - 1)
+        OS << ", ";
+    }
+    OS << ')';
+  } else if (const auto *SpecialisationTy =
+                 dyn_cast<TemplateSpecializationType>(Ty)) {
+    SpecialisationTy->getTemplateName().print(OS, PP, /* SuppressNNS =*/true);
+    OS << '<';
+    for (const TemplateArgument &Arg : SpecialisationTy->template_arguments()) {
+      Arg.print(PP, OS);
+    }
+    OS << '>';
+  } else if (const auto *ParenTy = dyn_cast<ParenType>(Ty))
+    putTypeName(ParenTy->getInnerType(), OS, PP);
+  else if (const auto *PackTy = dyn_cast<PackExpansionType>(Ty)) {
+    putTypeName(PackTy->getPattern(), OS, PP);
+    OS << "...";
+  } else {
+    // There are things like "GCC Vector type" and such that who knows how
+    // to print properly?
+    OS << "<unhandled Type of " << Ty->getTypeClassName() << '>';
+  }
+}
+
+// For convenience, ignore a few well-known parameter variable and type names
+// which usually show up with repeated parameters.
+static const char *IgnoredParmNames[] = {
+    "iterator", "Iterator", "begin", "Begin", "end",
+    "End",      "first",    "First", "last",  "Last"};
+
+static const char *IgnoredTypeNames[] = {"it",
+                                         "It",
+                                         "iterator",
+                                         "Iterator",
+                                         "inputit",
+                                         "InputIt",
+                                         "forwardit",
+                                         "ForwardIt",
+                                         "bidirit",
+                                         "BidirIt",
+                                         "constiterator",
+                                         "const_iterator",
+                                         "Const_Iterator",
+                                         "Constiterator",
+                                         "ConstIterator"};
+
+/// Matches function parameters which type or variable name are not on the
+/// ignore list.
+static bool hasIgnoredName(const ParmVarDecl *Node) {
+  if (!Node->getIdentifier())
+    return true;
+  StringRef NodeName = Node->getName();
+  if (llvm::any_of(IgnoredParmNames, [&NodeName](const char *IgnoredName) {
+        if (NodeName == IgnoredName) {
+          LLVM_DEBUG(llvm::dbgs() << NodeName << " is on ignore list.\n";);
+          return true;
+        }
+        return false;
+      }))
+    return true;
+
+  const Type *NodeType =
+      Node->getType().getDesugaredType(Node->getASTContext()).getTypePtr();
+  assert(NodeType && "parameter without a type?");
+
+  std::string NodeTypeName = Node->getType().getAsString();
+  if (!NodeTypeName.empty()) {
+    if (llvm::any_of(IgnoredTypeNames, [&NodeName, &NodeTypeName](
+                                           const char *IgnoredName) {
+          if (NodeTypeName == IgnoredName) {
+            LLVM_DEBUG(llvm::dbgs() << NodeName << " has ignored type name "
+                                    << NodeTypeName << "\n";);
+            return true;
+          }
+
+          std::size_t IgnoredNameLen = strlen(IgnoredName);
+          if (NodeTypeName.length() > IgnoredNameLen &&
+              NodeTypeName.compare(NodeTypeName.length() - IgnoredNameLen,
+                                   IgnoredNameLen, IgnoredName) == 0) {
+            LLVM_DEBUG(llvm::dbgs()
+                           << NodeName << " ends with ignored type name "
+                           << IgnoredName << " in " << NodeTypeName << "\n";);
+            return true;
+          }
+          return false;
+        }))
+      return true;
+  }
+
+  return false;
+}
+
+AST_MATCHER_P(FunctionDecl, hasAtLeastNumArguments, unsigned int, N) {
+  return Node.getNumParams() >= N;
+}
+
+// Default to 3 so the users don't get a warning for every possible thing.
+static const unsigned DefaultMinLength = 3;
+
+/// A range length of 0 or 1 is useless as it would simply report for every
+/// argument. Fix the user's input if bad value is given.
+static unsigned clampMinimumLength(const unsigned Option) {
+  return Option < 2 ? DefaultMinLength : Option;
+}
+
+AdjacentArgumentsOfSameTypeCheck::AdjacentArgumentsOfSameTypeCheck(
+    StringRef Name, ClangTidyContext *Context)
+    : ClangTidyCheck(Name, Context),
+      MinimumLength(
+          clampMinimumLength(Options.get("MinimumLength", DefaultMinLength))),
+      CVRMixPossible(Options.get("CVRMixPossible", false)) {}
+
+void AdjacentArgumentsOfSameTypeCheck::storeOptions(
+    ClangTidyOptions::OptionMap &Opts) {
+  Options.store(Opts, "MinimumLength", MinimumLength);
+  Options.store(Opts, "CVRMixPossible", CVRMixPossible);
+}
+
+void AdjacentArgumentsOfSameTypeCheck::registerMatchers(MatchFinder *Finder) {
+  const LangOptions &Opts = getLangOpts();
+  if (Opts.ObjC)
+    // FIXME: Revise how this check could operate on ObjC code.
+    return;
+
+  Finder->addMatcher(functionDecl(
+                         // Only report for definitions as the user only has
+                         // chance to fix if they can change the def.
+                         isDefinition(), hasAtLeastNumArguments(MinimumLength),
+                         unless(ast_matchers::isTemplateInstantiation()))
+                         .bind("fun"),
+                     this);
+
+  Finder->addMatcher(functionDecl(
+                         // Only report for definitions as the user only has
+                         // chance to fix if they can change the def.
+                         isDefinition(), hasAtLeastNumArguments(MinimumLength),
+                         isExplicitTemplateSpecialization())
+                         .bind("fun"),
+                     this);
+}
+
+void AdjacentArgumentsOfSameTypeCheck::check(
+    const MatchFinder::MatchResult &Result) {
+  const auto *Fun = Result.Nodes.getNodeAs<FunctionDecl>("fun");
+  LLVM_DEBUG(llvm::dbgs() << "\n#############################################"
+                          << "\nHandling match for:\n";
+             Fun->dump(llvm::dbgs()); llvm::dbgs() << "\n\n";);
+  unsigned int ParamEqRangeStartIdx = 0;
+  const unsigned int NumArgs = Fun->getNumParams();
+  while (ParamEqRangeStartIdx < NumArgs) {
+    if (hasIgnoredName(Fun->getParamDecl(ParamEqRangeStartIdx))) {
+      // If the current argument's name or type name is ignored, don't try
+      // creating a range from it.
+      LLVM_DEBUG(llvm::dbgs()
+                     << ParamEqRangeStartIdx << "th argument ignored.\n";);
+      ++ParamEqRangeStartIdx;
+      continue;
+    }
+
+    AdjacentParamsMixup APR =
+        paramEqualTypeRange(Fun, ParamEqRangeStartIdx, CVRMixPossible);
+    ParamEqRangeStartIdx += APR.size();
+    if (APR.size() < MinimumLength)
+      continue;
+
+    // A function with an adjacent argument range of sufficient length found.
+    std::string FunName = Fun->getDeclName().getAsString();
+    if (FunName.empty())
+      FunName = "<unknown>";
+
+    const PrintingPolicy &PP{Fun->getASTContext().getLangOpts()};
+    std::string FirstArgType = APR.front().first->getType().getAsString(PP);
+
+    const auto HasMixupTagWarrantingTypePrint =
+        [](const AdjacentParamsMixup::value_type &E) {
+          return E.second & (MIXUP_Typedef | MIXUP_RefBind | MIXUP_CVR);
+        };
+
+    if (llvm::any_of(APR, HasMixupTagWarrantingTypePrint)) {
+      // If any of the arguments in the range is annotated with a tag that will
+      // warrant printing type names, the primary diagnostic shouldn't have
+      // a type name printed.
+      diag(APR.front().first->getOuterLocStart(),
+           "%0 adjacent arguments for '%1' of similar type are easily swapped "
+           "by mistake")
+          << static_cast<unsigned>(APR.size()) << FunName;
+    } else {
+      diag(APR.front().first->getOuterLocStart(),
+           "%0 adjacent arguments for '%1' of similar type ('%2') are easily "
+           "swapped by mistake")
+          << static_cast<unsigned>(APR.size()) << FunName << FirstArgType;
+    }
+
+    // FIXME: ClangTidy doesn't seem to support emitting a custom source range
+    // highlight without a fixit. This check can't produce useful automatic
+    // fixits, but highlighting the 'CharSourceRange' would be good to have.
+    diag(APR.back().first->getLocation(),
+         "last argument in the adjacent range here", DiagnosticIDs::Note);
+
+    for (const auto &ParmPair : APR) {
+      if (ParmPair.second < MIXUP_Trivial)
+        llvm_unreachable("Invalid mixup type in result when emitting diags.");
+      // MIXUP_Trivial: no extra diagnostics required.
+
+      if (HasMixupTagWarrantingTypePrint(ParmPair)) {
+        std::string ParmType;
+        {
+          llvm::raw_string_ostream OS{ParmType};
+          LLVM_DEBUG(llvm::dbgs()
+                         << "\n\nGenerating type name for diagnostic...\n\n";);
+          putTypeName(ParmPair.first->getType(), OS, PP);
+        }
+        LLVM_DEBUG(llvm::dbgs()
+                       << "Type name generated: " << ParmType << '\n';);
+
+        if (ParmPair.second & MIXUP_Typedef) {
+          diag(ParmPair.first->getOuterLocStart(),
+               "after resolving type aliases, type of argument '%0' is '%1'",
+               DiagnosticIDs::Note)
+              << ParmPair.first->getName() << ParmType;
+        }
+
+        if (ParmPair.second & (MIXUP_RefBind | MIXUP_CVR)) {
+          diag(ParmPair.first->getOuterLocStart(),
+               "at a call site, '%0' might bind with same force as '%1'",
+               DiagnosticIDs::Note)
+              << ParmType << FirstArgType;
+        }
+      }
+    }
+  }
+}
+
+} // namespace cppcoreguidelines
+} // namespace tidy
+} // namespace clang
_______________________________________________
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to