whisperity updated this revision to Diff 259324.
whisperity retitled this revision from "[clang-tidy] Approximate implicit 
conversion issues for the 
'experimental-cppcoreguidelines-avoid-adjacent-arguments-of-the-same-type' 
check" to "[clang-tidy] Approximate implicit conversion issues in 
'experimental-cppcoreguidelines-avoid-adjacent-arguments-of-the-same-type'".
whisperity added a comment.

- Re-organised code, removed debug prints, rebased, the usual tidy-up.
- Bug fixes on certain crashes like incomplete types, conversion operator 
templates, etc.
- Even more bug fixes against crashes, hopefully... sorry I lost the individual 
commits in a `squash` I think


Repository:
  rG LLVM Github Monorepo

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

https://reviews.llvm.org/D75041

Files:
  
clang-tools-extra/clang-tidy/experimental/CppcoreguidelinesAvoidAdjacentParametersOfTheSameTypeCheck.cpp
  
clang-tools-extra/clang-tidy/experimental/CppcoreguidelinesAvoidAdjacentParametersOfTheSameTypeCheck.h
  
clang-tools-extra/docs/clang-tidy/checks/experimental-cppcoreguidelines-avoid-adjacent-parameters-of-the-same-type.rst
  
clang-tools-extra/test/clang-tidy/checkers/experimental-cppcoreguidelines-avoid-adjacent-parameters-of-the-same-type-implicits.c
  
clang-tools-extra/test/clang-tidy/checkers/experimental-cppcoreguidelines-avoid-adjacent-parameters-of-the-same-type-implicits.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
===================================================================
--- clang-tools-extra/test/clang-tidy/checkers/experimental-cppcoreguidelines-avoid-adjacent-parameters-of-the-same-type.c
+++ clang-tools-extra/test/clang-tidy/checkers/experimental-cppcoreguidelines-avoid-adjacent-parameters-of-the-same-type.c
@@ -32,3 +32,6 @@
 // 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'
+
+void ptrs(int *i, long *l) {}
+// NO-WARN: Mixing fundamentals' pointers is diagnosed by compiler warnings.
Index: 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-minlen3.cpp
+++ clang-tools-extra/test/clang-tidy/checkers/experimental-cppcoreguidelines-avoid-adjacent-parameters-of-the-same-type-minlen3.cpp
@@ -203,3 +203,20 @@
 // 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'
