| 1 | // RUN: %check_clang_tidy -std=c++17-or-later %s bugprone-easily-swappable-parameters %t \ |
| 2 | // RUN: -config='{CheckOptions: { \ |
| 3 | // RUN: bugprone-easily-swappable-parameters.MinimumLength: 2, \ |
| 4 | // RUN: bugprone-easily-swappable-parameters.IgnoredParameterNames: "", \ |
| 5 | // RUN: bugprone-easily-swappable-parameters.IgnoredParameterTypeSuffixes: "", \ |
| 6 | // RUN: bugprone-easily-swappable-parameters.QualifiersMix: 0, \ |
| 7 | // RUN: bugprone-easily-swappable-parameters.ModelImplicitConversions: 1, \ |
| 8 | // RUN: bugprone-easily-swappable-parameters.SuppressParametersUsedTogether: 0, \ |
| 9 | // RUN: bugprone-easily-swappable-parameters.NamePrefixSuffixSilenceDissimilarityTreshold: 0 \ |
| 10 | // RUN: }}' -- |
| 11 | |
| 12 | void implicitDoesntBreakOtherStuff(int A, int B) {} |
| 13 | // CHECK-MESSAGES: :[[@LINE-1]]:36: warning: 2 adjacent parameters of 'implicitDoesntBreakOtherStuff' of similar type ('int') are easily swapped by mistake [bugprone-easily-swappable-parameters] |
| 14 | // CHECK-MESSAGES: :[[@LINE-2]]:40: note: the first parameter in the range is 'A' |
| 15 | // CHECK-MESSAGES: :[[@LINE-3]]:47: note: the last parameter in the range is 'B' |
| 16 | |
| 17 | void arrayAndPtr1(int *IP, int IA[]) { arrayAndPtr1(IP: IA, IA: IP); } |
| 18 | // CHECK-MESSAGES: :[[@LINE-1]]:19: warning: 2 adjacent parameters of 'arrayAndPtr1' of similar type ('int *') |
| 19 | // CHECK-MESSAGES: :[[@LINE-2]]:24: note: the first parameter in the range is 'IP' |
| 20 | // CHECK-MESSAGES: :[[@LINE-3]]:32: note: the last parameter in the range is 'IA' |
| 21 | |
| 22 | void arrayAndPtr2(int *IP, int IA[8]) { arrayAndPtr2(IP: IA, IA: IP); } |
| 23 | // CHECK-MESSAGES: :[[@LINE-1]]:19: warning: 2 adjacent parameters of 'arrayAndPtr2' of similar type ('int *') |
| 24 | // CHECK-MESSAGES: :[[@LINE-2]]:24: note: the first parameter in the range is 'IP' |
| 25 | // CHECK-MESSAGES: :[[@LINE-3]]:32: note: the last parameter in the range is 'IA' |
| 26 | |
| 27 | void arrayAndElement(int I, int IA[]) {} // NO-WARN. |
| 28 | |
| 29 | typedef int Point2D[2]; |
| 30 | typedef int Point3D[3]; |
| 31 | |
| 32 | void arrays1(Point2D P2D, Point3D P3D) {} // In reality this is (int*, int*). |
| 33 | // CHECK-MESSAGES: :[[@LINE-1]]:14: warning: 2 adjacent parameters of 'arrays1' of similar type ('int *') are |
| 34 | // CHECK-MESSAGES: :[[@LINE-2]]:22: note: the first parameter in the range is 'P2D' |
| 35 | // CHECK-MESSAGES: :[[@LINE-3]]:35: note: the last parameter in the range is 'P3D' |
| 36 | |
| 37 | void crefToArrayTypedef1(int I, const Point2D &P) {} |
| 38 | // NO-WARN. |
| 39 | |
| 40 | void crefToArrayTypedef2(int *IA, const Point2D &P) {} |
| 41 | // NO-WARN. |
| 42 | |
| 43 | void crefToArrayTypedef3(int P1[2], const Point2D &P) {} |
| 44 | // NO-WARN. |
| 45 | |
| 46 | void crefToArrayTypedefBoth1(const Point2D &VecDescartes, const Point3D &VecThreeD) {} |
| 47 | // NO-WARN: Distinct types. |
| 48 | |
| 49 | template <int N, int M> |
| 50 | void templatedArrayRef(int (&Array1)[N], int (&Array2)[M]) {} |
| 51 | // NO-WARN: Distinct template types in the primary template. |
| 52 | |
| 53 | void templatedArrayRefTest() { |
| 54 | int Foo[12], Bar[12]; |
| 55 | templatedArrayRef(Array1&: Foo, Array2&: Bar); |
| 56 | |
| 57 | int Baz[12], Quux[42]; |
| 58 | templatedArrayRef(Array1&: Baz, Array2&: Quux); |
| 59 | |
| 60 | // NO-WARN: Implicit instantiations are not checked. |
| 61 | } |
| 62 | |
| 63 | template <> |
| 64 | void templatedArrayRef(int (&Array1)[8], int (&Array2)[8]) { templatedArrayRef(Array1&: Array2, Array2&: Array1); } |
| 65 | // CHECK-MESSAGES: :[[@LINE-1]]:24: warning: 2 adjacent parameters of 'templatedArrayRef<8, 8>' of similar type ('int (&)[8]') are |
| 66 | // CHECK-MESSAGES: :[[@LINE-2]]:30: note: the first parameter in the range is 'Array1' |
| 67 | // CHECK-MESSAGES: :[[@LINE-3]]:48: note: the last parameter in the range is 'Array2' |
| 68 | |
| 69 | template <> |
| 70 | void templatedArrayRef(int (&Array1)[16], int (&Array2)[24]) {} |
| 71 | // NO-WARN: Not the same type. |
| 72 | |
| 73 | void numericConversion1(int I, double D) { numericConversion1(I: D, D: I); } |
| 74 | // CHECK-MESSAGES: :[[@LINE-1]]:25: warning: 2 adjacent parameters of 'numericConversion1' of convertible types are easily swapped by mistake [bugprone-easily-swappable-parameters] |
| 75 | // CHECK-MESSAGES: :[[@LINE-2]]:29: note: the first parameter in the range is 'I' |
| 76 | // CHECK-MESSAGES: :[[@LINE-3]]:39: note: the last parameter in the range is 'D' |
| 77 | // CHECK-MESSAGES: :[[@LINE-4]]:32: note: 'int' and 'double' may be implicitly converted{{$}} |
| 78 | |
| 79 | void numericConversion2(int I, short S) { numericConversion2(I: S, S: I); } |
| 80 | // CHECK-MESSAGES: :[[@LINE-1]]:25: warning: 2 adjacent parameters of 'numericConversion2' of convertible types |
| 81 | // CHECK-MESSAGES: :[[@LINE-2]]:29: note: the first parameter in the range is 'I' |
| 82 | // CHECK-MESSAGES: :[[@LINE-3]]:38: note: the last parameter in the range is 'S' |
| 83 | // CHECK-MESSAGES: :[[@LINE-4]]:32: note: 'int' and 'short' may be implicitly converted{{$}} |
| 84 | |
| 85 | void numericConversion3(float F, unsigned long long ULL) { numericConversion3(F: ULL, ULL: F); } |
| 86 | // CHECK-MESSAGES: :[[@LINE-1]]:25: warning: 2 adjacent parameters of 'numericConversion3' of convertible types |
| 87 | // CHECK-MESSAGES: :[[@LINE-2]]:31: note: the first parameter in the range is 'F' |
| 88 | // CHECK-MESSAGES: :[[@LINE-3]]:53: note: the last parameter in the range is 'ULL' |
| 89 | // CHECK-MESSAGES: :[[@LINE-4]]:34: note: 'float' and 'unsigned long long' may be implicitly converted{{$}} |
| 90 | |
| 91 | enum Unscoped { U_A, |
| 92 | U_B }; |
| 93 | enum UnscopedFixed : char { UF_A, |
| 94 | UF_B }; |
| 95 | enum struct Scoped { A, |
| 96 | B }; |
| 97 | |
| 98 | void numericConversion4(int I, Unscoped U) {} // NO-WARN. |
| 99 | |
| 100 | void numericConversion5(int I, UnscopedFixed UF) {} // NO-WARN. |
| 101 | |
| 102 | void numericConversion6(int I, Scoped S) {} // NO-WARN. |
| 103 | |
| 104 | void numericConversion7(double D, Unscoped U) {} // NO-WARN. |
| 105 | |
| 106 | void numericConversion8(double D, UnscopedFixed UF) {} // NO-WARN. |
| 107 | |
| 108 | void numericConversion9(double D, Scoped S) {} // NO-WARN. |
| 109 | |
| 110 | void numericConversionMultiUnique(int I, double D1, double D2) {} |
| 111 | // CHECK-MESSAGES: :[[@LINE-1]]:35: warning: 3 adjacent parameters of 'numericConversionMultiUnique' of convertible types |
| 112 | // CHECK-MESSAGES: :[[@LINE-2]]:39: note: the first parameter in the range is 'I' |
| 113 | // CHECK-MESSAGES: :[[@LINE-3]]:60: note: the last parameter in the range is 'D2' |
| 114 | // CHECK-MESSAGES: :[[@LINE-4]]:42: note: 'int' and 'double' may be implicitly converted{{$}} |
| 115 | // (Note: int<->double conversion for I<->D2 not diagnosed again.) |
| 116 | |
| 117 | typedef int MyInt; |
| 118 | using MyDouble = double; |
| 119 | |
| 120 | void numericConversion10(MyInt MI, MyDouble MD) { numericConversion10(MI: MD, MD: MI); } |
| 121 | // CHECK-MESSAGES: :[[@LINE-1]]:26: warning: 2 adjacent parameters of 'numericConversion10' of convertible types |
| 122 | // CHECK-MESSAGES: :[[@LINE-2]]:32: note: the first parameter in the range is 'MI' |
| 123 | // CHECK-MESSAGES: :[[@LINE-3]]:45: note: the last parameter in the range is 'MD' |
| 124 | // CHECK-MESSAGES: :[[@LINE-4]]:36: note: 'MyInt' and 'MyDouble' may be implicitly converted: 'MyInt' (as 'int') -> 'MyDouble' (as 'double'), 'MyDouble' (as 'double') -> 'MyInt' (as 'int') |
| 125 | |
| 126 | void numericAndQualifierConversion(int I, const double CD) { numericAndQualifierConversion(I: CD, CD: I); } |
| 127 | // NO-WARN: Qualifier mixing is handled by a different check option. |
| 128 | |
| 129 | struct FromInt { |
| 130 | FromInt(int); |
| 131 | }; |
| 132 | |
| 133 | void oneWayConversion1(int I, FromInt FI) {} // NO-WARN: One-way. |
| 134 | |
| 135 | struct AmbiguousConvCtor { |
| 136 | AmbiguousConvCtor(int); |
| 137 | AmbiguousConvCtor(double); |
| 138 | }; |
| 139 | |
| 140 | void ambiguous1(long L, AmbiguousConvCtor ACC) {} // NO-WARN: Ambiguous, one-way. |
| 141 | |
| 142 | struct ToInt { |
| 143 | operator int() const; |
| 144 | }; |
| 145 | |
| 146 | void oneWayConversion2(ToInt TI, int I) {} // NO-WARN: One-way. |
| 147 | |
| 148 | struct AmbiguousConvOp { |
| 149 | operator int() const; |
| 150 | operator double() const; |
| 151 | }; |
| 152 | |
| 153 | void ambiguous2(AmbiguousConvOp ACO, long L) {} // NO-WARN: Ambiguous, one-way. |
| 154 | |
| 155 | struct AmbiguousEverything1; |
| 156 | struct AmbiguousEverything2; |
| 157 | struct AmbiguousEverything1 { |
| 158 | AmbiguousEverything1(); |
| 159 | AmbiguousEverything1(AmbiguousEverything2); |
| 160 | operator AmbiguousEverything2() const; |
| 161 | }; |
| 162 | struct AmbiguousEverything2 { |
| 163 | AmbiguousEverything2(); |
| 164 | AmbiguousEverything2(AmbiguousEverything1); |
| 165 | operator AmbiguousEverything1() const; |
| 166 | }; |
| 167 | |
| 168 | void ambiguous3(AmbiguousEverything1 AE1, AmbiguousEverything2 AE2) {} // NO-WARN: Ambiguous. |
| 169 | |
| 170 | struct Integer { |
| 171 | Integer(int); |
| 172 | operator int() const; |
| 173 | }; |
| 174 | |
| 175 | void userDefinedConversion1(int I1, Integer I2) { userDefinedConversion1(I1: I2, I2: I1); } |
| 176 | // CHECK-MESSAGES: :[[@LINE-1]]:29: warning: 2 adjacent parameters of 'userDefinedConversion1' of convertible types |
| 177 | // CHECK-MESSAGES: :[[@LINE-2]]:33: note: the first parameter in the range is 'I1' |
| 178 | // CHECK-MESSAGES: :[[@LINE-3]]:45: note: the last parameter in the range is 'I2' |
| 179 | // CHECK-MESSAGES: :[[@LINE-4]]:37: note: 'int' and 'Integer' may be implicitly converted{{$}} |
| 180 | // CHECK-MESSAGES: :[[@LINE-9]]:3: note: the implicit conversion involves the converting constructor declared here |
| 181 | // CHECK-MESSAGES: :[[@LINE-9]]:3: note: the implicit conversion involves the conversion operator declared here |
| 182 | |
| 183 | struct Ambiguous { |
| 184 | Ambiguous(int); |
| 185 | Ambiguous(double); |
| 186 | operator long() const; |
| 187 | operator float() const; |
| 188 | }; |
| 189 | |
| 190 | void ambiguous3(char C, Ambiguous A) {} // NO-WARN: Ambiguous. |
| 191 | |
| 192 | struct CDouble { |
| 193 | CDouble(const double &); |
| 194 | operator const double &() const; |
| 195 | }; |
| 196 | |
| 197 | void userDefinedConversion2(double D, CDouble CD) { userDefinedConversion2(D: CD, CD: D); } |
| 198 | // CHECK-MESSAGES: :[[@LINE-1]]:29: warning: 2 adjacent parameters of 'userDefinedConversion2' of convertible types |
| 199 | // CHECK-MESSAGES: :[[@LINE-2]]:36: note: the first parameter in the range is 'D' |
| 200 | // CHECK-MESSAGES: :[[@LINE-3]]:47: note: the last parameter in the range is 'CD' |
| 201 | // CHECK-MESSAGES: :[[@LINE-4]]:39: note: 'double' and 'CDouble' may be implicitly converted: 'double' -> 'const double &' -> 'CDouble', 'CDouble' -> 'const double &' -> 'double' |
| 202 | // CHECK-MESSAGES: :[[@LINE-9]]:3: note: the implicit conversion involves the converting constructor declared here |
| 203 | // CHECK-MESSAGES: :[[@LINE-9]]:3: note: the implicit conversion involves the conversion operator declared here |
| 204 | |
| 205 | void userDefinedConversion3(int I, CDouble CD) { userDefinedConversion3(I: CD, CD: I); } |
| 206 | // CHECK-MESSAGES: :[[@LINE-1]]:29: warning: 2 adjacent parameters of 'userDefinedConversion3' of convertible types |
| 207 | // CHECK-MESSAGES: :[[@LINE-2]]:33: note: the first parameter in the range is 'I' |
| 208 | // CHECK-MESSAGES: :[[@LINE-3]]:44: note: the last parameter in the range is 'CD' |
| 209 | // CHECK-MESSAGES: :[[@LINE-4]]:36: note: 'int' and 'CDouble' may be implicitly converted: 'int' -> 'double' -> 'const double &' -> 'CDouble', 'CDouble' -> 'const double &' -> 'int' |
| 210 | // CHECK-MESSAGES: :[[@LINE-17]]:3: note: the implicit conversion involves the converting constructor declared here |
| 211 | // CHECK-MESSAGES: :[[@LINE-17]]:3: note: the implicit conversion involves the conversion operator declared here |
| 212 | |
| 213 | struct TDInt { |
| 214 | TDInt(const MyInt &); |
| 215 | operator MyInt() const; |
| 216 | }; |
| 217 | |
| 218 | void userDefinedConversion4(int I, TDInt TDI) { userDefinedConversion4(I: TDI, TDI: I); } |
| 219 | // CHECK-MESSAGES: :[[@LINE-1]]:29: warning: 2 adjacent parameters of 'userDefinedConversion4' of convertible types |
| 220 | // CHECK-MESSAGES: :[[@LINE-2]]:33: note: the first parameter in the range is 'I' |
| 221 | // CHECK-MESSAGES: :[[@LINE-3]]:42: note: the last parameter in the range is 'TDI' |
| 222 | // CHECK-MESSAGES: :[[@LINE-4]]:36: note: 'int' and 'TDInt' may be implicitly converted: 'int' -> 'const MyInt &' -> 'TDInt', 'TDInt' -> 'MyInt' -> 'int' |
| 223 | // CHECK-MESSAGES: :[[@LINE-9]]:3: note: the implicit conversion involves the converting constructor declared here |
| 224 | // CHECK-MESSAGES: :[[@LINE-9]]:3: note: the implicit conversion involves the conversion operator declared here |
| 225 | |
| 226 | struct TDIntDouble { |
| 227 | TDIntDouble(const MyInt &); |
| 228 | TDIntDouble(const MyDouble &); |
| 229 | operator MyInt() const; |
| 230 | operator MyDouble() const; |
| 231 | }; |
| 232 | |
| 233 | void userDefinedConversion5(int I, TDIntDouble TDID) { userDefinedConversion5(I: TDID, TDID: I); } |
| 234 | // CHECK-MESSAGES: :[[@LINE-1]]:29: warning: 2 adjacent parameters of 'userDefinedConversion5' of convertible types |
| 235 | // CHECK-MESSAGES: :[[@LINE-2]]:33: note: the first parameter in the range is 'I' |
| 236 | // CHECK-MESSAGES: :[[@LINE-3]]:48: note: the last parameter in the range is 'TDID' |
| 237 | // CHECK-MESSAGES: :[[@LINE-4]]:36: note: 'int' and 'TDIntDouble' may be implicitly converted: 'int' -> 'const MyInt &' -> 'TDIntDouble', 'TDIntDouble' -> 'MyInt' -> 'int' |
| 238 | // CHECK-MESSAGES: :[[@LINE-11]]:3: note: the implicit conversion involves the converting constructor declared here |
| 239 | // CHECK-MESSAGES: :[[@LINE-10]]:3: note: the implicit conversion involves the conversion operator declared here |
| 240 | |
| 241 | void userDefinedConversion6(double D, TDIntDouble TDID) { userDefinedConversion6(D: TDID, TDID: D); } |
| 242 | // CHECK-MESSAGES: :[[@LINE-1]]:29: warning: 2 adjacent parameters of 'userDefinedConversion6' of convertible types |
| 243 | // CHECK-MESSAGES: :[[@LINE-2]]:36: note: the first parameter in the range is 'D' |
| 244 | // CHECK-MESSAGES: :[[@LINE-3]]:51: note: the last parameter in the range is 'TDID' |
| 245 | // CHECK-MESSAGES: :[[@LINE-4]]:39: note: 'double' and 'TDIntDouble' may be implicitly converted: 'double' -> 'const MyDouble &' -> 'TDIntDouble', 'TDIntDouble' -> 'MyDouble' -> 'double' |
| 246 | // CHECK-MESSAGES: :[[@LINE-18]]:3: note: the implicit conversion involves the converting constructor declared here |
| 247 | // CHECK-MESSAGES: :[[@LINE-17]]:3: note: the implicit conversion involves the conversion operator declared here |
| 248 | |
| 249 | void userDefinedConversion7(char C, TDIntDouble TDID) {} // NO-WARN: Ambiguous. |
| 250 | |
| 251 | struct Forward1; |
| 252 | struct Forward2; |
| 253 | |
| 254 | void incomplete(Forward1 *F1, Forward2 *F2) {} // NO-WARN: Do not compare incomplete types. |
| 255 | |
| 256 | void pointeeConverison(int *IP, double *DP) {} // NO-WARN. |
| 257 | |
| 258 | void pointerConversion1(void *VP, int *IP) {} // NO-WARN: One-way. |
| 259 | |
| 260 | struct PointerBox { |
| 261 | PointerBox(void *); |
| 262 | operator int *() const; |
| 263 | }; |
| 264 | |
| 265 | void pointerConversion2(PointerBox PB, int *IP) { pointerConversion2(PB: IP, IP: PB); } |
| 266 | // CHECK-MESSAGES: :[[@LINE-1]]:25: warning: 2 adjacent parameters of 'pointerConversion2' of convertible types |
| 267 | // CHECK-MESSAGES: :[[@LINE-2]]:36: note: the first parameter in the range is 'PB' |
| 268 | // CHECK-MESSAGES: :[[@LINE-3]]:45: note: the last parameter in the range is 'IP' |
| 269 | // CHECK-MESSAGES: :[[@LINE-4]]:40: note: 'PointerBox' and 'int *' may be implicitly converted: 'PointerBox' -> 'int *', 'int *' -> 'void *' -> 'PointerBox' |
| 270 | // CHECK-MESSAGES: :[[@LINE-8]]:3: note: the implicit conversion involves the conversion operator declared here |
| 271 | // CHECK-MESSAGES: :[[@LINE-10]]:3: note: the implicit conversion involves the converting constructor declared here |
| 272 | |
| 273 | void pointerConversion3(PointerBox PB, double *DP) {} // NO-WARN: Not convertible. |
| 274 | |
| 275 | struct Base {}; |
| 276 | struct Derived : Base {}; |
| 277 | |
| 278 | void pointerConversion4(Base *BP, Derived *DP) {} // NO-WARN: One-way. |
| 279 | |
| 280 | struct BaseAndDerivedInverter { |
| 281 | BaseAndDerivedInverter(Base); // Takes a Base |
| 282 | operator Derived() const; // and becomes a Derived. |
| 283 | }; |
| 284 | |
| 285 | void pointerConversion5(BaseAndDerivedInverter BADI, Derived D) { pointerConversion5(BADI: D, D: BADI); } |
| 286 | // CHECK-MESSAGES: :[[@LINE-1]]:25: warning: 2 adjacent parameters of 'pointerConversion5' of convertible types |
| 287 | // CHECK-MESSAGES: :[[@LINE-2]]:48: note: the first parameter in the range is 'BADI' |
| 288 | // CHECK-MESSAGES: :[[@LINE-3]]:62: note: the last parameter in the range is 'D' |
| 289 | // CHECK-MESSAGES: :[[@LINE-4]]:54: note: 'BaseAndDerivedInverter' and 'Derived' may be implicitly converted: 'BaseAndDerivedInverter' -> 'Derived', 'Derived' -> 'Base' -> 'BaseAndDerivedInverter' |
| 290 | // CHECK-MESSAGES: :[[@LINE-8]]:3: note: the implicit conversion involves the conversion operator declared here |
| 291 | // CHECK-MESSAGES: :[[@LINE-10]]:3: note: the implicit conversion involves the converting constructor declared here |
| 292 | |
| 293 | void pointerConversion6(void (*NTF)() noexcept, void (*TF)()) {} |
| 294 | // NO-WARN: This call cannot be swapped, even if "getCanonicalType()" believes otherwise. |
| 295 | |
| 296 | using NonThrowingFunction = void (*)() noexcept; |
| 297 | |
| 298 | struct NoexceptMaker { |
| 299 | NoexceptMaker(void (*ThrowingFunction)()); |
| 300 | // Need to use a typedef here because |
| 301 | // "conversion function cannot convert to a function type". |
| 302 | // operator (void (*)() noexcept) () const; |
| 303 | operator NonThrowingFunction() const; |
| 304 | }; |
| 305 | |
| 306 | void pointerConversion7(void (*NTF)() noexcept, NoexceptMaker NM) { pointerConversion7(NTF: NM, NM: NTF); } |
| 307 | // CHECK-MESSAGES: :[[@LINE-1]]:25: warning: 2 adjacent parameters of 'pointerConversion7' of convertible types |
| 308 | // CHECK-MESSAGES: :[[@LINE-2]]:32: note: the first parameter in the range is 'NTF' |
| 309 | // CHECK-MESSAGES: :[[@LINE-3]]:63: note: the last parameter in the range is 'NM' |
| 310 | // CHECK-MESSAGES: :[[@LINE-4]]:49: note: 'void (*)() noexcept' and 'NoexceptMaker' may be implicitly converted: 'void (*)() noexcept' -> 'void (*)()' -> 'NoexceptMaker', 'NoexceptMaker' -> 'NonThrowingFunction' -> 'void (*)() noexcept' |
| 311 | // CHECK-MESSAGES: :[[@LINE-12]]:3: note: the implicit conversion involves the converting constructor declared here |
| 312 | // CHECK-MESSAGES: :[[@LINE-9]]:3: note: the implicit conversion involves the conversion operator declared here |
| 313 | |
| 314 | struct ToType; |
| 315 | struct MiddleStep1 { |
| 316 | operator ToType() const; |
| 317 | }; |
| 318 | struct FromType { |
| 319 | operator MiddleStep1() const; |
| 320 | }; |
| 321 | struct MiddleStep2 { |
| 322 | operator FromType() const; |
| 323 | }; |
| 324 | struct ToType { |
| 325 | operator MiddleStep2() const; |
| 326 | }; |
| 327 | |
| 328 | void f(FromType F, ToType T) { // NO-WARN: The path takes two steps. |
| 329 | MiddleStep2 MS2 = T; |
| 330 | FromType F2 = MS2; |
| 331 | |
| 332 | MiddleStep1 MS1 = F; |
| 333 | ToType T2 = MS1; |
| 334 | |
| 335 | f(F: F2, T: T2); |
| 336 | } |
| 337 | |
| 338 | // Synthesised example from OpenCV. |
| 339 | template <typename T> |
| 340 | struct TemplateConversion { |
| 341 | template <typename T2> |
| 342 | operator TemplateConversion<T2>() const; |
| 343 | }; |
| 344 | using IntConverter = TemplateConversion<int>; |
| 345 | using FloatConverter = TemplateConversion<float>; |
| 346 | |
| 347 | void templateConversion(IntConverter IC, FloatConverter FC) { templateConversion(IC: FC, FC: IC); } |
| 348 | // Note: even though this swap is possible, we do not model things when it comes to "template magic". |
| 349 | // But at least the check should not crash! |
| 350 | |