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

- Better organisation of code, cleanup of debug prints, fix nomenclature of 
functions
- Explicitly mention the first and last param of the range for clarity
- Downlift the logic of joining and breaking ranges to main patch (this isn't 
terribly useful at this point, but both subsequent improvements to the check 
depend on this change)
  - Basically no longer assume that if param N can be swapped with param 1, 
then it can also be swapped with 2, 3, etc. without checking.
- Made the list of ignored parameter and type names configurable as a checker 
option
- Changed the default value of `MinimumLength` to `2`, as prescribed in the 
guideline's text.


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

https://reviews.llvm.org/D69560

Files:
  clang-tools-extra/clang-tidy/experimental/CMakeLists.txt
  
clang-tools-extra/clang-tidy/experimental/CppcoreguidelinesAvoidAdjacentParametersOfTheSameTypeCheck.cpp
  
clang-tools-extra/clang-tidy/experimental/CppcoreguidelinesAvoidAdjacentParametersOfTheSameTypeCheck.h
  clang-tools-extra/clang-tidy/experimental/ExperimentalTidyModule.cpp
  clang-tools-extra/docs/ReleaseNotes.rst
  
clang-tools-extra/docs/clang-tidy/checks/experimental-cppcoreguidelines-avoid-adjacent-parameters-of-the-same-type.rst
  clang-tools-extra/docs/clang-tidy/checks/list.rst
  
clang-tools-extra/test/clang-tidy/checkers/experimental-cppcoreguidelines-avoid-adjacent-parameters-of-the-same-type-cvr-on.cpp
  
clang-tools-extra/test/clang-tidy/checkers/experimental-cppcoreguidelines-avoid-adjacent-parameters-of-the-same-type-ignores.cpp
  
clang-tools-extra/test/clang-tidy/checkers/experimental-cppcoreguidelines-avoid-adjacent-parameters-of-the-same-type-minlen3.cpp
  
clang-tools-extra/test/clang-tidy/checkers/experimental-cppcoreguidelines-avoid-adjacent-parameters-of-the-same-type.c