+
+// NO-WARN: Implicit conversions should not warn if the check option is turned off.
+
+void integral1(signed char sc, int si) {}
+
+struct FromInt {
+  FromInt(int);
+};
+void userConv1(int i, FromInt fi) {}
+
+struct Base {};
+struct Derived1 : Base {};
+struct Derived2 : Base {};
+void upcasting(Base *bp, Derived1 *d1p, Derived2 *d2p) {}
+void upcasting_ref(const Base &br, const Derived1 &d1r, const Derived2 &d2r) {}
+
+// END of NO-WARN.
Index: clang-tools-extra/test/clang-tidy/checkers/experimental-cppcoreguidelines-avoid-adjacent-parameters-of-the-same-type-implicits.cpp
===================================================================
--- /dev/null
+++ clang-tools-extra/test/clang-tidy/checkers/experimental-cppcoreguidelines-avoid-adjacent-parameters-of-the-same-type-implicits.cpp
@@ -0,0 +1,362 @@
+// 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.ImplicitConversion, value: 1} \
+// RUN:   ]}' --
+
+void compare(int a, int b, int c) {}
+// CHECK-MESSAGES: :[[@LINE-1]]:14: warning: 3 adjacent parameters for 'compare' of similar type ('int') are easily swapped by mistake [experimental-cppcoreguidelines-avoid-adjacent-parameters-of-the-same-type]
+// CHECK-MESSAGES: :[[@LINE-2]]:18: note: the first parameter in this range is 'a'
+// CHECK-MESSAGES: :[[@LINE-3]]:32: note: the last parameter in this range is 'c'
+
+void b(bool b1, bool b2, int i) {}
+// CHECK-MESSAGES: :[[@LINE-1]]:8: warning: 3 adjacent parameters for 'b' of convertible types may be easily swapped by mistake [experimental-cppcoreguidelines-avoid-adjacent-parameters-of-the-same-type]
+// CHECK-MESSAGES: :[[@LINE-2]]:13: note: the first parameter in this range is 'b1'
+// CHECK-MESSAGES: :[[@LINE-3]]:30: note: the last parameter in this range is 'i'
+// CHECK-MESSAGES: :[[@LINE-4]]:26: note: 'bool' and 'int' can suffer implicit conversions between one another{{$}}
+
+void integral1(signed char sc, int si) {}
+// CHECK-MESSAGES: :[[@LINE-1]]:16: warning: 2 adjacent parameters for 'integral1' of convertible
+// CHECK-MESSAGES: :[[@LINE-2]]:28: note: the first parameter in this range is 'sc'
+// CHECK-MESSAGES: :[[@LINE-3]]:36: note: the last parameter in this range is 'si'
+// CHECK-MESSAGES: :[[@LINE-4]]:32: note: 'signed char' and 'int' can suffer implicit conversions between one another{{$}}
+
+void integral2(long l, int i) {}
+// CHECK-MESSAGES: :[[@LINE-1]]:16: warning: 2 adjacent parameters for 'integral2' of convertible
+// CHECK-MESSAGES: :[[@LINE-2]]:21: note: the first parameter in this range is 'l'
+// CHECK-MESSAGES: :[[@LINE-3]]:28: note: the last parameter in this range is 'i'
+// CHECK-MESSAGES: :[[@LINE-4]]:24: note: 'long' and 'int' can suffer implicit conversions between one another{{$}}
+
+void integral3(int i, long l) {}
+// CHECK-MESSAGES: :[[@LINE-1]]:16: warning: 2 adjacent parameters for 'integral3' of convertible
+// CHECK-MESSAGES: :[[@LINE-2]]:20: note: the first parameter in this range is 'i'
+// CHECK-MESSAGES: :[[@LINE-3]]:28: note: the last parameter in this range is 'l'
+// CHECK-MESSAGES: :[[@LINE-4]]:23: note: 'int' and 'long' can suffer implicit conversions between one another{{$}}
+
+void integral4(char c, int i, long l) {}
+// CHECK-MESSAGES: :[[@LINE-1]]:16: warning: 3 adjacent parameters for 'integral4' of convertible
+// CHECK-MESSAGES: :[[@LINE-2]]:21: note: the first parameter in this range is 'c'
+// CHECK-MESSAGES: :[[@LINE-3]]:36: note: the last parameter in this range is 'l'
+// CHECK-MESSAGES: :[[@LINE-4]]:24: note: 'char' and 'int' can suffer implicit conversions between one another{{$}}
+// CHECK-MESSAGES: :[[@LINE-5]]:31: note: 'char' and 'long' can suffer implicit conversions between one another{{$}}
+
+void floating1(float f, double d) {}
+// CHECK-MESSAGES: :[[@LINE-1]]:16: warning: 2 adjacent parameters for 'floating1' of convertible
+// CHECK-MESSAGES: :[[@LINE-2]]:22: note: the first parameter in this range is 'f'
+// CHECK-MESSAGES: :[[@LINE-3]]:32: note: the last parameter in this range is 'd'
+// CHECK-MESSAGES: :[[@LINE-4]]:25: note: 'float' and 'double' can suffer implicit conversions between one another{{$}}
+
+typedef double D;
+void floating2(float f, D d) {}
+// CHECK-MESSAGES: :[[@LINE-1]]:16: warning: 2 adjacent parameters for 'floating2' of convertible
+// CHECK-MESSAGES: :[[@LINE-2]]:22: note: the first parameter in this range is 'f'
+// CHECK-MESSAGES: :[[@LINE-3]]:27: note: the last parameter in this range is 'd'
+// CHECK-MESSAGES: :[[@LINE-4]]:16: note: after resolving type aliases, type of parameter 'f' is 'float'
+// CHECK-MESSAGES: :[[@LINE-5]]:25: note: after resolving type aliases, type of parameter 'd' is 'double'
+// CHECK-MESSAGES: :[[@LINE-6]]:25: note: 'float' and 'double' can suffer implicit conversions between one another{{$}}
+
+void floatToInt(float f, int i) {}
+// CHECK-MESSAGES: :[[@LINE-1]]:17: warning: 2 adjacent parameters for 'floatToInt' of convertible
+// CHECK-MESSAGES: :[[@LINE-2]]:23: note: the first parameter in this range is 'f'
+// CHECK-MESSAGES: :[[@LINE-3]]:30: note: the last parameter in this range is 'i'
+// CHECK-MESSAGES: :[[@LINE-4]]:26: note: 'float' and 'int' can suffer implicit conversions between one another{{$}}
+
+enum En { A,
+          B };
+
+void unscopedEnumToIntegral(En e, int i) {}
+// CHECK-MESSAGES: :[[@LINE-1]]:29: warning: 2 adjacent parameters for 'unscopedEnumToIntegral' of convertible
+// CHECK-MESSAGES: :[[@LINE-2]]:32: note: the first parameter in this range is 'e'
+// CHECK-MESSAGES: :[[@LINE-3]]:39: note: the last parameter in this range is 'i'
+// CHECK-MESSAGES: :[[@LINE-4]]:35: note: 'En' can be implicitly converted to 'int'
+
+void unscopedEnumToIntegral2(En e, unsigned long long ull) {}
+// CHECK-MESSAGES: :[[@LINE-1]]:30: warning: 2 adjacent parameters for 'unscopedEnumToIntegral2' of convertible
+// CHECK-MESSAGES: :[[@LINE-2]]:33: note: the first parameter in this range is 'e'
+// CHECK-MESSAGES: :[[@LINE-3]]:55: note: the last parameter in this range is 'ull'
+// CHECK-MESSAGES: :[[@LINE-4]]:36: note: 'En' can be implicitly converted to 'unsigned long long'
+
+void unscopedEnum3(char c, En e, char c2) {}
+// CHECK-MESSAGES: :[[@LINE-1]]:20: warning: 3 adjacent parameters for 'unscopedEnum3' of convertible
+// CHECK-MESSAGES: :[[@LINE-2]]:25: note: the first parameter in this range is 'c'
+// CHECK-MESSAGES: :[[@LINE-3]]:39: note: the last parameter in this range is 'c2'
+// CHECK-MESSAGES: :[[@LINE-4]]:28: note: 'char' can be implicitly converted from 'En'
+// CHECK-MESSAGES: :[[@LINE-5]]:34: note: 'En' can be implicitly converted to 'char'
+
+void unscopedEnumToFloating(En e, long double ld) {}
+// CHECK-MESSAGES: :[[@LINE-1]]:29: warning: 2 adjacent parameters for 'unscopedEnumToFloating' of convertible
+// CHECK-MESSAGES: :[[@LINE-2]]:32: note: the first parameter in this range is 'e'
+// CHECK-MESSAGES: :[[@LINE-3]]:47: note: the last parameter in this range is 'ld'
+// CHECK-MESSAGES: :[[@LINE-4]]:35: note: 'En' can be implicitly converted to 'long double'
+
+void unscopedEnumToIntOrFloat(En e, int i, float f) {}
+// CHECK-MESSAGES: :[[@LINE-1]]:31: warning: 3 adjacent parameters for 'unscopedEnumToIntOrFloat' of convertible
+// CHECK-MESSAGES: :[[@LINE-2]]:34: note: the first parameter in this range is 'e'
+// CHECK-MESSAGES: :[[@LINE-3]]:50: note: the last parameter in this range is 'f'
+// CHECK-MESSAGES: :[[@LINE-4]]:37: note: 'En' can be implicitly converted to 'int'
+// CHECK-MESSAGES: :[[@LINE-5]]:44: note: 'En' can be implicitly converted to 'float'
+// CHECK-MESSAGES: :[[@LINE-6]]:44: note: 'int' and 'float' can suffer implicit conversions between one another{{$}}
+
+enum class SEn { C,
+                 D };
+void scopedEnumToIntegral(SEn e, int i) {}
+// NO-WARN: Scoped enumerations mustn't be promoted.
+
+struct FromInt {
+  FromInt(int);
+};
+
+void userConv1(int i, FromInt fi) {}
+// CHECK-MESSAGES: :[[@LINE-1]]:16: warning: 2 adjacent parameters for 'userConv1' of convertible
+// CHECK-MESSAGES: :[[@LINE-2]]:20: note: the first parameter in this range is 'i'
+// CHECK-MESSAGES: :[[@LINE-3]]:31: note: the last parameter in this range is 'fi'
+// CHECK-MESSAGES: :[[@LINE-4]]:23: note: 'int' can be implicitly converted to 'FromInt'
+
+void userConv1c(int i, const FromInt cfi) {}
+// NO-WARN: CVRMixPossible is set to 0, so an 'int' and a 'const "int"' does not mix.
+
+void userConv1cr(int i, const FromInt &cfir) {}
+// CHECK-MESSAGES: :[[@LINE-1]]:18: warning: 2 adjacent parameters for 'userConv1cr' of convertible
+// CHECK-MESSAGES: :[[@LINE-2]]:22: note: the first parameter in this range is 'i'
+// CHECK-MESSAGES: :[[@LINE-3]]:40: note: the last parameter in this range is 'cfir'
+// CHECK-MESSAGES: :[[@LINE-4]]:25: note: at a call site, 'const FromInt &' might bind with same force as 'int'
+// CHECK-MESSAGES: :[[@LINE-5]]:25: note: 'int' can be implicitly converted to 'const FromInt &'
+
+void userConv2(unsigned long long ull, FromInt fi) {}
+// CHECK-MESSAGES: :[[@LINE-1]]:16: warning: 2 adjacent parameters for 'userConv2' of convertible
+// CHECK-MESSAGES: :[[@LINE-2]]:35: note: the first parameter in this range is 'ull'
+// CHECK-MESSAGES: :[[@LINE-3]]:48: note: the last parameter in this range is 'fi'
+// CHECK-MESSAGES: :[[@LINE-4]]:40: note: 'unsigned long long' can be implicitly converted to 'FromInt': 'unsigned long long' -> 'int' -> 'FromInt'
+
+struct ToInt {
+  operator int() const;
+};
+
+void userConv3(int i, ToInt ti) {}
+// CHECK-MESSAGES: :[[@LINE-1]]:16: warning: 2 adjacent parameters for 'userConv3' of convertible
+// CHECK-MESSAGES: :[[@LINE-2]]:20: note: the first parameter in this range is 'i'
+// CHECK-MESSAGES: :[[@LINE-3]]:29: note: the last parameter in this range is 'ti'
+// CHECK-MESSAGES: :[[@LINE-4]]:23: note: 'int' can be implicitly converted from 'ToInt'
+
+void userConv4(double d, FromInt fi) {}
+// CHECK-MESSAGES: :[[@LINE-1]]:16: warning: 2 adjacent parameters for 'userConv4' of convertible
+// CHECK-MESSAGES: :[[@LINE-2]]:23: note: the first parameter in this range is 'd'
+// CHECK-MESSAGES: :[[@LINE-3]]:34: note: the last parameter in this range is 'fi'
+// CHECK-MESSAGES: :[[@LINE-4]]:26: note: 'double' can be implicitly converted to 'FromInt': 'double' -> 'int' -> 'FromInt'
+
+void userConv4cr(double d, const FromInt &cfir) {}
+// CHECK-MESSAGES: :[[@LINE-1]]:18: warning: 2 adjacent parameters for 'userConv4cr' of convertible
+// CHECK-MESSAGES: :[[@LINE-2]]:25: note: the first parameter in this range is 'd'
+// CHECK-MESSAGES: :[[@LINE-3]]:43: note: the last parameter in this range is 'cfir'
+// CHECK-MESSAGES: :[[@LINE-4]]:28: note: at a call site, 'const FromInt &' might bind with same force as 'double'
+// CHECK-MESSAGES: :[[@LINE-5]]:28: note: 'double' can be implicitly converted to 'const FromInt &': 'double' -> 'int' -> 'FromInt'
+
+struct FromDouble {
+  FromDouble(double);
+};
+
+void userConv5(En e, FromDouble fd) {}
+// CHECK-MESSAGES: :[[@LINE-1]]:16: warning: 2 adjacent parameters for 'userConv5' of convertible
+// CHECK-MESSAGES: :[[@LINE-2]]:19: note: the first parameter in this range is 'e'
+// CHECK-MESSAGES: :[[@LINE-3]]:33: note: the last parameter in this range is 'fd'
+// CHECK-MESSAGES: :[[@LINE-4]]:22: note: 'En' can be implicitly converted to 'FromDouble': 'En' -> 'double' -> 'FromDouble'
+
+struct Ambiguous {
+  Ambiguous(int);
+  Ambiguous(long);
+};
+
+void userConv6(En e, Ambiguous a) {}
+// NO-WARN: En -> int -> Ambiguous vs. En -> long -> Ambiguous.
+
+struct ToExplicitInt {
+  explicit operator int() const;
+};
+struct FromExplicitInt {
+  explicit FromExplicitInt(int);
+};
+
+void nonConverting1(int i, FromExplicitInt fei) {}
+// NO-WARN: There is no chance of mix-up as 'fei' has only explicit constructor.
+
+void nonConverting2(int i, ToExplicitInt tei) {}
+// NO-WARN: 'tei' converts to 'int' only in explicit context.
+
+struct Both {
+  Both(int);
+  operator int() const;
+};
+
+typedef int I;
+typedef double D;
+
+void both1(int i, Both b) {}
+// CHECK-MESSAGES: :[[@LINE-1]]:12: warning: 2 adjacent parameters for 'both1' of convertible
+// CHECK-MESSAGES: :[[@LINE-2]]:16: note: the first parameter in this range is 'i'
+// CHECK-MESSAGES: :[[@LINE-3]]:24: note: the last parameter in this range is 'b'
+// CHECK-MESSAGES: :[[@LINE-4]]:19: note: 'int' and 'Both' can suffer implicit conversions between one another{{$}}
+
+void both2(double d, Both b) {}
+// CHECK-MESSAGES: :[[@LINE-1]]:12: warning: 2 adjacent parameters for 'both2' of convertible
+// CHECK-MESSAGES: :[[@LINE-2]]:19: note: the first parameter in this range is 'd'
+// CHECK-MESSAGES: :[[@LINE-3]]:27: note: the last parameter in this range is 'b'
+// CHECK-MESSAGES: :[[@LINE-4]]:22: note: 'double' and 'Both' can suffer implicit conversions between one another: 'double' -> 'int' -> 'Both' and 'Both' -> 'int' -> 'double'
+
+void both2typedef(D d, Both b) {}
+// CHECK-MESSAGES: :[[@LINE-1]]:19: warning: 2 adjacent parameters for 'both2typedef' of convertible
+// CHECK-MESSAGES: :[[@LINE-2]]:21: note: the first parameter in this range is 'd'
+// CHECK-MESSAGES: :[[@LINE-3]]:29: note: the last parameter in this range is 'b'
+// CHECK-MESSAGES: :[[@LINE-4]]:19: note: after resolving type aliases, type of parameter 'd' is 'double'
+// CHECK-MESSAGES: :[[@LINE-5]]:24: note: after resolving type aliases, type of parameter 'b' is 'Both'
+// CHECK-MESSAGES: :[[@LINE-6]]:24: note: 'double' and 'Both' can suffer implicit conversions between one another: 'double' -> 'int' -> 'Both' and 'Both' -> 'int' -> 'double'
+
+struct BoxedInt {
+  BoxedInt();
+  BoxedInt(const BoxedInt &);
+  BoxedInt(BoxedInt &&);
+  BoxedInt(const Both &B);
+};
+
+void both3(int i, BoxedInt biv) {}
+// NO-WARN: Two converting constructor calls (int->both->BoxedInt) is not
+// possible implicitly.
+
+struct IntAndDouble {
+  IntAndDouble(const int);
+  IntAndDouble(const double);
+
+  operator int() const;
+  operator double() const;
+};
+
+void twoTypes1(int i, IntAndDouble iad) {}
+// CHECK-MESSAGES: :[[@LINE-1]]:16: warning: 2 adjacent parameters for 'twoTypes1' of convertible
+// CHECK-MESSAGES: :[[@LINE-2]]:20: note: the first parameter in this range is 'i'
+// CHECK-MESSAGES: :[[@LINE-3]]:36: note: the last parameter in this range is 'iad'
+// CHECK-MESSAGES: :[[@LINE-4]]:23: note: 'int' and 'IntAndDouble' can suffer implicit conversions between one another{{$}}
+
+void twoTypes2(double d, IntAndDouble iad) {}
+// CHECK-MESSAGES: :[[@LINE-1]]:16: warning: 2 adjacent parameters for 'twoTypes2' of convertible
+// CHECK-MESSAGES: :[[@LINE-2]]:23: note: the first parameter in this range is 'd'
+// CHECK-MESSAGES: :[[@LINE-3]]:39: note: the last parameter in this range is 'iad'
+// CHECK-MESSAGES: :[[@LINE-4]]:26: note: 'double' and 'IntAndDouble' can suffer implicit conversions between one another{{$}}
+
+void twoTypes3(unsigned long ul, IntAndDouble iad) {}
+// NO-WARN: Ambiguous constructor call and conversion call, should we take
+// 'int' or 'double' route from 'unsigned long'?
+
+struct IADTypedef {
+  IADTypedef(I);
+  IADTypedef(D);
+  operator I() const;
+  operator D() const;
+};
+
+void twoTypedefs1(int i, IADTypedef iadt) {}
+// CHECK-MESSAGES: :[[@LINE-1]]:19: warning: 2 adjacent parameters for 'twoTypedefs1' of convertible
+// CHECK-MESSAGES: :[[@LINE-2]]:23: note: the first parameter in this range is 'i'
+// CHECK-MESSAGES: :[[@LINE-3]]:37: note: the last parameter in this range is 'iadt'
+// CHECK-MESSAGES: :[[@LINE-4]]:26: note: 'int' and 'IADTypedef' can suffer implicit conversions between one another{{$}}
+
+void twoTypedefs2(double d, IADTypedef iadt) {}
+// CHECK-MESSAGES: :[[@LINE-1]]:19: warning: 2 adjacent parameters for 'twoTypedefs2' of convertible
+// CHECK-MESSAGES: :[[@LINE-2]]:26: note: the first parameter in this range is 'd'
+// CHECK-MESSAGES: :[[@LINE-3]]:40: note: the last parameter in this range is 'iadt'
+// CHECK-MESSAGES: :[[@LINE-4]]:29: note: 'double' and 'IADTypedef' can suffer implicit conversions between one another{{$}}
+
+void twoTypedefs3(unsigned long ul, IADTypedef iadt) {}
+// NO-WARN: Ambiguous constructor call and conversion call, should we take
+// 'int' or 'double' route from 'unsigned long'?
+
+struct Fwd1;
+struct Fwd2;
+void forwards(Fwd1 *f1, Fwd2 *f2) {}
+// NO-WARN and NO-CRASH: Don't try to compare incomplete types.
+
+struct Rec {};
+struct FromRec {
+  FromRec(const Rec &);
+};
+struct ToRec {
+  operator Rec() const;
+};
+struct RecBoth {
+  RecBoth(Rec);
+  operator Rec() const;
+};
+
+void record2record_1(Rec r, FromRec fr) {}
+// CHECK-MESSAGES: :[[@LINE-1]]:22: warning: 2 adjacent parameters for 'record2record_1' of convertible
+// CHECK-MESSAGES: :[[@LINE-2]]:26: note: the first parameter in this range is 'r'
+// CHECK-MESSAGES: :[[@LINE-3]]:37: note: the last parameter in this range is 'fr'
+// CHECK-MESSAGES: :[[@LINE-4]]:29: note: 'Rec' can be implicitly converted to 'FromRec'
+
+void record2record_2(Rec r, ToRec tr) {}
+// CHECK-MESSAGES: :[[@LINE-1]]:22: warning: 2 adjacent parameters for 'record2record_2' of convertible
+// CHECK-MESSAGES: :[[@LINE-2]]:26: note: the first parameter in this range is 'r'
+// CHECK-MESSAGES: :[[@LINE-3]]:35: note: the last parameter in this range is 'tr'
+// CHECK-MESSAGES: :[[@LINE-4]]:29: note: 'Rec' can be implicitly converted from 'ToRec'
+
+void record2record_3(Rec r, RecBoth rb) {}
+// CHECK-MESSAGES: :[[@LINE-1]]:22: warning: 2 adjacent parameters for 'record2record_3' of convertible
+// CHECK-MESSAGES: :[[@LINE-2]]:26: note: the first parameter in this range is 'r'
+// CHECK-MESSAGES: :[[@LINE-3]]:37: note: the last parameter in this range is 'rb'
+// CHECK-MESSAGES: :[[@LINE-4]]:29: note: 'Rec' and 'RecBoth' can suffer implicit conversions between one another{{$}}
+
+void record2record_4(Rec r, FromRec fr, ToRec tr, RecBoth rb) {}
+// CHECK-MESSAGES: :[[@LINE-1]]:22: warning: 4 adjacent parameters for 'record2record_4' of convertible
+// CHECK-MESSAGES: :[[@LINE-2]]:26: note: the first parameter in this range is 'r'
+// CHECK-MESSAGES: :[[@LINE-3]]:59: note: the last parameter in this range is 'rb'
+// CHECK-MESSAGES: :[[@LINE-4]]:29: note: 'Rec' can be implicitly converted to 'FromRec'
+// CHECK-MESSAGES: :[[@LINE-5]]:41: note: 'Rec' can be implicitly converted from 'ToRec'
+// CHECK-MESSAGES: :[[@LINE-6]]:51: note: 'Rec' and 'RecBoth' can suffer implicit conversions between one another{{$}}
+
+struct X;
+struct Y;
+struct X {
+  X();
+  X(Y);
+  operator Y();
+};
+struct Y {
+  Y();
+  Y(X);
+  operator X();
+};
+void ambiguous_records(X x, Y y) {}
+// NO-WARN: Ambiguous conversion if the arguments are swapped, which results in
+// compiler error: f3(Y{}, X{})
+
+struct Base {};
+struct Derived1 : Base {};
+struct Derived2 : Base {};
+void upcasting(Base *bp, Derived1 *d1p, Derived2 *d2p) {}
+// CHECK-MESSAGES: :[[@LINE-1]]:16: warning: 3 adjacent parameters for 'upcasting' of convertible
+// CHECK-MESSAGES: :[[@LINE-2]]:22: note: the first parameter in this range is 'bp'
+// CHECK-MESSAGES: :[[@LINE-3]]:51: note: the last parameter in this range is 'd2p'
+// CHECK-MESSAGES: :[[@LINE-4]]:26: note: 'Base *' can be implicitly converted from 'Derived1 *'
+// CHECK-MESSAGES: :[[@LINE-5]]:41: note: 'Base *' can be implicitly converted from 'Derived2 *'
+
+void upcasting_ref(const Base &br, const Derived1 &d1r, const Derived2 &d2r) {}
+// CHECK-MESSAGES: :[[@LINE-1]]:20: warning: 3 adjacent parameters for 'upcasting_ref' of convertible
+// CHECK-MESSAGES: :[[@LINE-2]]:32: note: the first parameter in this range is 'br'
+// CHECK-MESSAGES: :[[@LINE-3]]:73: note: the last parameter in this range is 'd2r'
+// CHECK-MESSAGES: :[[@LINE-4]]:36: note: at a call site, 'const Derived1 &' might bind with same force as 'const Base &'
+// CHECK-MESSAGES: :[[@LINE-5]]:36: note: 'const Base &' can be implicitly converted from 'const Derived1 &'
+// CHECK-MESSAGES: :[[@LINE-6]]:57: note: at a call site, 'const Derived2 &' might bind with same force as 'const Base &'
+// CHECK-MESSAGES: :[[@LINE-7]]:57: note: 'const Base &' can be implicitly converted from 'const Derived2 &'
+
+// Reduced case from live crash on OpenCV
+// http://github.com/opencv/opencv/blob/1996ae4a42d7c7cd338fbdd4abbd83b41b448328/modules/core/include/opencv2/core/types.hpp#L173
+template <typename T>
+struct TemplateConversion {
+  TemplateConversion();
+  TemplateConversion(const T &);
+  template <typename T2>
+  operator TemplateConversion<T2>() const;
+};
+typedef TemplateConversion<int> IntConvert;
+typedef TemplateConversion<float> FloatConvert;
+void templated_conversion_to_other_specialisation(FloatConvert fc, IntConvert ic) {}
+// NO-WARN: Template conversion operators are not resolved, we have approximated
+// much of Sema already...
Index: clang-tools-extra/test/clang-tidy/checkers/experimental-cppcoreguidelines-avoid-adjacent-parameters-of-the-same-type-implicits.c
===================================================================
--- /dev/null
+++ clang-tools-extra/test/clang-tidy/checkers/experimental-cppcoreguidelines-avoid-adjacent-parameters-of-the-same-type-implicits.c
@@ -0,0 +1,102 @@
+// 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.ImplicitConversion, value: 1} \
+// RUN:   ]}' --
+
+void compare(int a, int b, int c) {}
+// CHECK-MESSAGES: :[[@LINE-1]]:14: warning: 3 adjacent parameters for 'compare' of similar type ('int') are easily swapped by mistake [experimental-cppcoreguidelines-avoid-adjacent-parameters-of-the-same-type]
+// CHECK-MESSAGES: :[[@LINE-2]]:18: note: the first parameter in this range is 'a'
+// CHECK-MESSAGES: :[[@LINE-3]]:32: note: the last parameter in this range is 'c'
+
+void b(_Bool b1, _Bool b2, int i) {}
+// CHECK-MESSAGES: :[[@LINE-1]]:8: warning: 3 adjacent parameters for 'b' of convertible types may be easily swapped by mistake [experimental-cppcoreguidelines-avoid-adjacent-parameters-of-the-same-type]
+// CHECK-MESSAGES: :[[@LINE-2]]:14: note: the first parameter in this range is 'b1'
+// CHECK-MESSAGES: :[[@LINE-3]]:32: note: the last parameter in this range is 'i'
+// CHECK-MESSAGES: :[[@LINE-4]]:28: note: '_Bool' and 'int' can suffer implicit conversions between one another{{$}}
+
+void integral1(signed char sc, int si) {}
+// CHECK-MESSAGES: :[[@LINE-1]]:16: warning: 2 adjacent parameters for 'integral1' of convertible
+// CHECK-MESSAGES: :[[@LINE-2]]:28: note: the first parameter in this range is 'sc'
+// CHECK-MESSAGES: :[[@LINE-3]]:36: note: the last parameter in this range is 'si'
+// CHECK-MESSAGES: :[[@LINE-4]]:32: note: 'signed char' and 'int' can suffer implicit conversions between one another{{$}}
+
+void integral2(long l, int i) {}
+// CHECK-MESSAGES: :[[@LINE-1]]:16: warning: 2 adjacent parameters for 'integral2' of convertible
+// CHECK-MESSAGES: :[[@LINE-2]]:21: note: the first parameter in this range is 'l'
+// CHECK-MESSAGES: :[[@LINE-3]]:28: note: the last parameter in this range is 'i'
+// CHECK-MESSAGES: :[[@LINE-4]]:24: note: 'long' and 'int' can suffer implicit conversions between one another{{$}}
+
+void integral3(int i, long l) {}
+// CHECK-MESSAGES: :[[@LINE-1]]:16: warning: 2 adjacent parameters for 'integral3' of convertible
+// CHECK-MESSAGES: :[[@LINE-2]]:20: note: the first parameter in this range is 'i'
+// CHECK-MESSAGES: :[[@LINE-3]]:28: note: the last parameter in this range is 'l'
+// CHECK-MESSAGES: :[[@LINE-4]]:23: note: 'int' and 'long' can suffer implicit conversions between one another{{$}}
+
+void integral4(char c, int i, long l) {}
+// CHECK-MESSAGES: :[[@LINE-1]]:16: warning: 3 adjacent parameters for 'integral4' of convertible
+// CHECK-MESSAGES: :[[@LINE-2]]:21: note: the first parameter in this range is 'c'
+// CHECK-MESSAGES: :[[@LINE-3]]:36: note: the last parameter in this range is 'l'
+// CHECK-MESSAGES: :[[@LINE-4]]:24: note: 'char' and 'int' can suffer implicit conversions between one another{{$}}
+// CHECK-MESSAGES: :[[@LINE-5]]:31: note: 'char' and 'long' can suffer implicit conversions between one another{{$}}
+
+void floating1(float f, double d) {}
+// CHECK-MESSAGES: :[[@LINE-1]]:16: warning: 2 adjacent parameters for 'floating1' of convertible
+// CHECK-MESSAGES: :[[@LINE-2]]:22: note: the first parameter in this range is 'f'
+// CHECK-MESSAGES: :[[@LINE-3]]:32: note: the last parameter in this range is 'd'
+// CHECK-MESSAGES: :[[@LINE-4]]:25: note: 'float' and 'double' can suffer implicit conversions between one another{{$}}
+
+typedef double D;
+void floating2(float f, D d) {}
+// CHECK-MESSAGES: :[[@LINE-1]]:16: warning: 2 adjacent parameters for 'floating2' of convertible
+// CHECK-MESSAGES: :[[@LINE-2]]:22: note: the first parameter in this range is 'f'
+// CHECK-MESSAGES: :[[@LINE-3]]:27: note: the last parameter in this range is 'd'
+// CHECK-MESSAGES: :[[@LINE-4]]:16: note: after resolving type aliases, type of parameter 'f' is 'float'
+// CHECK-MESSAGES: :[[@LINE-5]]:25: note: after resolving type aliases, type of parameter 'd' is 'double'
+// CHECK-MESSAGES: :[[@LINE-6]]:25: note: 'float' and 'double' can suffer implicit conversions between one another{{$}}
+
+void floatToInt(float f, int i) {}
+// CHECK-MESSAGES: :[[@LINE-1]]:17: warning: 2 adjacent parameters for 'floatToInt' of convertible
+// CHECK-MESSAGES: :[[@LINE-2]]:23: note: the first parameter in this range is 'f'
+// CHECK-MESSAGES: :[[@LINE-3]]:30: note: the last parameter in this range is 'i'
+// CHECK-MESSAGES: :[[@LINE-4]]:26: note: 'float' and 'int' can suffer implicit conversions between one another{{$}}
+
+enum En { A,
+          B };
+
+void unscopedEnumToIntegral(enum En e, int i) {}
+// CHECK-MESSAGES: :[[@LINE-1]]:29: warning: 2 adjacent parameters for 'unscopedEnumToIntegral' of convertible
+// CHECK-MESSAGES: :[[@LINE-2]]:37: note: the first parameter in this range is 'e'
+// CHECK-MESSAGES: :[[@LINE-3]]:44: note: the last parameter in this range is 'i'
+// CHECK-MESSAGES: :[[@LINE-4]]:40: note: 'enum En' and 'int' can suffer implicit conversions between one another{{$}}
+
+void unscopedEnumToIntegral2(enum En e, unsigned long long ull) {}
+// CHECK-MESSAGES: :[[@LINE-1]]:30: warning: 2 adjacent parameters for 'unscopedEnumToIntegral2' of convertible
+// CHECK-MESSAGES: :[[@LINE-2]]:38: note: the first parameter in this range is 'e'
+// CHECK-MESSAGES: :[[@LINE-3]]:60: note: the last parameter in this range is 'ull'
+// CHECK-MESSAGES: :[[@LINE-4]]:41: note: 'enum En' and 'unsigned long long' can suffer implicit conversions between one another{{$}}
+
+void unscopedEnum3(char c, enum En e, char c2) {}
+// CHECK-MESSAGES: :[[@LINE-1]]:20: warning: 3 adjacent parameters for 'unscopedEnum3' of convertible
+// CHECK-MESSAGES: :[[@LINE-2]]:25: note: the first parameter in this range is 'c'
+// CHECK-MESSAGES: :[[@LINE-3]]:44: note: the last parameter in this range is 'c2'
+// CHECK-MESSAGES: :[[@LINE-4]]:28: note: 'char' and 'enum En' can suffer implicit conversions between one another{{$}}
+// CHECK-MESSAGES: :[[@LINE-5]]:39: note: 'enum En' and 'char' can suffer implicit conversions between one another{{$}}
+
+void unscopedEnumToFloating(enum En e, long double ld) {}
+// CHECK-MESSAGES: :[[@LINE-1]]:29: warning: 2 adjacent parameters for 'unscopedEnumToFloating' of convertible
+// CHECK-MESSAGES: :[[@LINE-2]]:37: note: the first parameter in this range is 'e'
+// CHECK-MESSAGES: :[[@LINE-3]]:52: note: the last parameter in this range is 'ld'
+// CHECK-MESSAGES: :[[@LINE-4]]:40: note: 'enum En' and 'long double' can suffer implicit conversions between one another{{$}}
+
+void unscopedEnumToIntOrFloat(enum En e, int i, float f) {}
+// CHECK-MESSAGES: :[[@LINE-1]]:31: warning: 3 adjacent parameters for 'unscopedEnumToIntOrFloat' of convertible
+// CHECK-MESSAGES: :[[@LINE-2]]:39: note: the first parameter in this range is 'e'
+// CHECK-MESSAGES: :[[@LINE-3]]:55: note: the last parameter in this range is 'f'
+// CHECK-MESSAGES: :[[@LINE-4]]:42: note: 'enum En' and 'int' can suffer implicit conversions between one another{{$}}
+// CHECK-MESSAGES: :[[@LINE-5]]:49: note: 'enum En' and 'float' can suffer implicit conversions between one another{{$}}
+// CHECK-MESSAGES: :[[@LINE-6]]:49: note: 'int' and 'float' can suffer implicit conversions between one another{{$}}
+
+void ptr(int *i, long *l) {}
+// NO-WARN: Though 'int' and 'long' can be converted between one-another
+// implicitly, mixing such pointers is diagnosed by compiler warnings.
Index: clang-tools-extra/docs/clang-tidy/checks/experimental-cppcoreguidelines-avoid-adjacent-parameters-of-the-same-type.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/experimental-cppcoreguidelines-avoid-adjacent-parameters-of-the-same-type.rst
@@ -99,6 +99,41 @@
         struct T {};
         void f(T *tp, const T *ctp) {}
 
