whisperity created this revision.
whisperity added reviewers: aaron.ballman, alexfh, Eugene.Zelenko, JonasToth, 
NoQ, Szelethus, xazax.hun, baloghadamsoftware, Charusso.
whisperity added a project: clang-tools-extra.
Herald added subscribers: cfe-commits, ormris, kbarton, mgorny, nemanjai.
Herald added a project: clang.

This 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.

The relevant C++ Core Guidelines rules to which conformity is checked is found 
at: 
http://github.com/isocpp/CppCoreGuidelines/blob/master/CppCoreGuidelines.md#i24-avoid-adjacent-unrelated-parameters-of-the-same-type


Repository:
  rG LLVM Github Monorepo

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/cppcoreguidelines-avoid-adjacent-arguments-of-same-type-cvr-on.cpp
  
clang-tools-extra/test/clang-tidy/cppcoreguidelines-avoid-adjacent-arguments-of-same-type-default.cpp
  
clang-tools-extra/test/clang-tidy/cppcoreguidelines-avoid-adjacent-arguments-of-same-type-verbose.cpp

Index: clang-tools-extra/test/clang-tidy/cppcoreguidelines-avoid-adjacent-arguments-of-same-type-verbose.cpp
===================================================================
--- /dev/null
+++ clang-tools-extra/test/clang-tidy/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/cppcoreguidelines-avoid-adjacent-arguments-of-same-type-default.cpp
===================================================================
--- /dev/null
+++ clang-tools-extra/test/clang-tidy/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'
\ No newline at end of file
Index: clang-tools-extra/test/clang-tidy/cppcoreguidelines-avoid-adjacent-arguments-of-same-type-cvr-on.cpp
===================================================================
--- /dev/null
+++ clang-tools-extra/test/clang-tidy/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,124 @@
+.. title:: clang-tidy - cppcoreguidelines-avoid-adjacent-arguments-of-same-type
+
+cppcoreguidelines-avoid-adjacent-arguments-of-same-type
+=======================================================
+
+This 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.
+
+This 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.
+    Can be any (positive integer) number.
+    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) { /* ... */ }
\ No newline at end of file
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.
+
+  This 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 {
+
+/// This 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.
+///
+/// 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,602 @@
+//===--- 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 <string>
+#include <utility>
+
+#include "AdjacentArgumentsOfSameTypeCheck.h"
+#include "clang/AST/ASTContext.h"
+#include "clang/ASTMatchers/ASTMatchFinder.h"
+#include "llvm/ADT/BitmaskEnum.h"
+#include "llvm/ADT/SmallVector.h"
+
+// 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 {
+
+/// 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.
+const Type *stripType(const Type *T);
+
+/// 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();
+
+/// Returns whether an lvalue reference refers to the same type as T.
+MixupTag refBindsToSameType(const LValueReferenceType *LRef, const Type *T,
+                            const bool CVRMixPossible);
+
+/// Sanitises the MixupTag so it doesn't contain contradictory bits set.
+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.
+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 LangOptions &LOpts = Ctx.getLangOpts();
+  assert(LOpts.CPlusPlus && "check should only be enabled for C++ TUs");
+
+  {
+    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 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() ||
+        RType.isLocalConstQualified() || RType.isLocalVolatileQualified()) {
+      // 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() ||
+       LType.isLocalRestrictQualified())) {
+    LLVM_DEBUG(llvm::dbgs() << "!!! Left is CVR qualified.";
+               LType.dump(llvm::dbgs()); llvm::dbgs() << '\n';);
+    return MIXUP_CVR | howPossibleToMixUpAtCallSite(LType.getUnqualifiedType(),
+                                                    RType, Ctx, CVRMixPossible);
+  }
+  if (CVRMixPossible &&
+      (RType.isLocalConstQualified() || RType.isLocalVolatileQualified() ||
+       RType.isLocalRestrictQualified())) {
+    LLVM_DEBUG(llvm::dbgs() << "!!! Right is CVR 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 on the top level a '&',
+  // 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;
+}
+
+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 TypedefType *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;
+}
+
+using AdjacentParamsMixup =
+    llvm::SmallVector<std::pair<const ParmVarDecl *, MixupTag>, 4>;
+
+/// 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.
+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;
+}
+
+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.
+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()) {
+    StringRef Name = TagDeclTy->getName();
+    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.
+const char *IgnoredParmNames[] = {"iterator", "Iterator", "begin", "Begin",
+                                  "end",      "End",      "first", "First",
+                                  "last",     "Last"};
+
+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.
+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;
+}
+
+} // namespace
+
+AdjacentArgumentsOfSameTypeCheck::AdjacentArgumentsOfSameTypeCheck(
+    StringRef Name, ClangTidyContext *Context)
+    : ClangTidyCheck(Name, Context),
+      MinimumLength(Options.getLocalOrGlobal("MinimumLength", 0) < 2
+                        ? 3
+                        : Options.getLocalOrGlobal("MinimumLength", 3)),
+      CVRMixPossible(Options.getLocalOrGlobal("CVRMixPossible", false)) {}
+
+void AdjacentArgumentsOfSameTypeCheck::storeOptions(
+    ClangTidyOptions::OptionMap &Opts) {
+  Options.store(Opts, "MinimumLength", MinimumLength);
+  Options.store(Opts, "CVRMixPossible", CVRMixPossible);
+}
+
+void AdjacentArgumentsOfSameTypeCheck::registerMatchers(MatchFinder *Finder) {
+  if (!getLangOpts().CPlusPlus)
+    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