Index: clang-tools-extra/test/clang-tidy/checkers/experimental-cppcoreguidelines-avoid-adjacent-parameters-of-the-same-type.c
===================================================================
--- /dev/null
+++ clang-tools-extra/test/clang-tidy/checkers/experimental-cppcoreguidelines-avoid-adjacent-parameters-of-the-same-type.c
@@ -0,0 +1,34 @@
+// RUN: %check_clang_tidy %s \
+// RUN:   experimental-cppcoreguidelines-avoid-adjacent-parameters-of-the-same-type %t \
+// RUN:   -config='{CheckOptions: [ \
+// RUN:     {key: experimental-cppcoreguidelines-avoid-adjacent-parameters-of-the-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 parameters for 'memcpy' of similar type are
+// CHECK-MESSAGES: :[[@LINE-2]]:32: note: the first parameter in this range is 'dest'
+// CHECK-MESSAGES: :[[@LINE-3]]:63: note: the last parameter in this range is 'src'
+// CHECK-MESSAGES: :[[@LINE-4]]: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 parameters for 'merge' of similar type are
+// CHECK-MESSAGES: :[[@LINE-2]]:22: note: the first parameter in this range is 'dst'
+// CHECK-MESSAGES: :[[@LINE-3]]:65: note: the last parameter in this range is 'src2'
+// CHECK-MESSAGES: :[[@LINE-4]]:27: note: at a call site, 'const struct T *' might bind with same force as 'struct T *'
+// CHECK-MESSAGES: :[[@LINE-5]]: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 parameters for 'equals' of similar type ('struct T') are
+// CHECK-MESSAGES: :[[@LINE-2]]:21: note: the first parameter in this range is 'a'
+// CHECK-MESSAGES: :[[@LINE-3]]:33: note: the last parameter in this range is 'b'
+
+typedef struct {
+  int x;
+} S;
+
+int equals2(S l, S r) { return l.x == r.x; }
+// CHECK-MESSAGES: :[[@LINE-1]]:13: warning: 2 adjacent parameters for 'equals2' of similar type ('S') are
+// CHECK-MESSAGES: :[[@LINE-2]]:15: note: the first parameter in this range is 'l'
+// CHECK-MESSAGES: :[[@LINE-3]]:20: note: the last parameter in this range is 'r'
Index: clang-tools-extra/test/clang-tidy/checkers/experimental-cppcoreguidelines-avoid-adjacent-parameters-of-the-same-type-minlen3.cpp
===================================================================
--- /dev/null
+++ clang-tools-extra/test/clang-tidy/checkers/experimental-cppcoreguidelines-avoid-adjacent-parameters-of-the-same-type-minlen3.cpp
@@ -0,0 +1,205 @@
+// RUN: %check_clang_tidy %s \
+// RUN:   experimental-cppcoreguidelines-avoid-adjacent-parameters-of-the-same-type %t \
+// RUN:   -config='{CheckOptions: [ \
+// RUN:     {key: experimental-cppcoreguidelines-avoid-adjacent-parameters-of-the-same-type.MinimumLength, value: 3} \
+// 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 parameters present.
+
+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) {}
+
+void typedef_and_realtype_ptr_const(int *p1, I1 *p2, const I1 *p3) {}
+
+void typedef_ptr_const(I1 *p1, I1 *p2, const I1 *p3) {}
+
+// END of NO-WARN.
+
+// 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) {}
+
+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 parameters for 'protochain' of similar type ('int') are
+// CHECK-MESSAGES: :[[@LINE-2]]:21: note: the first parameter in this range is 'i'
+// CHECK-MESSAGES: :[[@LINE-3]]:35: note: the last parameter in this range is 'k'
+
+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 parameters for 'protochain2' of similar type are
+// CHECK-MESSAGES: :[[@LINE-2]]:21: note: the first parameter in this range is 'i'
+// CHECK-MESSAGES: :[[@LINE-3]]:41: note: the last parameter in this range is 'k'
+// CHECK-MESSAGES: :[[@LINE-4]]:18: note: after resolving type aliases, type of parameter 'i' is 'int'
+// CHECK-MESSAGES: :[[@LINE-5]]:24: note: after resolving type aliases, type of parameter 'j' is 'int'
+// CHECK-MESSAGES: :[[@LINE-6]]:30: note: after resolving type aliases, type of parameter 'k' is 'int'
+
+void len3(const StringRef Drive, const StringRef Dirs, const StringRef FileName) {}
+// CHECK-MESSAGES: :[[@LINE-1]]:11: warning: 3 adjacent parameters for 'len3' of similar type ('const StringRef') are
+// CHECK-MESSAGES: :[[@LINE-2]]:27: note: the first parameter in this range is 'Drive'
+// CHECK-MESSAGES: :[[@LINE-3]]:72: note: the last parameter in this range is 'FileName'
+
+void len4(const StringRef Drive, const StringRef Dirs, const StringRef FileName, const StringRef Extension) {}
+// CHECK-MESSAGES: :[[@LINE-1]]:11: warning: 4 adjacent parameters for 'len4' of similar type ('const StringRef') are
+// CHECK-MESSAGES: :[[@LINE-2]]:27: note: the first parameter in this range is 'Drive'
+// CHECK-MESSAGES: :[[@LINE-3]]:98: note: the last parameter in this range is 'Extension'
+
+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 parameters for 'multi_packs' of similar type ('StringRef') are
+// CHECK-MESSAGES: :[[@LINE-4]]:28: note: the first parameter in this range is 'Path'
+// CHECK-MESSAGES: :[[@LINE-5]]:60: note: the last parameter in this range is 'Occupation'
+// CHECK-MESSAGES: :[[@LINE-4]]:18: warning: 3 adjacent parameters for 'multi_packs' of similar type ('Toggle') are
+// CHECK-MESSAGES: :[[@LINE-5]]:25: note: the first parameter in this range is 'Read'
+// CHECK-MESSAGES: :[[@LINE-6]]:52: note: the last parameter in this range is 'Execute'
+
+// CVRMixupPossible only concerns 'T' and 'c/v/r T', or pointers thereof, not
+// references.
+void lifetime_extension(T t, const T &tr1, const T &tr2) {}
+// CHECK-MESSAGES: :[[@LINE-1]]:25: warning: 3 adjacent parameters for 'lifetime_extension' of similar type are
+// CHECK-MESSAGES: :[[@LINE-2]]:27: note: the first parameter in this range is 't'
+// CHECK-MESSAGES: :[[@LINE-3]]:53: note: the last parameter in this range is 'tr2'
+// CHECK-MESSAGES: :[[@LINE-4]]:30: note: at a call site, 'const T &' might bind with same force as 'T'
+// CHECK-MESSAGES: :[[@LINE-5]]: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 parameters for 'setColouredOutput' of similar type ('OldSchoolTermColour') are
+// CHECK-MESSAGES: :[[@LINE-4]]:44: note: the first parameter in this range is 'Background'
+// CHECK-MESSAGES: :[[@LINE-3]]:44: note: the last parameter in this range is 'Border'
+
+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 parameters for 'setColouredOutput2' of similar type are
+// CHECK-MESSAGES: :[[@LINE-4]]:36: note: the first parameter in this range is 'Background'
+// CHECK-MESSAGES: :[[@LINE-3]]:38: note: the last parameter in this range is 'Border'
+// CHECK-MESSAGES: :[[@LINE-6]]:25: note: after resolving type aliases, type of parameter 'Background' is 'OldSchoolTermColour'
+// CHECK-MESSAGES: :[[@LINE-6]]:25: note: after resolving type aliases, type of parameter 'Foreground' is 'OldSchoolTermColour'
+// CHECK-MESSAGES: :[[@LINE-6]]:25: note: after resolving type aliases, type of parameter 'Border' is 'OldSchoolTermColour'
Index: clang-tools-extra/test/clang-tidy/checkers/experimental-cppcoreguidelines-avoid-adjacent-parameters-of-the-same-type-ignores.cpp
===================================================================
--- /dev/null
+++ clang-tools-extra/test/clang-tidy/checkers/experimental-cppcoreguidelines-avoid-adjacent-parameters-of-the-same-type-ignores.cpp
@@ -0,0 +1,40 @@
+// RUN: %check_clang_tidy %s \
+// RUN:   experimental-cppcoreguidelines-avoid-adjacent-parameters-of-the-same-type %t \
+// RUN:   -config='{CheckOptions: [ \
+// RUN:     {key: experimental-cppcoreguidelines-avoid-adjacent-parameters-of-the-same-type.IgnoredNames, value: "a;b"}, \
+// RUN:     {key: experimental-cppcoreguidelines-avoid-adjacent-parameters-of-the-same-type.IgnoredTypes, value: "foo;bar"} \
+// RUN:   ]}' --
+
+struct iterator {};
+
+// These names are not ignored.
+void copy(iterator begin, iterator end) {}
+// CHECK-MESSAGES: :[[@LINE-1]]:11: warning: 2 adjacent parameters for 'copy' of similar type ('iterator') are easily swapped by mistake [experimental-cppcoreguidelines-avoid-adjacent-parameters-of-the-same-type]
+// CHECK-MESSAGES: :[[@LINE-2]]:20: note: the first parameter in this range is 'begin'
+// CHECK-MESSAGES: :[[@LINE-3]]:36: note: the last parameter in this range is 'end'
+
+struct foo {};
+struct bar {};
+
+// NO-WARN: Ignored parameter types.
+void test01(foo x, foo y) {}
+void test02(bar x, bar y) {}
+
+struct crowbar {};
+void test03(crowbar x, crowbar y) {} // NO-WARN: Ignored parameter type suffix.
+
+// NO-WARN: Ignored parameter names.
+void test04(int a, int b) {}
+
+void len05(int x, int y, int z, int u, int v) {}
+// CHECK-MESSAGES: :[[@LINE-1]]:12: warning: 5 adjacent parameters for 'len05' of similar type ('int') are
+// CHECK-MESSAGES: :[[@LINE-2]]:16: note: the first parameter in this range is 'x'
+// CHECK-MESSAGES: :[[@LINE-3]]:44: note: the last parameter in this range is 'v'
+
+void ignored_inside_range(int x, int y, int a, int u, int v) {}
+// CHECK-MESSAGES: :[[@LINE-1]]:27: warning: 2 adjacent parameters for 'ignored_inside_range' of similar type ('int') are
+// CHECK-MESSAGES: :[[@LINE-2]]:31: note: the first parameter in this range is 'x'
+// CHECK-MESSAGES: :[[@LINE-3]]:38: note: the last parameter in this range is 'y'
+// CHECK-MESSAGES: :[[@LINE-4]]:48: warning: 2 adjacent parameters for 'ignored_inside_range' of similar type ('int') are
+// CHECK-MESSAGES: :[[@LINE-5]]:52: note: the first parameter in this range is 'u'
+// CHECK-MESSAGES: :[[@LINE-6]]:59: note: the last parameter in this range is 'v'
Index: clang-tools-extra/test/clang-tidy/checkers/experimental-cppcoreguidelines-avoid-adjacent-parameters-of-the-same-type-cvr-on.cpp
===================================================================
--- /dev/null
+++ clang-tools-extra/test/clang-tidy/checkers/experimental-cppcoreguidelines-avoid-adjacent-parameters-of-the-same-type-cvr-on.cpp
@@ -0,0 +1,492 @@
+// RUN: %check_clang_tidy %s \
+// RUN:   experimental-cppcoreguidelines-avoid-adjacent-parameters-of-the-same-type %t \
+// RUN:   -config='{CheckOptions: [ \
+// RUN:     {key: experimental-cppcoreguidelines-avoid-adjacent-parameters-of-the-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 unnamed_param(T *p, T *) {}
+// CHECK-MESSAGES: :[[@LINE-1]]:20: warning: 2 adjacent parameters for 'unnamed_param' of similar type ('T *') are easily swapped by mistake [experimental-cppcoreguidelines-avoid-adjacent-parameters-of-the-same-type]
+// CHECK-MESSAGES: :[[@LINE-2]]:23: note: the first parameter in this range is 'p'
+// CHECK-MESSAGES: :[[@LINE-3]]:29: note: the last parameter in this range is '<unnamed>'
+
+void copy(T *p, T *q, int n) {}
+// CHECK-MESSAGES: :[[@LINE-1]]:11: warning: 2 adjacent parameters for 'copy' of similar type ('T *') are
+// CHECK-MESSAGES: :[[@LINE-2]]:14: note: the first parameter in this range is 'p'
+// CHECK-MESSAGES: :[[@LINE-3]]:20: note: the last parameter in this range is 'q'
+
+void mov(T *dst, const T *src) {}
+// CHECK-MESSAGES: :[[@LINE-1]]:10: warning: 2 adjacent parameters for 'mov' of similar type are
+// CHECK-MESSAGES: :[[@LINE-2]]:13: note: the first parameter in this range is 'dst'
+// CHECK-MESSAGES: :[[@LINE-3]]:27: note: the last parameter in this range is 'src'
+// CHECK-MESSAGES: :[[@LINE-4]]: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 parameters for 'compare01' of similar type ('T') are
+// CHECK-MESSAGES: :[[@LINE-2]]:18: note: the first parameter in this range is 't1'
+// CHECK-MESSAGES: :[[@LINE-3]]:24: note: the last parameter in this range is 't2'
+
+void compare02(const T t1, T t2) {}
+// CHECK-MESSAGES: :[[@LINE-1]]:16: warning: 2 adjacent parameters for 'compare02' of similar type are
+// CHECK-MESSAGES: :[[@LINE-2]]:24: note: the first parameter in this range is 't1'
+// CHECK-MESSAGES: :[[@LINE-3]]:30: note: the last parameter in this range is 't2'
+// CHECK-MESSAGES: :[[@LINE-4]]: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 parameters for 'compare03' of similar type are
+// CHECK-MESSAGES: :[[@LINE-2]]:18: note: the first parameter in this range is 't1'
+// CHECK-MESSAGES: :[[@LINE-3]]:30: note: the last parameter in this range is 't2'
+// CHECK-MESSAGES: :[[@LINE-4]]: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 parameters for 'compare04' of similar type are
+// CHECK-MESSAGES: :[[@LINE-2]]:25: note: the first parameter in this range is 't1'
+// CHECK-MESSAGES: :[[@LINE-3]]:31: note: the last parameter in this range is 't2'
+// CHECK-MESSAGES: :[[@LINE-4]]: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 parameters for 'compare05' of similar type are
+// CHECK-MESSAGES: :[[@LINE-2]]:18: note: the first parameter in this range is 't1'
+// CHECK-MESSAGES: :[[@LINE-3]]:31: note: the last parameter in this range is 't2'
+// CHECK-MESSAGES: :[[@LINE-4]]: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 parameters for 'compare06' of similar type ('const T &') are
+// CHECK-MESSAGES: :[[@LINE-2]]:25: note: the first parameter in this range is 't1'
+// CHECK-MESSAGES: :[[@LINE-3]]:38: note: the last parameter in this range is 't2'
+
+void compare07(const T &t1, const T t2) {}
+// CHECK-MESSAGES: :[[@LINE-1]]:16: warning: 2 adjacent parameters for 'compare07' of similar type are
+// CHECK-MESSAGES: :[[@LINE-2]]:25: note: the first parameter in this range is 't1'
+// CHECK-MESSAGES: :[[@LINE-3]]:37: note: the last parameter in this range is 't2'
+// CHECK-MESSAGES: :[[@LINE-4]]: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 parameters for 'compare08' of similar type are
+// CHECK-MESSAGES: :[[@LINE-2]]:24: note: the first parameter in this range is 't1'
+// CHECK-MESSAGES: :[[@LINE-3]]:37: note: the last parameter in this range is 't2'
+// CHECK-MESSAGES: :[[@LINE-4]]: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 parameters for 'compare0B' of similar type ('T &&') are
+// CHECK-MESSAGES: :[[@LINE-2]]:20: note: the first parameter in this range is 't1'
+// CHECK-MESSAGES: :[[@LINE-3]]:28: note: the last parameter in this range is 't2'
+
+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 parameters for 'multi_bind' of similar type are
+// CHECK-MESSAGES: :[[@LINE-2]]:19: note: the first parameter in this range is 't'
+// CHECK-MESSAGES: :[[@LINE-3]]:43: note: the last parameter in this range is 't3'
+// CHECK-MESSAGES: :[[@LINE-4]]:22: note: at a call site, 'const T' might bind with same force as 'T'
+// CHECK-MESSAGES: :[[@LINE-5]]: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 parameters for 'ptr_quals' of similar type are
+// CHECK-MESSAGES: :[[@LINE-2]]:21: note: the first parameter in this range is 'ip'
+// CHECK-MESSAGES: :[[@LINE-3]]:99: note: the last parameter in this range is 'vipv'
+// CHECK-MESSAGES: :[[@LINE-4]]:25: note: at a call site, 'const int *' might bind with same force as 'int *'
+// CHECK-MESSAGES: :[[@LINE-5]]:41: note: at a call site, 'volatile int *' might bind with same force as 'int *'
+// CHECK-MESSAGES: :[[@LINE-6]]:60: note: at a call site, 'int * const' might bind with same force as 'int *'
+// CHECK-MESSAGES: :[[@LINE-7]]: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 parameters for 'value_quals' of similar type are
+// CHECK-MESSAGES: :[[@LINE-2]]:20: note: the first parameter in this range is 't'
+// CHECK-MESSAGES: :[[@LINE-3]]:67: note: the last parameter in this range is 'cvt'
+// CHECK-MESSAGES: :[[@LINE-4]]:23: note: at a call site, 'const T' might bind with same force as 'T'
+// CHECK-MESSAGES: :[[@LINE-5]]:35: note: at a call site, 'volatile T' might bind with same force as 'T'
+// CHECK-MESSAGES: :[[@LINE-6]]: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 parameters for 'value_ptr_quals' of similar type are
+// CHECK-MESSAGES: :[[@LINE-2]]:25: note: the first parameter in this range is 'tp'
+// CHECK-MESSAGES: :[[@LINE-3]]:110: note: the last parameter in this range is 'tpv'
+// CHECK-MESSAGES: :[[@LINE-4]]:29: note: at a call site, 'const T *' might bind with same force as 'T *'
+// CHECK-MESSAGES: :[[@LINE-5]]:43: note: at a call site, 'volatile T *' might bind with same force as 'T *'
+// CHECK-MESSAGES: :[[@LINE-6]]:60: note: at a call site, 'const volatile T *' might bind with same force as 'T *'
+// CHECK-MESSAGES: :[[@LINE-7]]:84: note: at a call site, 'T * const' might bind with same force as 'T *'
+// CHECK-MESSAGES: :[[@LINE-8]]: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 parameters for 'set_lerped' of similar type are
+// CHECK-MESSAGES: :[[@LINE-4]]:38: note: the first parameter in this range is 'min'
+// CHECK-MESSAGES: :[[@LINE-3]]:32: note: the last parameter in this range is 'value'
+// CHECK-MESSAGES: :[[@LINE-4]]:17: note: at a call site, 'int * const' might bind with same force as 'const Numeral *const'
+
+typedef int I1;
+typedef int I2;
+void multi_through_typedef(I1 i1, I2 i2) {}
+// CHECK-MESSAGES: :[[@LINE-1]]:28: warning: 2 adjacent parameters for 'multi_through_typedef' of similar type are
+// CHECK-MESSAGES: :[[@LINE-2]]:31: note: the first parameter in this range is 'i1'
+// CHECK-MESSAGES: :[[@LINE-3]]:38: note: the last parameter in this range is 'i2'
+// CHECK-MESSAGES: :[[@LINE-4]]:28: note: after resolving type aliases, type of parameter 'i1' is 'int'
+// CHECK-MESSAGES: :[[@LINE-5]]:35: note: after resolving type aliases, type of parameter 'i2' is 'int'
+
+void multi_ptr_through_typedef(I1 *p1, I2 *p2) {}
+// CHECK-MESSAGES: :[[@LINE-1]]:32: warning: 2 adjacent parameters for 'multi_ptr_through_typedef' of similar type are
+// CHECK-MESSAGES: :[[@LINE-2]]:36: note: the first parameter in this range is 'p1'
+// CHECK-MESSAGES: :[[@LINE-3]]:44: note: the last parameter in this range is 'p2'
+// CHECK-MESSAGES: :[[@LINE-4]]:32: note: after resolving type aliases, type of parameter 'p1' is 'int *'
+// CHECK-MESSAGES: :[[@LINE-5]]:40: note: after resolving type aliases, type of parameter 'p2' is 'int *'
+
+void typedef_and_realtype_ptr_const(int *p1, const I1 *p2) {}
+// CHECK-MESSAGES: :[[@LINE-1]]:37: warning: 2 adjacent parameters for 'typedef_and_realtype_ptr_const' of similar type are
+// CHECK-MESSAGES: :[[@LINE-2]]:42: note: the first parameter in this range is 'p1'
+// CHECK-MESSAGES: :[[@LINE-3]]:56: note: the last parameter in this range is 'p2'
+// CHECK-MESSAGES: :[[@LINE-4]]:37: note: after resolving type aliases, type of parameter 'p1' is 'int *'
+// CHECK-MESSAGES: :[[@LINE-5]]:46: note: after resolving type aliases, type of parameter 'p2' is 'const int *'
+// CHECK-MESSAGES: :[[@LINE-6]]:46: note: at a call site, 'const int *' might bind with same force as 'int *'
+
+void typedef_ptr_const(I1 *p1, const I1 *p2) {}
+// CHECK-MESSAGES: :[[@LINE-1]]:24: warning: 2 adjacent parameters for 'typedef_ptr_const' of similar type are
+// CHECK-MESSAGES: :[[@LINE-2]]:28: note: the first parameter in this range is 'p1'
+// CHECK-MESSAGES: :[[@LINE-3]]:42: note: the last parameter in this range is 'p2'
+// CHECK-MESSAGES: :[[@LINE-4]]:32: note: at a call site, 'const int *' might bind with same force as 'I1 *'
+
+typedef I2 myInt;
+typedef I2 myOtherInt;
+void typedef_chain(myInt mi, myOtherInt moi) {}
+// CHECK-MESSAGES: :[[@LINE-1]]:20: warning: 2 adjacent parameters for 'typedef_chain' of similar type are
+// CHECK-MESSAGES: :[[@LINE-2]]:26: note: the first parameter in this range is 'mi'
+// CHECK-MESSAGES: :[[@LINE-3]]:41: note: the last parameter in this range is 'moi'
+// CHECK-MESSAGES: :[[@LINE-4]]:20: note: after resolving type aliases, type of parameter 'mi' is 'int'
+// CHECK-MESSAGES: :[[@LINE-5]]:30: note: after resolving type aliases, type of parameter 'moi' is 'int'
+
+void bind_power_and_typedef(int Read, const myOtherInt &Write) {}
+// CHECK-MESSAGES: :[[@LINE-1]]:29: warning: 2 adjacent parameters for 'bind_power_and_typedef' of similar type are
+// CHECK-MESSAGES: :[[@LINE-2]]:33: note: the first parameter in this range is 'Read'
+// CHECK-MESSAGES: :[[@LINE-3]]:57: note: the last parameter in this range is 'Write'
+// CHECK-MESSAGES: :[[@LINE-4]]:39: note: after resolving type aliases, type of parameter 'Write' is 'const int &'
+// CHECK-MESSAGES: :[[@LINE-5]]: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 parameters for 'bind_power_and_multi_layer_typedef' of similar type are
+// CHECK-MESSAGES: :[[@LINE-2]]:46: note: the first parameter in this range is 'RP'
+// CHECK-MESSAGES: :[[@LINE-3]]:55: note: the last parameter in this range is 'WP'
+// CHECK-MESSAGES: :[[@LINE-4]]:50: note: after resolving type aliases, type of parameter 'WP' is 'const int * &'
+// CHECK-MESSAGES: :[[@LINE-5]]: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 parameters for 'multi_pack' of similar type ('StringRef') are
+// CHECK-MESSAGES: :[[@LINE-2]]:27: note: the first parameter in this range is 'Path'
+// CHECK-MESSAGES: :[[@LINE-3]]:43: note: the last parameter in this range is 'Name'
+// CHECK-MESSAGES: :[[@LINE-4]]:59: warning: 2 adjacent parameters for 'multi_pack' of similar type ('Toggle') are
+// CHECK-MESSAGES: :[[@LINE-5]]:66: note: the first parameter in this range is 'Read'
+// CHECK-MESSAGES: :[[@LINE-6]]:79: note: the last parameter in this range is 'Write'
+
+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 parameters for 'protochain' of similar type ('int') are
+// CHECK-MESSAGES: :[[@LINE-2]]:21: note: the first parameter in this range is 'i'
+// CHECK-MESSAGES: :[[@LINE-3]]:35: note: the last parameter in this range is 'k'
+
+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 parameters for 'protochain2' of similar type are
+// CHECK-MESSAGES: :[[@LINE-2]]:21: note: the first parameter in this range is 'i'
+// CHECK-MESSAGES: :[[@LINE-3]]:41: note: the last parameter in this range is 'k'
+// CHECK-MESSAGES: :[[@LINE-4]]:18: note: after resolving type aliases, type of parameter 'i' is 'int'
+// CHECK-MESSAGES: :[[@LINE-5]]:24: note: after resolving type aliases, type of parameter 'j' is 'int'
+// CHECK-MESSAGES: :[[@LINE-6]]:30: note: after resolving type aliases, type of parameter 'k' is 'int'
+
+void defined(int a, int b);
+void defined(int i, int j) {}
+// CHECK-MESSAGES: :[[@LINE-1]]:14: warning: 2 adjacent parameters for 'defined' of similar type ('int') are
+// CHECK-MESSAGES: :[[@LINE-2]]:18: note: the first parameter in this range is 'i'
+// CHECK-MESSAGES: :[[@LINE-3]]:25: note: the last parameter in this range is 'j'
+
+template <typename T>
+void ptr_pair(T *p, T *q) {}
+// CHECK-MESSAGES: :[[@LINE-1]]:15: warning: 2 adjacent parameters for 'ptr_pair' of similar type ('T *') are
+// CHECK-MESSAGES: :[[@LINE-2]]:18: note: the first parameter in this range is 'p'
+// CHECK-MESSAGES: :[[@LINE-3]]:24: note: the last parameter in this range is 'q'
+
+template <typename T>
+void cptr_pair(const T *p, const T *q) {}
+// CHECK-MESSAGES: :[[@LINE-1]]:16: warning: 2 adjacent parameters for 'cptr_pair' of similar type ('const T *') are
+// CHECK-MESSAGES: :[[@LINE-2]]:25: note: the first parameter in this range is 'p'
+// CHECK-MESSAGES: :[[@LINE-3]]:37: note: the last parameter in this range is 'q'
+
+void ref_and_typedef(int i, I2 i2) {}
+// CHECK-MESSAGES: :[[@LINE-1]]:22: warning: 2 adjacent parameters for 'ref_and_typedef' of similar type are
+// CHECK-MESSAGES: :[[@LINE-2]]:26: note: the first parameter in this range is 'i'
+// CHECK-MESSAGES: :[[@LINE-3]]:32: note: the last parameter in this range is 'i2'
+// CHECK-MESSAGES: :[[@LINE-4]]:22: note: after resolving type aliases, type of parameter 'i' is 'int'
+// CHECK-MESSAGES: :[[@LINE-5]]:29: note: after resolving type aliases, type of parameter 'i2' is 'int'
+
+void ref_and_typedef2(I1 i, int i2) {}
+// CHECK-MESSAGES: :[[@LINE-1]]:23: warning: 2 adjacent parameters for 'ref_and_typedef2' of similar type are
+// CHECK-MESSAGES: :[[@LINE-2]]:26: note: the first parameter in this range is 'i'
+// CHECK-MESSAGES: :[[@LINE-3]]:33: note: the last parameter in this range is 'i2'
+// CHECK-MESSAGES: :[[@LINE-4]]:23: note: after resolving type aliases, type of parameter 'i' is 'int'
+// CHECK-MESSAGES: :[[@LINE-5]]:29: note: after resolving type aliases, type of parameter 'i2' is 'int'
+
+int strcmp(const char *A, const char *B) {}
+// CHECK-MESSAGES: :[[@LINE-1]]:12: warning: 2 adjacent parameters for 'strcmp' of similar type ('const char *') are
+// CHECK-MESSAGES: :[[@LINE-2]]:24: note: the first parameter in this range is 'A'
+// CHECK-MESSAGES: :[[@LINE-3]]:39: note: the last parameter in this range is 'B'
+
+using str = char *;
+using strliteral = const char *;
+int str_compare(strliteral a, strliteral b) {}
+// CHECK-MESSAGES: :[[@LINE-1]]:17: warning: 2 adjacent parameters for 'str_compare' of similar type ('strliteral') are
+// CHECK-MESSAGES: :[[@LINE-2]]:28: note: the first parameter in this range is 'a'
+// CHECK-MESSAGES: :[[@LINE-3]]:42: note: the last parameter in this range is 'b'
+
+void append(str s, char *s2) {}
+// CHECK-MESSAGES: :[[@LINE-1]]:13: warning: 2 adjacent parameters for 'append' of similar type are
+// CHECK-MESSAGES: :[[@LINE-2]]:17: note: the first parameter in this range is 's'
+// CHECK-MESSAGES: :[[@LINE-3]]:26: note: the last parameter in this range is 's2'
+// CHECK-MESSAGES: :[[@LINE-4]]:13: note: after resolving type aliases, type of parameter 's' is 'char *'
+// CHECK-MESSAGES: :[[@LINE-5]]:20: note: after resolving type aliases, type of parameter 's2' is 'char *'
+
+void append_lit(str s, strliteral s2) {}
+// CHECK-MESSAGES: :[[@LINE-1]]:17: warning: 2 adjacent parameters for 'append_lit' of similar type are
+// CHECK-MESSAGES: :[[@LINE-2]]:21: note: the first parameter in this range is 's'
+// CHECK-MESSAGES: :[[@LINE-3]]:35: note: the last parameter in this range is 's2'
+// CHECK-MESSAGES: :[[@LINE-4]]:17: note: after resolving type aliases, type of parameter 's' is 'char *'
+// CHECK-MESSAGES: :[[@LINE-5]]:24: note: after resolving type aliases, type of parameter 's2' is 'const char *'
+
+void swap(str *a, char **b) {}
+// CHECK-MESSAGES: :[[@LINE-1]]:11: warning: 2 adjacent parameters for 'swap' of similar type are
+// CHECK-MESSAGES: :[[@LINE-2]]:16: note: the first parameter in this range is 'a'
+// CHECK-MESSAGES: :[[@LINE-3]]:26: note: the last parameter in this range is 'b'
+// CHECK-MESSAGES: :[[@LINE-4]]:11: note: after resolving type aliases, type of parameter 'a' is 'char * *'
+// CHECK-MESSAGES: :[[@LINE-5]]:19: note: after resolving type aliases, type of parameter '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 parameters for 'shortthing' of similar type ('I') are
+// CHECK-MESSAGES: :[[@LINE-2]]:19: note: the first parameter in this range is 'a'
+// CHECK-MESSAGES: :[[@LINE-3]]:24: note: the last parameter in this range is 'b'
+
+// 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: parameter 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: parameter 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: parameters 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 parameter 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 parameters.
+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 parameters.
+template <typename Comp>
+int compare(int x, int y, Comp C) { return C(x, y); }
+// CHECK-MESSAGES: :[[@LINE-1]]:13: warning: 2 adjacent parameters for 'compare' of similar type ('int') are
+// CHECK-MESSAGES: :[[@LINE-2]]:17: note: the first parameter in this range is 'x'
+// CHECK-MESSAGES: :[[@LINE-3]]:24: note: the last parameter in this range is 'y'
+
+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 parameters for 'Sum' of similar type ('int') are
+    // CHECK-MESSAGES: :[[@LINE-2]]:13: note: the first parameter in this range is '_i'
+    // CHECK-MESSAGES: :[[@LINE-3]]:21: note: the last parameter in this range is '_j'
+
+    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 parameters for 'len3' of similar type ('const StringRef') are
+// CHECK-MESSAGES: :[[@LINE-2]]:27: note: the first parameter in this range is 'Drive'
+// CHECK-MESSAGES: :[[@LINE-3]]:72: note: the last parameter in this range is 'FileName'
+
+void len4(const StringRef Drive, const StringRef Dirs, const StringRef FileName, const StringRef Extension) {}
+// CHECK-MESSAGES: :[[@LINE-1]]:11: warning: 4 adjacent parameters for 'len4' of similar type ('const StringRef') are
+// CHECK-MESSAGES: :[[@LINE-2]]:27: note: the first parameter in this range is 'Drive'
+// CHECK-MESSAGES: :[[@LINE-3]]:98: note: the last parameter in this range is 'Extension'
+
+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 parameters for 'call_two_programs' of similar type ('MainT *') are
+// CHECK-MESSAGES: :[[@LINE-2]]:29: note: the first parameter in this range is 'one'
+// CHECK-MESSAGES: :[[@LINE-3]]:40: note: the last parameter in this range is 'two'
+
+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 parameters for 'call_two_programs2' of similar type are
+// CHECK-MESSAGES: :[[@LINE-2]]:30: note: the first parameter in this range is 'p1'
+// CHECK-MESSAGES: :[[@LINE-3]]:41: note: the last parameter in this range is 'p2'
+// CHECK-MESSAGES: :[[@LINE-4]]:24: note: after resolving type aliases, type of parameter 'p1' is 'int (int, const char *, const char *)'
+// CHECK-MESSAGES: :[[@LINE-5]]:34: note: after resolving type aliases, type of parameter '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 parameters for 'int_equals' of similar type ('Integer') are
+// CHECK-MESSAGES: :[[@LINE-2]]:25: note: the first parameter in this range is 'i1'
+// CHECK-MESSAGES: :[[@LINE-3]]:37: note: the last parameter in this range is 'i2'
+
+typedef enum {
+  Exclamation,
+  Question,
+  Dot
+} Token;
+bool tok_equals(Token t1, Token t2) {}
+// CHECK-MESSAGES: :[[@LINE-1]]:17: warning: 2 adjacent parameters for 'tok_equals' of similar type ('Token') are
+// CHECK-MESSAGES: :[[@LINE-2]]:23: note: the first parameter in this range is 't1'
+// CHECK-MESSAGES: :[[@LINE-3]]:33: note: the last parameter in this range is 't2'
+
+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 parameters for 'fwd' of similar type ('Args &&...') are
+// CHECK-MESSAGES: :[[@LINE-2]]:21: note: the first parameter in this range is 'args'
+// CHECK-MESSAGES: :[[@LINE-3]]:38: note: the last parameter in this range is 'args2'
+
+// 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 parameters for 'gthread_create_random' of similar type ('void *(*)(void *)') are
+// CHECK-MESSAGES: :[[@LINE-4]]:35: note: the first parameter in this range is 'func_1'
+// CHECK-MESSAGES: :[[@LINE-4]]:35: note: the last parameter in this range is 'func_2'
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
@@ -152,6 +152,7 @@
    `cppcoreguidelines-special-member-functions <cppcoreguidelines-special-member-functions.html>`_,
    `darwin-avoid-spinlock <darwin-avoid-spinlock.html>`_,
    `darwin-dispatch-once-nonstatic <darwin-dispatch-once-nonstatic.html>`_, "Yes"