+.. option:: ImplicitConversion
+
+    Whether to allow the approximation of some usual
+    `implicit conversions <https://en.cppreference.com/w/cpp/language/implicit_conversion>`_
+    when considering which type might be potentially mixed up with another.
+    If `0` (default value), the check does not consider **any** implicit
+    conversions.
+    A non-zero value turns this **on**.
+    A non-zero value will in almost all cases produce a **significantly broader**
+    set of results.
+
+    .. warning::
+        Turning the modelling of implicit conversion sequences on
+        relaxes the constraints for "type convertibility" significantly,
+        however, it also applies a generous performance hit on the check's cost.
+        The check will have to explore a **polynomially more** possibilities:
+        O(n\ :sup:`2`\ ) instead of O(n) for each function's ``n`` parameters.
+        The emitted diagnostics will also be more verbose, which might take more
+        time to stringify.
+
+        It is advised to normally leave this option *off*.
+
+    For details on what is matched by this option, see
+    `Implicit conversion modelling`_.
+
+    The following examples will not produce a diagnostic unless
+    *ImplicitConversion* is set to a non-zero value.
+
+    .. code-block:: c++
+
+        void f(char c, int i, double d) {}
+
+        enum E { Ea, Eb };
+        void f(E e, int i, double d) {}
+
 Limitations
 -----------
 
@@ -133,10 +168,10 @@
 
     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;
+        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"
@@ -148,3 +183,36 @@
         const          vector<T>                       & Vector,
               typename vector<T>::const_reference_type   RightEnd,
         const typename vector<T>::element_type         & LeftBegin) { /* ... */ }
+
+
+Implicit conversion modelling
+-----------------------------
+
+Given a function ``void f(T1 t, T2 u)``, unless ``T1`` and ``T2`` are the same,
+the check won't warn in *"default"* mode. If ``ImplicitConversion`` is turned on
+(see Options_), and either ``T1``
+`can be converted to <https://en.cppreference.com/w/cpp/language/implicit_conversion>`_
+``T2``, or vica versa, diagnostics will be made.
+The "adjacent parameter mixup" is considered even if the conversion is asymmetric,
+which is natural in most cases.
+
+In the case of the following function, the numeric parameters can be
+converted between one another, and the ``SomeEnum`` is convertible to any
+numeric type.
+Thus, an "adjacent parameter range" of **5** is diagnosed here.
+
+.. code-blocK:: c++
+
+    enum SomeEnum { /* ... */ };
+    void SomeFunction(int, float, double, unsigned long, SomeEnum) {}
+
+Currently, the following implicit conversions are modelled:
+
+ - *standard conversion* sequences:
+    - numeric promotion and conversion between integer (including ``enum``) and
+      floating types, considered essentially the same under the umbrella term
+      "conversion"
+    - For **C** projects, the numeric conversion rules are relaxed to conform
+      to C rules.
+ - *user defined conversions*: non-``explicit`` converting constructors and
+   conversion operators.
Index: clang-tools-extra/clang-tidy/experimental/CppcoreguidelinesAvoidAdjacentParametersOfTheSameTypeCheck.h
===================================================================
--- clang-tools-extra/clang-tidy/experimental/CppcoreguidelinesAvoidAdjacentParametersOfTheSameTypeCheck.h
+++ clang-tools-extra/clang-tidy/experimental/CppcoreguidelinesAvoidAdjacentParametersOfTheSameTypeCheck.h
@@ -48,6 +48,10 @@
   /// Whether to consider 'T' and 'const T'/'volatile T'/etc. arguments to be
   /// possible mixup.
   const bool CVRMixPossible;