+   `experimental-cppcoreguidelines-avoid-adjacent-parameters-of-the-same-type <experimental-cppcoreguidelines-avoid-adjacent-parameters-of-the-same-type.html>`_,
    `fuchsia-default-arguments-calls <fuchsia-default-arguments-calls.html>`_,
    `fuchsia-default-arguments-declarations <fuchsia-default-arguments-declarations.html>`_, "Yes"
    `fuchsia-multiple-inheritance <fuchsia-multiple-inheritance.html>`_,
Index: clang-tools-extra/docs/clang-tidy/checks/experimental-cppcoreguidelines-avoid-adjacent-parameters-of-the-same-type.rst
===================================================================
--- /dev/null
+++ clang-tools-extra/docs/clang-tidy/checks/experimental-cppcoreguidelines-avoid-adjacent-parameters-of-the-same-type.rst
@@ -0,0 +1,150 @@
+.. title:: clang-tidy - experimental-cppcoreguidelines-avoid-adjacent-parameters-of-the-same-type
+
+experimental-cppcoreguidelines-avoid-adjacent-parameters-of-the-same-type
+=========================================================================
+
+Finds function definitions where parameters 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 parameters,
+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 *parameter 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 parameters found) are ignored:
+   ``It``, ``Iterator``, ``InputIt``, ``ForwardIt``, ``BidirIt``,
+   ``const_iterator``, ``ConstIterator``
+
+Options
+-------
+
+.. option:: MinimumLength
+
+    The minimum length of the adjacent parameter sequence required before a
+    diagnostic is emitted.
+    Defaults to `2`.
+    Should be any integer number that's at least 2.
+    If `0` or `1` is given, the check will run as if `2` was specified.
+
+.. option:: IgnoredNames
+
+    The list of parameter names that should not be considered part of a mixable
+    parameter range by the checker.
+    Parameters bearing ignored names will be skipped.
+    The value should be a ``;``-separated list of names.
+    The option is case-sensitive!
+
+    By default, the following, and their Uppercase-initial variants are ignored:
+    ``iterator``, ``begin``, ``end``, ``first``, ``last``.
+
+.. option:: IgnoredTypes
+
+    The list of parameter type suffices that should not be considered part of a
+    mixable parameter range by the checker.
+    Parameters of type bearing an ignored type name (or which suffix is found in
+    the list) will be skipped.
+    The value should be a ``;``-separated list of names.
+    The option is case-sensitive!
+
+    By default, the following, and their lowercase-initial variants are ignored:
+    ``It``, ``Iterator``, ``InputIt``, ``ForwardIt``, ``BidirIt``,
+    ``const_iterator``, ``ConstIterator``.
+
+.. option:: CVRMixPossible
+
+    Whether to consider parameters of type ``T`` and the qualified
+    ``const T``/``volatile T`` counterpart forming a common "adjacent
+    parameter" sequence.
+    If `0` (default value), the check assumes such parameters 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.
+As such, only the primary template definitions and explicit specialisations are
+checked.
+
+Furthermore, this check is not able to find if some parameters 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> *next) { /* ... */ }
+
+The following example will not be warned about, despite it should be, as the
+last parameters to the function in the example are of type ``const T &``.
+
+.. code-block:: c++
+
+    template <typename T>
+    struct vector {
+        typedef       T          element_type;
+        typedef const T    const_element_type;
+        typedef       T &        reference_type;
+        typedef 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(
+        const          vector<T>                       & Vector,
+              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
@@ -75,6 +75,7 @@
 
 New checks
 ^^^^^^^^^^
+
 - New :doc:`cppcoreguidelines-avoid-non-const-global-variables
   <clang-tidy/checks/cppcoreguidelines-avoid-non-const-global-variables>` check.
   Finds non-const global variables as described in check I.2 of C++ Core
@@ -113,6 +114,14 @@
   Flags use of the `C` standard library functions ``memset``, ``memcpy`` and
   ``memcmp`` and similar derivatives on non-trivial types.
 
+- New :doc:`experimental-cppcoreguidelines-avoid-adjacent-parameters-of-the-same-type
+  <clang-tidy/checks/experimental-cppcoreguidelines-avoid-adjacent-parameters-of-the-same-type>`
+  check.
+
+  Finds function definitions where parameters of the same type follow each other
+  directly, making call sites prone to calling the function with swapped or badly
+  ordered arguments.
+
 - New :doc:`llvmlibc-implementation-in-namespace
   <clang-tidy/checks/llvmlibc-implementation-in-namespace>` check.
 
Index: clang-tools-extra/clang-tidy/experimental/ExperimentalTidyModule.cpp
===================================================================
--- clang-tools-extra/clang-tidy/experimental/ExperimentalTidyModule.cpp
+++ clang-tools-extra/clang-tidy/experimental/ExperimentalTidyModule.cpp
@@ -9,6 +9,7 @@
 #include "../ClangTidy.h"
 #include "../ClangTidyModule.h"
 #include "../ClangTidyModuleRegistry.h"
+#include "CppcoreguidelinesAvoidAdjacentParametersOfTheSameTypeCheck.h"
 
 namespace clang {
 namespace tidy {
@@ -19,7 +20,10 @@
 class ExperimentalModule : public ClangTidyModule {
 public:
   void addCheckFactories(ClangTidyCheckFactories &CheckFactories) override {
-
+    CheckFactories.registerCheck<
+        CppcoreguidelinesAvoidAdjacentParametersOfTheSameTypeCheck>(
+        "experimental-cppcoreguidelines-avoid-adjacent-parameters-of-the-same-"
+        "type");
   }
 };
 
Index: clang-tools-extra/clang-tidy/experimental/CppcoreguidelinesAvoidAdjacentParametersOfTheSameTypeCheck.h
===================================================================
--- /dev/null
+++ clang-tools-extra/clang-tidy/experimental/CppcoreguidelinesAvoidAdjacentParametersOfTheSameTypeCheck.h
@@ -0,0 +1,57 @@
+//== CppcoreguidelinesAvoidAdjacentParametersOfTheSameTypeCheck.h 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
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_EXPERIMENTAL_CPPCOREGUIDELINESAVOIDADJACENTPARAMETERSOFTHESAMETYPECHECK_H
+#define LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_EXPERIMENTAL_CPPCOREGUIDELINESAVOIDADJACENTPARAMETERSOFTHESAMETYPECHECK_H
+
+#include "../ClangTidyCheck.h"
+
+namespace clang {
+namespace tidy {
+namespace experimental {
+
+/// Finds function definitions where parameters 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/experimental-cppcoreguidelines-avoid-adjacent-parameters-of-the-same-type.html
+class CppcoreguidelinesAvoidAdjacentParametersOfTheSameTypeCheck : public ClangTidyCheck {
+public:
+  CppcoreguidelinesAvoidAdjacentParametersOfTheSameTypeCheck(
+      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;
+
+  /// Returns whether the given parameter is ignored based on its type name
+  /// or parameter name.
+  bool isIgnored(const ParmVarDecl *Node) const;
+
+  /// The minimum length of an adjacent range required to have to produce
+  /// a diagnostic.
+  const unsigned MinimumLength;
+
+  /// The parameter names (as written in the code) to be ignored when
+  /// checking for adjacent parameter ranges.
+  const std::vector<std::string> IgnoredParamNames;
+
+  /// The parameter type name suffixes (as written in the code) to be ignored
+  /// when checking for adjacent parameter ranges.
+  const std::vector<std::string> IgnoredParamTypes;
+
+  /// Whether to consider 'T' and 'const T'/'volatile T'/etc. arguments to be
+  /// possible mixup.
+  const bool CVRMixPossible;
+};
+
+} // namespace experimental
+} // namespace tidy
+} // namespace clang
+
+#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_EXPERIMENTAL_CPPCOREGUIDELINESAVOIDADJACENTPARAMETERSOFTHESAMETYPECHECK_H
Index: clang-tools-extra/clang-tidy/experimental/CppcoreguidelinesAvoidAdjacentParametersOfTheSameTypeCheck.cpp
===================================================================
--- /dev/null
+++ clang-tools-extra/clang-tidy/experimental/CppcoreguidelinesAvoidAdjacentParametersOfTheSameTypeCheck.cpp
@@ -0,0 +1,619 @@
+// CppcoreguidelinesAvoidAdjacentParametersOfTheSameTypeCheck.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 "CppcoreguidelinesAvoidAdjacentParametersOfTheSameTypeCheck.h"
+#include "../utils/OptionsUtils.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>
+
+using namespace clang::ast_matchers;
+
+namespace clang {
+namespace tidy {
+namespace experimental {
+
+namespace {
+
+/// Annotates a possible mix of parameters based on which language construct
+/// is responsible for allowing thee mix to happen. This is a bitflag.
+enum MixupTag : unsigned char {
+  MIXUP_Invalid = 0, //< Sentinel 0 bit pattern value for masking. DO NOT USE!
+
+// Set the bit at index N to 1 as the enum constant. N = 0 is invalid.
+#define BIT(Name, N) MIXUP_##Name = (1ull << (N##ull - 1ull))
+  BIT(None, 1),    //< Mixup is not possible.
+  BIT(Trivial, 2), //< No extra information needed.
+  BIT(Typedef, 3), //< Parameter of a typedef which resolves to an effective
+                   //< desugared type same as the other arg.
+  BIT(RefBind, 4), //< Parameter mixes with another due to reference binding.
+  BIT(CVR, 5),     //< Parameter mixes with another through implicit
+                   //< qualification.
+#undef BIT
+
+  LLVM_MARK_AS_BITMASK_ENUM(/* LargestValue = */ MIXUP_CVR)
+};
+LLVM_ENABLE_BITMASK_ENUMS_IN_NAMESPACE();
+
+/// A named tuple that contains which parameter with which other parameter
+/// can be mixed up in what fashion.
+struct Mixup {
+  const ParmVarDecl *First, *Second;
+  MixupTag Flags;
+
+  Mixup(const ParmVarDecl *A, const ParmVarDecl *B, MixupTag Flags)
+      : First(A), Second(B), Flags(Flags) {}
+
+  Mixup operator|(MixupTag EnableFlags) const {
+    return {First, Second, Flags | EnableFlags};
+  }
+  Mixup &operator|=(MixupTag EnableFlags) {
+    Flags |= EnableFlags;
+    return *this;
+  }
+
+  /// Sanitises the Mixup's flags so it doesn't contain contradictory bits.
+  void sanitise() {
+    assert(Flags != MIXUP_Invalid &&
+           "Mixup tag had full zero bit pattern value!");
+
+    if (Flags & MIXUP_None) {
+      // If at any point the checks mark the mixup impossible, it is just simply
+      // impossible.
+      Flags = MIXUP_None;
+      return;
+    }
+
+    if (Flags == MIXUP_Trivial)
+      return;
+
+    if (Flags ^ MIXUP_Trivial)
+      // If any other bits than Trivial is set, unset Trivial, so only the
+      // annotation bits warranting extra diagnostic are set.
+      Flags &= ~MIXUP_Trivial;
+  }
+};
+
+static_assert(std::is_trivially_copyable<Mixup>::value,
+              "keep Mixup trivially copyable!");
+
+/// Represents a (closed) range of adjacent parameters that can be mixed up at
+/// a call site.
+struct MixableAdjacentParamRange {
+  std::size_t NumParamsChecked = 0;
+  llvm::SmallVector<Mixup, 8> Mixups;
+
+  const ParmVarDecl *getFirstParm() const {
+    // The first element added to the mixup vector has the left hand
+    // side set to the first argument in the range, if any is added.
+    assert(!Mixups.empty());
+    return Mixups.front().First;
+  }
+
+  const ParmVarDecl *getLastParm() const {
+    // There builder function paramEqualTypeRange breaks building an instance of
+    // this type if it finds something that can not be mixed up, by going
+    // *forward* in the list of arguments. So at the moment of break, the right
+    // hand side of the last conversion in the vector is the last argument in
+    // the adjacency range.
+    assert(!Mixups.empty());
+    return Mixups.back().Second;
+  }
+};
+
+} // namespace
+
+/// Returns whether an lvalue reference refers to the same type as T.
+static MixupTag RefBindsToSameType(const LValueReferenceType *LRef,
+                                   const Type *T, bool CVRMixPossible);
+
+/// 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) {
+  if (LType == RType)
+    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);
+
+  // 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()) {
+      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()) {
+      MixupTag RefBind = RefBindsToSameType(RType->getAs<LValueReferenceType>(),
+                                            LType.getTypePtr(), CVRMixPossible);
+      return RefBind | MIXUP_RefBind;
+    }
+  }
+
+  // A parameter 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 (LType.getLocalCVRQualifiers() || RType.getLocalCVRQualifiers()) {
+    if (!CVRMixPossible)
+      return MIXUP_None;
+
+    return HowPossibleToMixUpAtCallSite(LType.getUnqualifiedType(),
+                                        RType.getUnqualifiedType(), Ctx,
+                                        CVRMixPossible) |
+           MIXUP_CVR;
+  }
+
+  {
+    const auto *LTypedef = LType->getAs<TypedefType>();
+    const auto *RTypedef = RType->getAs<TypedefType>();
+    if (LTypedef && RTypedef)
+      return MIXUP_Typedef | HowPossibleToMixUpAtCallSite(LTypedef->desugar(),
+                                                          RTypedef->desugar(),
+                                                          Ctx, CVRMixPossible);
+    if (LTypedef)
+      return MIXUP_Typedef |
+             HowPossibleToMixUpAtCallSite(LTypedef->desugar(), RType, Ctx,
+                                          CVRMixPossible);
+    if (RTypedef)
+      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.)
+    return HowPossibleToMixUpAtCallSite(
+        LType->getPointeeType(), RType->getPointeeType(), Ctx, CVRMixPossible);
+
+  // A parameter 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()))
+    return MIXUP_CVR | HowPossibleToMixUpAtCallSite(LType.getUnqualifiedType(),
+                                                    RType, Ctx, CVRMixPossible);
+  if (CVRMixPossible &&
+      (RType.isLocalConstQualified() || RType.isLocalVolatileQualified()))
+    return MIXUP_CVR |
+           HowPossibleToMixUpAtCallSite(LType, RType.getUnqualifiedType(), Ctx,
+                                        CVRMixPossible);
+
+  return MIXUP_None;
+}
+
+static MixupTag RefBindsToSameType(const LValueReferenceType *LRef,
+                                   const Type *T, bool CVRMixPossible) {
+  const QualType ReferredType = LRef->getPointeeType();
+  if (!ReferredType.isLocalConstQualified())
+    // A non-const reference doesn't bind with the same power as a "normal"
+    // by-value parameter.
+    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.
+    return HowPossibleToMixUpAtCallSite(TypedefTy->desugar(), QualType{T, 0},
+                                        TypedefTy->getDecl()->getASTContext(),
+                                        CVRMixPossible);
+
+  return ReferredType.getTypePtr() == T ? MIXUP_Trivial : MIXUP_None;
+}
+
+/// Gets the mixable range of the parameters of F starting with the param at
+/// index StartIdx.
+static MixableAdjacentParamRange BuildMixableParameterRange(
+    const CppcoreguidelinesAvoidAdjacentParametersOfTheSameTypeCheck &Checker,
+    const FunctionDecl *F, unsigned int StartIdx) {
+  MixableAdjacentParamRange MixRange;
+  const unsigned int ParamCount = F->getNumParams();
+  assert(StartIdx < ParamCount && "invalid start index given!");
+  const ASTContext &Ctx = F->getASTContext();
+  const ParmVarDecl *First = F->getParamDecl(StartIdx);
+
+  // A parameter (the one at StartIdx) was checked.
+  MixRange.NumParamsChecked = 1;
+
+  // Try checking parameters of the function from StartIdx until the range
+  // breaks. The range contains parameters that are mutually mixable with each
+  // other.
+  for (unsigned int I = StartIdx + 1; I < ParamCount; ++I) {
+    const ParmVarDecl *Ith = F->getParamDecl(I);
+    if (Checker.isIgnored(Ith))
+      // If the next parameter in the range is ignored, break the range.
+      break;
+
+    bool AnyMixupStored = false;
+    for (unsigned int J = StartIdx; J < I; ++J) {
+      const ParmVarDecl *Jth = F->getParamDecl(J);
+      Mixup M{Jth, Ith,
+              HowPossibleToMixUpAtCallSite(Jth->getType(), Ith->getType(), Ctx,
+                                           Checker.CVRMixPossible)};
+      M.sanitise();
+      assert(M.Flags != MIXUP_Invalid &&
+             "Bits fell off, result is sentinel value.");
+
+      if (M.Flags != MIXUP_None) {
+        MixRange.Mixups.emplace_back(M);
+        AnyMixupStored = true;
+      }
+    }
+
+    if (!AnyMixupStored)
+      // If there is no "new" mixup possibility for the Ith param, it signifies
+      // the end of range.
+      break;
+
+    // If the loop did not break earlier, another parameter was found to be
+    // mixable.
+    ++MixRange.NumParamsChecked;
+  }
+
+  return MixRange;
+}
+
+/// 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) {
+  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().str();
+    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().str();
+
+    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();
+  else if (const auto *FunctionPtrTy = dyn_cast<FunctionProtoType>(Ty)) {
+    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() << '>';
+}
+
+static std::string TypeNameAsString(const QualType QT,
+                                    const PrintingPolicy &PP) {
+  std::string Ret;
+  {
+    llvm::raw_string_ostream OS{Ret};
+    PutTypeName(QT, OS, PP);
+  }
+
+  if (Ret.empty())
+    Ret = "<unprintable type?>";
+  return Ret;
+}
+
+static const std::string DefaultIgnoredParamNames =
+    utils::options::serializeStringList({"iterator", "Iterator", "begin",
+                                         "Begin", "end", "End", "first",
+                                         "First", "last", "Last"});
+
+static const std::string DefaultIgnoredParamTypes =
+    utils::options::serializeStringList(
+        {"it", "It", "iterator", "Iterator", "inputit", "InputIt", "forwardit",
+         "ForwardIt", "bidirit", "BidirIt", "constiterator", "const_iterator",
+         "Const_Iterator", "Constiterator", "ConstIterator"});
+
+bool CppcoreguidelinesAvoidAdjacentParametersOfTheSameTypeCheck::isIgnored(
+    const ParmVarDecl *Node) const {
+  if (!Node->getIdentifier())
+    return false;
+
+  StringRef NodeName = Node->getName();
+  if (llvm::any_of(IgnoredParamNames,
+                   [&NodeName](const std::string &IgnoredName) {
+                     return NodeName == IgnoredName;
+                   }))
+    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(IgnoredParamTypes, [&NodeName, &NodeTypeName](
+                                            const std::string &IgnoredName) {
+          if (NodeTypeName == IgnoredName)
+            return true;
+
+          std::size_t IgnoredNameLen = IgnoredName.length();
+          if (NodeTypeName.length() > IgnoredNameLen &&
+              NodeTypeName.compare(NodeTypeName.length() - IgnoredNameLen,
+                                   IgnoredNameLen, IgnoredName) == 0)
+            return true;
+
+          return false;
+        }))
+      return true;
+  }
+
+  return false;
+}
+
+AST_MATCHER_P(FunctionDecl, hasAtLeastNumParams, unsigned int, N) {
+  return Node.getNumParams() >= N;
+}
+
+static constexpr unsigned DefaultMinLength = 2;
+
+/// A range length of 0 or 1 is useless as it would simply report for every
+/// parameter. Fix the user's input if bad value is given.
+static unsigned ClampMinimumLength(const unsigned Option) {
+  return Option < 2 ? DefaultMinLength : Option;
+}
+
+CppcoreguidelinesAvoidAdjacentParametersOfTheSameTypeCheck::
+    CppcoreguidelinesAvoidAdjacentParametersOfTheSameTypeCheck(
+        StringRef Name, ClangTidyContext *Context)
+    : ClangTidyCheck(Name, Context),
+      MinimumLength(
+          ClampMinimumLength(Options.get("MinimumLength", DefaultMinLength))),
+      IgnoredParamNames(utils::options::parseStringList(
+          Options.get("IgnoredNames", DefaultIgnoredParamNames))),
+      IgnoredParamTypes(utils::options::parseStringList(
+          Options.get("IgnoredTypes", DefaultIgnoredParamTypes))),
+      CVRMixPossible(Options.get("CVRMixPossible", false)) {}
+
+void CppcoreguidelinesAvoidAdjacentParametersOfTheSameTypeCheck::storeOptions(
+    ClangTidyOptions::OptionMap &Opts) {
+  Options.store(Opts, "MinimumLength", MinimumLength);
+  Options.store(Opts, "IgnoredNames",
+                utils::options::serializeStringList(IgnoredParamNames));
+  Options.store(Opts, "IgnoredTypes",
+                utils::options::serializeStringList(IgnoredParamTypes));
+  Options.store(Opts, "CVRMixPossible", CVRMixPossible);
+}
+
+void CppcoreguidelinesAvoidAdjacentParametersOfTheSameTypeCheck::
+    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(), hasAtLeastNumParams(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(), hasAtLeastNumParams(MinimumLength),
+                         isExplicitTemplateSpecialization())
+                         .bind("fun"),
+                     this);
+}
+
+/// Returns whether the given Mixup, when diagnosed, should elaborate the type
+/// of the arguments involved.
+static bool NeedsToPrintType(const Mixup &M) {
+  return M.Flags & (MIXUP_Typedef | MIXUP_RefBind | MIXUP_CVR);
+}
+
+void CppcoreguidelinesAvoidAdjacentParametersOfTheSameTypeCheck::check(
+    const MatchFinder::MatchResult &Result) {
+  const auto *Fun = Result.Nodes.getNodeAs<FunctionDecl>("fun");
+
+  unsigned int ParamMixRangeStartIdx = 0;
+  const unsigned int NumArgs = Fun->getNumParams();
+
+  while (ParamMixRangeStartIdx < NumArgs) {
+    if (isIgnored(Fun->getParamDecl(ParamMixRangeStartIdx))) {
+      // If the current parameters's name or type name is ignored, don't try
+      // creating a range from it.
+      ++ParamMixRangeStartIdx;
+      continue;
+    }
+
+    MixableAdjacentParamRange MixingRange =
+        BuildMixableParameterRange(*this, Fun, ParamMixRangeStartIdx);
+    assert(MixingRange.NumParamsChecked > 0 && "Ensure continuity!");
+    ParamMixRangeStartIdx += MixingRange.NumParamsChecked;
+    if (MixingRange.NumParamsChecked < 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 MainParmTypeAsWritten =
+        MixingRange.getFirstParm()->getType().getAsString(PP);
+
+    bool HasAnyTypePrint = llvm::any_of(MixingRange.Mixups, NeedsToPrintType);
+
+    {
+      const ParmVarDecl *RangeFirst = MixingRange.getFirstParm();
+      const ParmVarDecl *RangeLast = MixingRange.getLastParm();
+
+      {
+        StringRef MainDiagnostic;
+        if (HasAnyTypePrint)
+          MainDiagnostic = "%0 adjacent parameters for '%1' of similar type "
+                           "are easily swapped "
+                           "by mistake";
+        else
+          MainDiagnostic = "%0 adjacent parameters for '%1' of similar type "
+                           "('%2') are easily "
+                           "swapped by mistake";
+
+        auto Diag = diag(RangeFirst->getOuterLocStart(), MainDiagnostic)
+                    << static_cast<unsigned>(MixingRange.NumParamsChecked)
+                    << FunName;
+        if (!HasAnyTypePrint)
+          Diag << MainParmTypeAsWritten;
+      }
+
+      // Unfortunately, undersquiggly highlights of ranges is not supported by
+      // ClangTidyDiagnosticConsumer without a fake fixit...
+      diag(RangeFirst->getLocation(),
+           "the first parameter in this range is '%0'", DiagnosticIDs::Note)
+          << (!RangeFirst->getName().empty() ? RangeFirst->getName()
+                                             : "<unnamed>");
+      diag(RangeLast->getLocation(), "the last parameter in this range is '%0'",
+           DiagnosticIDs::Note)
+          << (!RangeLast->getName().empty() ? RangeLast->getName()
+                                            : "<unnamed>");
+    }
+
+    llvm::SmallPtrSet<const ParmVarDecl *, 4> TypedefResolutionPrintedForParm;
+    llvm::SmallPtrSet<const ParmVarDecl *, 4> CVRNotePrintedForParm;
+    for (const Mixup &M : MixingRange.Mixups) {
+      assert(M.Flags >= MIXUP_Trivial && "Too low bits in mixup type.");
+      // For MIXUP_Trivial no extra diagnostics required.
+
+      std::string FirstParmType, SecondParmType;
+      if (NeedsToPrintType(M)) {
+        // Typedefs, and reference binds might result in the type of a variable
+        // printed in the diagnostic, so we have to prepare it.
+        FirstParmType = TypeNameAsString(M.First->getType(), PP);
+        SecondParmType = TypeNameAsString(M.Second->getType(), PP);
+      }
+
+      if (NeedsToPrintType(M)) {
+        if (M.Flags & MIXUP_Typedef) {
+          // FIXME: Don't emit the typedef note for the parameter that isn't
+          // actually a typedef.
+          if (!TypedefResolutionPrintedForParm.count(M.First)) {
+            diag(M.First->getOuterLocStart(),
+                 "after resolving type aliases, type of parameter '%0' is '%1'",
+                 DiagnosticIDs::Note)
+                << M.First->getName() << FirstParmType;
+            TypedefResolutionPrintedForParm.insert(M.First);
+          }
+
+          if (!TypedefResolutionPrintedForParm.count(M.Second)) {
+            diag(M.Second->getOuterLocStart(),
+                 "after resolving type aliases, type of parameter '%0' is '%1'",
+                 DiagnosticIDs::Note)
+                << M.Second->getName() << SecondParmType;
+            TypedefResolutionPrintedForParm.insert(M.Second);
+          }
+        }
+
+        if (M.Flags & (MIXUP_RefBind | MIXUP_CVR)) {
+          if (!CVRNotePrintedForParm.count(M.Second)) {
+            diag(M.Second->getOuterLocStart(),
+                 "at a call site, '%0' might bind with same force as '%1'",
+                 DiagnosticIDs::Note)
+                << SecondParmType << MainParmTypeAsWritten;
+            CVRNotePrintedForParm.insert(M.Second);
+          }
+        }
+      }
+    }
+  }
+}
+
+} // namespace experimental
+} // namespace tidy
+} // namespace clang
Index: clang-tools-extra/clang-tidy/experimental/CMakeLists.txt
===================================================================
--- clang-tools-extra/clang-tidy/experimental/CMakeLists.txt
+++ clang-tools-extra/clang-tidy/experimental/CMakeLists.txt
@@ -1,6 +1,7 @@
 set(LLVM_LINK_COMPONENTS support)
 
 add_clang_library(clangTidyExperimentalModule
+  CppcoreguidelinesAvoidAdjacentParametersOfTheSameTypeCheck.cpp
   ExperimentalTidyModule.cpp
 
   LINK_LIBS
_______________________________________________
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
  • [PATCH] D69560: [clang-tidy] Ad... Whisperity via Phabricator via cfe-commits

Reply via email to