+
+  /// Whether to consider implicit conversion possibilities as a potential match
+  /// for adjacency.
+  const bool ImplicitConversion;
 };
 
 } // namespace experimental
Index: clang-tools-extra/clang-tidy/experimental/CppcoreguidelinesAvoidAdjacentParametersOfTheSameTypeCheck.cpp
===================================================================
--- clang-tools-extra/clang-tidy/experimental/CppcoreguidelinesAvoidAdjacentParametersOfTheSameTypeCheck.cpp
+++ clang-tools-extra/clang-tidy/experimental/CppcoreguidelinesAvoidAdjacentParametersOfTheSameTypeCheck.cpp
@@ -11,6 +11,7 @@
 #include "clang/AST/ASTContext.h"
 #include "clang/ASTMatchers/ASTMatchFinder.h"
 #include "llvm/ADT/BitmaskEnum.h"
+#include "llvm/ADT/SmallPtrSet.h"
 #include "llvm/ADT/SmallVector.h"
 
 #include <string>
@@ -31,60 +32,100 @@
 
 // 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.
+  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.
+  BIT(Implicit, 6), //< An implicit conversion may happen.
 #undef BIT
 
-  LLVM_MARK_AS_BITMASK_ENUM(/* LargestValue = */ MIXUP_CVR)
+  LLVM_MARK_AS_BITMASK_ENUM(/* LargestValue = */ MIXUP_Implicit)
 };
 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;
+/// Implicit conversion sequence steps resulting in types referred here.
+struct ConversionSequence {
+  /// Type of the intermediate value after the first standard conversion.
+  const Type *StdPre;
+  /// Type of the intermediate value after executing the user-defined conversion
+  /// which, in case of constructors, the user type, in case of conversion
+  /// operators, the result of the operator.
+  const Type *UserType;
+  /// Type of the intermediate value after the second standard conversion.
+  const Type *StdPost;
+
+  ConversionSequence() : StdPre(nullptr), UserType(nullptr), StdPost(nullptr) {}
+
+  explicit operator bool() const { return StdPre || UserType || StdPost; }
+
+  /// Whether the conversion sequence is single-step only.
+  bool single() const {
+    return ((bool)StdPre ^ (bool)UserType ^ (bool)StdPost) &&
+           !(StdPre && UserType && StdPost);
+  }
+};
 
-  Mixup(const ParmVarDecl *A, const ParmVarDecl *B, MixupTag Flags)
-      : First(A), Second(B), Flags(Flags) {}
+struct MixupData {
+  MixupTag Flag;
+  ConversionSequence ConvLTR, ConvRTL;
 
-  Mixup operator|(MixupTag EnableFlags) const {
-    return {First, Second, Flags | EnableFlags};
+  MixupData(MixupTag Flags) : Flag(Flags) {}
+  MixupData(MixupTag Flags, const ConversionSequence &Seq)
+      : Flag(Flags), ConvLTR(Seq), ConvRTL(Seq) {}
+  MixupData(MixupTag Flags, const ConversionSequence &LTR,
+            const ConversionSequence &RTL)
+      : Flag(Flags), ConvLTR(LTR), ConvRTL(RTL) {}
+
+  MixupData operator|(MixupTag EnableFlag) const {
+    return {Flag | EnableFlag, ConvLTR, ConvRTL};
   }
-  Mixup &operator|=(MixupTag EnableFlags) {
-    Flags |= EnableFlags;
+  MixupData &operator|=(MixupTag EnableFlag) {
+    Flag |= EnableFlag;
     return *this;
   }
 
   /// Sanitises the Mixup's flags so it doesn't contain contradictory bits.
   void sanitise() {
-    assert(Flags != MIXUP_Invalid &&
+    assert(Flag != MIXUP_Invalid &&
            "Mixup tag had full zero bit pattern value!");
 
-    if (Flags & MIXUP_None) {
+    if (Flag & MIXUP_None) {
       // If at any point the checks mark the mixup impossible, it is just simply
       // impossible.
-      Flags = MIXUP_None;
+      Flag = MIXUP_None;
       return;
     }
 
-    if (Flags == MIXUP_Trivial)
+    if (Flag == MIXUP_Trivial)
       return;
 
-    if (Flags ^ MIXUP_Trivial)
+    if (Flag ^ 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;
+      Flag &= ~MIXUP_Trivial;
+
+    // Set LTR and RTL implicity according to the members being set.
+    if (ConvLTR || ConvRTL)
+      Flag |= MIXUP_Implicit;
+    else
+      Flag &= ~MIXUP_Implicit;
   }
 };
 
+/// Named tuple that contains that the types of the arguments From and To
+/// are mixable with the given flags in a particular fashion.
+struct Mixup {
+  const ParmVarDecl *First, *Second;
+  MixupData Data;
+
+  Mixup(const ParmVarDecl *A, const ParmVarDecl *B, MixupData Data)
+      : First(A), Second(B), Data(Data) {}
+};
 static_assert(std::is_trivially_copyable<Mixup>::value,
-              "keep Mixup trivially copyable!");
+              "keep Mixup and components trivially copyable!");
 
 /// Represents a (closed) range of adjacent parameters that can be mixed up at
 /// a call site.
@@ -112,29 +153,45 @@
 
 } // 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) {
+/// Returns how an lvalue reference refers to the same type as T.
+static MixupData RefBindsToSameType(const LValueReferenceType *LRef,
+                                    const Type *T, const ASTContext &Ctx,
+                                    bool IsRefRightType, bool CVRMixPossible,
+                                    bool ImplicitConversion);
+
+/// Returns whether the left side type is convertible to the right side type,
+/// by attempting to approximate implicit conversion sequences.
+/// \param AllowUserDefined If false, only standard conversions will be
+/// approximated.
+/// \note The result of this operation is not symmetric!
+static MixupData HowConvertible(const Type *LT, const Type *RT,
+                                const LangOptions &LOpts,
+                                bool AllowUserDefined = true);
+
+/// Returns how LType and RType may essentially refer to the same type - in a
+/// sense that at a call site it is possible to mix the arguments up if
+/// specified in the opposite order.
+/// \returns MixupData indicating how a mixup between the arguments happens.
+/// \note The final output of this potentially recursive function must be
+/// sanitised by 'sanitiseMixup' before it could be used, to ensure only the
+/// proper bits are set!
+static MixupData HowPossibleToMixUpAtCallSite(const QualType LType,
+                                              const QualType RType,
+                                              const ASTContext &Ctx,
+                                              const bool CVRMixPossible,
+                                              const bool ImplicitConversion) {
   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);
+                                        RType, Ctx, CVRMixPossible,
+                                        ImplicitConversion);
   if (dyn_cast<const ParenType>(RType.getTypePtr()))
     return HowPossibleToMixUpAtCallSite(
-        LType, RType.getSingleStepDesugaredType(Ctx), Ctx, CVRMixPossible);
+        LType, RType.getSingleStepDesugaredType(Ctx), Ctx, CVRMixPossible,
+        ImplicitConversion);
 
   // 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,
@@ -142,21 +199,23 @@
   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;
-    }
+    if (LType->isLValueReferenceType())
+      // Return value of function call 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 RefBindsToSameType(LType->getAs<LValueReferenceType>(),
+                                RType.getTypePtr(), Ctx,
+                                /* IsRefRightType =*/false, CVRMixPossible,
+                                ImplicitConversion) |
+             MIXUP_RefBind;
+    if (RType->isLValueReferenceType())
+      return RefBindsToSameType(RType->getAs<LValueReferenceType>(),
+                                LType.getTypePtr(), Ctx,
+                                /* IsRefRightType =*/true, CVRMixPossible,
+                                ImplicitConversion) |
+             MIXUP_RefBind;
   }
 
   // A parameter of type 'T' and 'const T' may bind with the same power.
@@ -168,7 +227,7 @@
 
     return HowPossibleToMixUpAtCallSite(LType.getUnqualifiedType(),
                                         RType.getUnqualifiedType(), Ctx,
-                                        CVRMixPossible) |
+                                        CVRMixPossible, ImplicitConversion) |
            MIXUP_CVR;
   }
 
@@ -176,56 +235,371 @@
     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);
+      return HowPossibleToMixUpAtCallSite(LTypedef->desugar(),
+                                          RTypedef->desugar(), Ctx,
+                                          CVRMixPossible, ImplicitConversion) |
+             MIXUP_Typedef;
     if (LTypedef)
-      return MIXUP_Typedef |
-             HowPossibleToMixUpAtCallSite(LTypedef->desugar(), RType, Ctx,
-                                          CVRMixPossible);
+      return HowPossibleToMixUpAtCallSite(LTypedef->desugar(), RType, Ctx,
+                                          CVRMixPossible, ImplicitConversion) |
+             MIXUP_Typedef;
     if (RTypedef)
-      return MIXUP_Typedef |
-             HowPossibleToMixUpAtCallSite(LType, RTypedef->desugar(), Ctx,
-                                          CVRMixPossible);
+      return HowPossibleToMixUpAtCallSite(LType, RTypedef->desugar(), Ctx,
+                                          CVRMixPossible, ImplicitConversion) |
+             MIXUP_Typedef;
   }
 
-  if (LType->isPointerType() && RType->isPointerType())
+  if (LType->isPointerType() && RType->isPointerType()) {
     // (Both types being the exact same pointer is handled by LType == RType.)
+    // The implicit conversion between any T* and U* possible in C is ignored,
+    // as virtually all compilers emit a warning if the conversion is done at a
+    // call site.
+    // Implicit conversions of two pointers should be diagnosed if they point to
+    // C++ records to find Derived-To-Base implicit casts.
+    // For non-user-types, do not check - giving an unrelated, e.g. "long *" in
+    // place of an "int *" is diagnosed anyways by warnings or errors.
+    bool ShouldDiagnoseImplicitBehindPtr = ImplicitConversion &&
+                                           LType->getPointeeCXXRecordDecl() &&
+                                           RType->getPointeeCXXRecordDecl();
     return HowPossibleToMixUpAtCallSite(
-        LType->getPointeeType(), RType->getPointeeType(), Ctx, CVRMixPossible);
+        LType->getPointeeType(), RType->getPointeeType(), Ctx, CVRMixPossible,
+        ShouldDiagnoseImplicitBehindPtr);
+  }
 
-  // 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);
+  if (ImplicitConversion) {
+    const Type *LT = LType.getTypePtr();
+    const Type *RT = RType.getTypePtr();
+
+    // Try approximating an implicit conversion sequence.
+    MixupData LTR =
+        HowConvertible(LT, RT, Ctx.getLangOpts(), /* AllowUserDefined =*/true);
+    MixupData RTL =
+        HowConvertible(RT, LT, Ctx.getLangOpts(), /* AllowUserDefined =*/true);
+
+    if (LTR.ConvLTR || RTL.ConvRTL)
+      return {MIXUP_Implicit, LTR.ConvLTR, RTL.ConvRTL};
+  }
 
   return MIXUP_None;
 }
 
-static MixupTag RefBindsToSameType(const LValueReferenceType *LRef,
-                                   const Type *T, bool CVRMixPossible) {
+static MixupData RefBindsToSameType(const LValueReferenceType *LRef,
+                                    const Type *T, const ASTContext &Ctx,
+                                    const bool IsRefRightType,
+                                    const bool CVRMixPossible,
+                                    const bool ImplicitConversion) {
   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 (const auto *TypedefTy = ReferredType->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);
+    if (!IsRefRightType)
+      return HowPossibleToMixUpAtCallSite(TypedefTy->desugar(), QualType{T, 0},
+                                          Ctx, CVRMixPossible,
+                                          ImplicitConversion);
+    return HowPossibleToMixUpAtCallSite(QualType{T, 0}, TypedefTy->desugar(),
+                                        Ctx, CVRMixPossible,
+                                        ImplicitConversion);
+  }
 
-  return ReferredType.getTypePtr() == T ? MIXUP_Trivial : MIXUP_None;
+  if (ReferredType.getTypePtr() == T)
+    return MIXUP_Trivial;
+
+  if (ImplicitConversion) {
+    // Try to see if the reference can be bound through an implicit conversion.
+    if (!IsRefRightType)
+      return HowPossibleToMixUpAtCallSite(ReferredType.getUnqualifiedType(),
+                                          QualType{T, 0}, Ctx, CVRMixPossible,
+                                          ImplicitConversion);
+    return HowPossibleToMixUpAtCallSite(QualType{T, 0},
+                                        ReferredType.getUnqualifiedType(), Ctx,
+                                        CVRMixPossible, ImplicitConversion);
+  }
+  return MIXUP_None;
+}
+
+static inline bool IsNumericConvertible(const Type *LT, const Type *RT) {
+  if (!LT || !RT)
+    return false;
+
+  bool LI = LT->isIntegerType();
+  bool LF = LT->isFloatingType();
+  bool RI = RT->isIntegerType();
+  bool RF = RT->isFloatingType();
+
+  // Through promotions and conversions, there is free passage between
+  // "integer types" and "floating types".
+  // Promotions don't change value, conversions may result in value or precision
+  // loss, but for the sake of easy understanding, we put the two under
+  // one umbrella.
+  return (LI && RI) || (LF && RF) || (LI && RF) || (LF && RI);
+}
+
+/// Returns a Mixup indicating if the specified converting constructor can be
+/// called with LT or after applying a standard conversion sequence on LT.
+static MixupData TryConvertingConstructor(const Type *LT,
+                                          const CXXRecordDecl *RD,
+                                          const CXXConstructorDecl *Conv,
+                                          const LangOptions &LOpts) {
+  const ParmVarDecl *ConArg = Conv->getParamDecl(0);
+  MixupData Mix = HowPossibleToMixUpAtCallSite(
+      QualType{LT, 0}, ConArg->getType(), Conv->getASTContext(),
+      // Model qualifier mixing generously.
+      /* CVRMixPossible =*/true,
+      // Do not allow any further implicit conversions.
+      /* ImplicitConversion =*/false);
+  Mix.sanitise();
+  if (Mix.Flag != MIXUP_None) {
+    // If simply applying the user-defined constructor works as is, this is
+    // the right constructor.
+    ConversionSequence Seq;
+    Seq.UserType = RD->getTypeForDecl();
+    Mix.ConvLTR = Seq;
+    return Mix;
+  }
+
+  // Otherwise, see if LT and the converting constructor's argument can be
+  // matched via a standard conversion before. This will take care of converting
+  // an int to another type.
+  ConversionSequence Seq;
+  MixupData Pre = HowConvertible(LT, ConArg->getType().getTypePtr(), LOpts,
+                                 /* AllowUserDefined =*/false);
+  if (Pre.Flag == MIXUP_None || Pre.Flag == MIXUP_Trivial)
+    // If not possible to mix, don't check. If trivial, previous case should
+    // have returned already.
+    return MIXUP_None;
+  Seq.StdPre = Pre.ConvLTR.StdPre;
+
+  Mix = HowPossibleToMixUpAtCallSite(
+      QualType{Seq.StdPre, 0}, ConArg->getType(), Conv->getASTContext(),
+      // Model qualifier mixing generously.
+      /* CVRMixPossible =*/true,
+      // Do not allow any further implicit conversions.
+      /* ImplicitConversion =*/false);
+  Mix.sanitise();
+  if (Mix.Flag != MIXUP_None) {
+    Seq.UserType = RD->getTypeForDecl();
+    Mix.ConvLTR = Seq;
+    return Mix;
+  }
+
+  return MIXUP_None;
+}
+
+static MixupData TryConversionOperator(const CXXConversionDecl *Conv,
+                                       const Type *RT,
+                                       const LangOptions &LOpts) {
+  MixupData Mix = HowPossibleToMixUpAtCallSite(
+      Conv->getConversionType(), QualType{RT, 0}, Conv->getASTContext(),
+      // Model qualifier mixing generously.
+      /* CVRMixPossible =*/true,
+      // Do not allow any further implicit conversions.
+      /* ImplicitConversion =*/false);
+  Mix.sanitise();
+  if (Mix.Flag != MIXUP_None) {
+    // If simply applying the user-defined operator works as is, this is
+    // the right conversion operator.
+    ConversionSequence Seq;
+    Seq.UserType = RT;
+    Mix.ConvLTR = Seq;
+    return Mix;
+  }
+
+  // Otherwise, see if the converting operator's result and RT can be
+  // matched via a standard conversion after. This will take care of converting
+  // a result of float to another type.
+  ConversionSequence Seq;
+  MixupData Post = HowConvertible(Conv->getConversionType().getTypePtr(), RT,
+                                  LOpts, /* AllowUserDefined =*/false);
+  if (Post.Flag == MIXUP_None || Post.Flag == MIXUP_Trivial)
+    // If not possible to mix, don't check. If trivial, previous case should
+    // have returned already.
+    return MIXUP_None;
+  // Save the result type of the user-defined conversion operator.
+  Seq.UserType = Conv->getConversionType().getTypePtr();
+
+  // The unqualified version of the conversion operator's return type can be
+  // converted to the "needed" Right-Type (output type wanted by the caller
+  // of this method). Now see if between we lost any qualifiers.
+  Mix = HowPossibleToMixUpAtCallSite(
+      Conv->getConversionType(),
+      QualType{Conv->getConversionType().getTypePtr(), 0},
+      Conv->getASTContext(),
+      // Model qualifier mixing generously.
+      /* CVRMixPossible =*/true,
+      // Do not allow any further implicit conversions.
+      /* ImplicitConversion =*/false);
+  Mix.sanitise();
+  if (Mix.Flag != MIXUP_None) {
+    Seq.StdPost = RT;
+    Mix.ConvLTR = Seq;
+    return Mix;
+  }
+
+  return MIXUP_None;
+}
+
+static MixupData HowConvertible(const Type *LT, const Type *RT,
+                                const LangOptions &LOpts,
+                                bool AllowUserDefined) {
+  if (LT == RT)
+    return MIXUP_Trivial;
+
+  ConversionSequence Seq;
+
+  const auto *LBT = LT->getAs<BuiltinType>();
+  const auto *RBT = RT->getAs<BuiltinType>();
+  if (LBT && RBT && LBT == RBT)
+    return {MIXUP_Trivial};
+  if (IsNumericConvertible(LBT, RBT)) {
+    // Builtin numerical types are back-and-forth convertible.
+    Seq.StdPre = RBT;
+    return {MIXUP_Implicit, Seq};
+  }
+
+  const auto *LET = LT->getAs<EnumType>();
+  const auto *RET = RT->getAs<EnumType>();
+  if (LET && !LET->isScopedEnumeralType() && RBT &&
+      (RBT->isIntegerType() || RBT->isFloatingType())) {
+    // Enum can convert to underlying integer type.
+    Seq.StdPre = RBT;
+    return {MIXUP_Implicit, Seq};
+  }
+  if (LBT && (LBT->isFloatingType() || LBT->isIntegerType()) && RET) {
+    // Enum cannot be constructed from any builtin type (neither int, nor
+    // float).
+    if (LOpts.CPlusPlus)
+      return MIXUP_None;
+
+    // In C, you can go back and forth between numeric types.
+    Seq.StdPre = RET;
+    return {MIXUP_Implicit, Seq};
+  }
+
+  if (LOpts.CPlusPlus) {
+    // We are checking Left -> Right mixup, in which case Left <: Right should
+    // hold for DerivedToBase implicit cast. This is a standard implicit
+    // conversion.
+    const auto *LCXXRec = LT->getAsCXXRecordDecl();
+    const auto *RCXXRec = RT->getAsCXXRecordDecl();
+    if (LCXXRec && RCXXRec && LCXXRec->isCompleteDefinition() &&
+        RCXXRec->isCompleteDefinition() &&
+        (LCXXRec->isDerivedFrom(RCXXRec) ||
+         LCXXRec->isVirtuallyDerivedFrom(RCXXRec))) {
+      Seq.StdPre = RT;
+      return {MIXUP_Implicit, Seq};
+    }
+  }
+
+  if (!LOpts.CPlusPlus || !AllowUserDefined)
+    // User-defined conversions are only sensible in C++ mode.
+    return MIXUP_None;
+  assert(!Seq && "Non-user defined conversion check should've returned.");
+
+  MixupTag UserMixup = MIXUP_Invalid;
+  bool FoundConvertingCtor = false, FoundConversionOperator = false;
+  bool FoundMultipleMatches = false;
+  // An implicit conversion sequence may only contain at most *one* user-defined
+  // conversion.
+  if (const auto *RRT = RT->getAs<RecordType>()) {
+    const auto *RD = RRT->getAsCXXRecordDecl();
+    if (!RD)
+      // Initialisation of C-style record types from an unrelated type is not
+      // possible.
+      return MIXUP_None;
+    if (!RD->isCompleteDefinition())
+      // Incomplete definition means we don't know anything about members.
+      return MIXUP_None;
+
+    for (const CXXConstructorDecl *Con : RD->ctors()) {
+      if (Con->isCopyOrMoveConstructor() ||
+          !Con->isConvertingConstructor(/* AllowExplicit =*/false))
+        continue;
+
+      MixupData ConvertingResult = TryConvertingConstructor(LT, RD, Con, LOpts);
+      if (ConvertingResult.Flag == MIXUP_None)
+        continue;
+
+      FoundConvertingCtor = true;
+
+      if (!ConvertingResult.ConvLTR.StdPre) {
+        // A match without a pre-conversion was found for the converting ctor.
+        UserMixup |= ConvertingResult.Flag;
+        Seq = ConvertingResult.ConvLTR;
+        FoundMultipleMatches = false;
+        break;
+      }
+
+      if (!FoundMultipleMatches && Seq) {
+        // If the loop executes multiple times, it's possible to find multiple
+        // converting constructors with different, ambiguous conversion
+        // sequences leading up to them. This is an error, but we need to check
+        // all constructors first for a possible exact match.
+        FoundMultipleMatches = true;
+        continue;
+      }
+
+      // Register the match.
+      UserMixup |= ConvertingResult.Flag;
+      Seq = ConvertingResult.ConvLTR;
+    }
+  }
+
+  if (const auto *LRT = LT->getAs<RecordType>()) {
+    const auto *RD = LRT->getAsCXXRecordDecl();
+    if (!RD)
+      // Initialisation of things from an unrelated C-style record type is not
+      // possible.
+      return MIXUP_None;
+    if (!RD->isCompleteDefinition())
+      // Against getVisibleConversionFunctions() assert.
+      return MIXUP_None;
+
+    for (const NamedDecl *Method : RD->getVisibleConversionFunctions()) {
+      const auto *Con = dyn_cast<CXXConversionDecl>(Method);
+      if (!Con || Con->isExplicit())
+        continue;
+
+      MixupData ConvertingResult = TryConversionOperator(Con, RT, LOpts);
+      if (ConvertingResult.Flag == MIXUP_None)
+        continue;
+
+      FoundConversionOperator = true;
+
+      if (!ConvertingResult.ConvLTR.StdPost) {
+        // A match without a pre-conversion was found for the converting oper.
+        UserMixup |= ConvertingResult.Flag;
+        Seq = ConvertingResult.ConvLTR;
+        FoundMultipleMatches = false;
+        break;
+      }
+
+      if (!FoundMultipleMatches && Seq) {
+        // If the loop executes multiple times, it's possible to find multiple
+        // conversion operators with different, ambiguous conversion
+        // sequences coming from them. This is an error, but we need to check
+        // all constructors first for a possible exact match.
+        FoundMultipleMatches = true;
+        continue;
+      }
+
+      // Register the match.
+      UserMixup |= ConvertingResult.Flag;
+      Seq = ConvertingResult.ConvLTR;
+    }
+  }
+
+  if (FoundMultipleMatches)
+    return MIXUP_None;
+
+  if (FoundConvertingCtor && FoundConversionOperator)
+    return MIXUP_None;
+
+  return Seq ? MixupData{UserMixup, Seq} : MixupData{MIXUP_None};
 }
 
 /// Gets the mixable range of the parameters of F starting with the param at
@@ -237,16 +611,17 @@
   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.
+  // breaks.
   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;
@@ -254,14 +629,16 @@
     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 &&
+                                           Checker.CVRMixPossible,
+                                           Checker.ImplicitConversion)};
+      M.Data.sanitise();
+      assert(M.Data.Flag != MIXUP_Invalid &&
              "Bits fell off, result is sentinel value.");
 
-      if (M.Flags != MIXUP_None) {
+      if (M.Data.Flag != MIXUP_None) {
         MixRange.Mixups.emplace_back(M);
         AnyMixupStored = true;
       }
@@ -367,10 +744,11 @@
   else if (const auto *PackTy = dyn_cast<PackExpansionType>(Ty)) {
     PutTypeName(PackTy->getPattern(), OS, PP);
     OS << "...";
-  } else
+  } 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,
@@ -456,7 +834,8 @@
           Options.get("IgnoredNames", DefaultIgnoredParamNames))),
       IgnoredParamTypes(utils::options::parseStringList(
           Options.get("IgnoredTypes", DefaultIgnoredParamTypes))),
-      CVRMixPossible(Options.get("CVRMixPossible", false)) {}
+      CVRMixPossible(Options.get("CVRMixPossible", false)),
+      ImplicitConversion(Options.get("ImplicitConversion", false)) {}
 
 void CppcoreguidelinesAvoidAdjacentParametersOfTheSameTypeCheck::storeOptions(
     ClangTidyOptions::OptionMap &Opts) {
@@ -466,6 +845,7 @@
   Options.store(Opts, "IgnoredTypes",
                 utils::options::serializeStringList(IgnoredParamTypes));
   Options.store(Opts, "CVRMixPossible", CVRMixPossible);
+  Options.store(Opts, "ImplicitConversion", ImplicitConversion);
 }
 
 void CppcoreguidelinesAvoidAdjacentParametersOfTheSameTypeCheck::
@@ -495,16 +875,20 @@
 /// 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);
+  return M.Data.Flag & (MIXUP_Typedef | MIXUP_RefBind | MIXUP_CVR);
+}
+
+/// Returns whether the given Mixup, when diagnosed, should elaborate on
+/// implicit conversions.
+static bool NeedsToElaborateImplicitConversion(const Mixup &M) {
+  return M.Data.Flag & MIXUP_Implicit;
 }
 
 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
@@ -530,6 +914,8 @@
         MixingRange.getFirstParm()->getType().getAsString(PP);
 
     bool HasAnyTypePrint = llvm::any_of(MixingRange.Mixups, NeedsToPrintType);
+    bool HasAnyImplicits =
+        llvm::any_of(MixingRange.Mixups, NeedsToElaborateImplicitConversion);
 
     {
       const ParmVarDecl *RangeFirst = MixingRange.getFirstParm();
@@ -537,7 +923,12 @@
 
       {
         StringRef MainDiagnostic;
-        if (HasAnyTypePrint)
+        if (HasAnyImplicits)
+          MainDiagnostic =
+              "%0 adjacent parameters for '%1' of convertible types "
+              "may be easily swapped "
+              "by mistake";
+        else if (HasAnyTypePrint)
           MainDiagnostic = "%0 adjacent parameters for '%1' of similar type "
                            "are easily swapped "
                            "by mistake";
@@ -549,7 +940,7 @@
         auto Diag = diag(RangeFirst->getOuterLocStart(), MainDiagnostic)
                     << static_cast<unsigned>(MixingRange.NumParamsChecked)
                     << FunName;
-        if (!HasAnyTypePrint)
+        if (!HasAnyImplicits && !HasAnyTypePrint)
           Diag << MainParmTypeAsWritten;
       }
 
@@ -568,11 +959,11 @@
     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.");
+      assert(M.Data.Flag >= MIXUP_Trivial && "Too low bits in mixup type.");
       // For MIXUP_Trivial no extra diagnostics required.
 
       std::string FirstParmType, SecondParmType;
-      if (NeedsToPrintType(M)) {
+      if (NeedsToPrintType(M) || NeedsToElaborateImplicitConversion(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);
@@ -580,7 +971,7 @@
       }
 
       if (NeedsToPrintType(M)) {
-        if (M.Flags & MIXUP_Typedef) {
+        if (M.Data.Flag & MIXUP_Typedef) {
           // FIXME: Don't emit the typedef note for the parameter that isn't
           // actually a typedef.
           if (!TypedefResolutionPrintedForParm.count(M.First)) {
@@ -600,7 +991,7 @@
           }
         }
 
-        if (M.Flags & (MIXUP_RefBind | MIXUP_CVR)) {
+        if (M.Data.Flag & (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'",
@@ -610,6 +1001,80 @@
           }
         }
       }
+
+      if (NeedsToElaborateImplicitConversion(M)) {
+        // FIXME: This seems to be VERY VERBOSE in some cases...
+        const ConversionSequence &LTR = M.Data.ConvLTR, &RTL = M.Data.ConvRTL;
+        const auto DiagnoseSequence =
+            [&M, &PP](const std::string &StartType,
+                      const ConversionSequence &Seq) -> std::string {
+          assert((&Seq == &M.Data.ConvLTR || &Seq == &M.Data.ConvRTL) &&
+                 "sequence should be a sequence from the capture.");
+          llvm::SmallString<256> SS;
+          llvm::raw_svector_ostream OS{SS};
+
+          OS << '\'' << StartType << '\'';
+          if (Seq.StdPre)
+            OS << " -> '" << TypeNameAsString(QualType{Seq.StdPre, 0}, PP)
+               << '\'';
+          if (Seq.UserType)
+            OS << " -> '" << TypeNameAsString(QualType{Seq.UserType, 0}, PP)
+               << '\'';
+          if (Seq.StdPost)
+            OS << " -> '" << TypeNameAsString(QualType{Seq.StdPost, 0}, PP)
+               << '\'';
+
+          return OS.str().str();
+        };
+
+        if (LTR && RTL) {
+          std::string ConvMsg =
+              "'%0' and '%1' can suffer implicit conversions between one "
+              "another";
+          if (!LTR.single() && !RTL.single())
+            ConvMsg.append(": %2 and %3");
+          else if (!LTR.single())
+            ConvMsg.append(": %2 and trivially");
+          else if (!RTL.single())
+            ConvMsg.append(": trivially and %2");
+
+          auto Diag =
+              diag(M.Second->getOuterLocStart(), ConvMsg, DiagnosticIDs::Note)
+              << FirstParmType << SecondParmType;
+          if (!LTR.single() && !RTL.single())
+            Diag << DiagnoseSequence(FirstParmType, LTR)
+                 << DiagnoseSequence(SecondParmType, RTL);
+          else if (!LTR.single())
+            Diag << DiagnoseSequence(FirstParmType, LTR);
+          else if (!RTL.single())
+            Diag << DiagnoseSequence(SecondParmType, RTL);
+        } else {
+          std::string SeqDetails;
+          StringRef ConvMsg;
+          if (LTR) {
+            if (LTR.single())
+              ConvMsg = "'%0' can be implicitly converted to '%1'";
+            else {
+              ConvMsg = "'%0' can be implicitly converted to '%1': %2";
+              SeqDetails = DiagnoseSequence(FirstParmType, LTR);
+            }
+          } else if (RTL) {
+            if (RTL.single())
+              ConvMsg = "'%0' can be implicitly converted from '%1'";
+            else {
+              ConvMsg = "'%0' can be implicitly converted from '%1': %2";
+              SeqDetails = DiagnoseSequence(SecondParmType, RTL);
+            }
+          }
+          assert(!ConvMsg.empty() && "implicit bit set but we arrived here?");
+
+          auto Diag =
+              diag(M.Second->getOuterLocStart(), ConvMsg, DiagnosticIDs::Note)
+              << FirstParmType << SecondParmType;
+          if (!SeqDetails.empty())
+            Diag << SeqDetails;
+        }
+      }
     }
   }
 }
_______________________________________________
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to