1 // RUN: %check_clang_tidy -std=c++17 %s bugprone-easily-swappable-parameters %t \
2 // RUN:   -config='{CheckOptions: [ \
3 // RUN:     {key: bugprone-easily-swappable-parameters.MinimumLength, value: 2}, \
4 // RUN:     {key: bugprone-easily-swappable-parameters.IgnoredParameterNames, value: ""}, \
5 // RUN:     {key: bugprone-easily-swappable-parameters.IgnoredParameterTypeSuffixes, value: ""}, \
6 // RUN:     {key: bugprone-easily-swappable-parameters.QualifiersMix, value: 0}, \
7 // RUN:     {key: bugprone-easily-swappable-parameters.ModelImplicitConversions, value: 1}, \
8 // RUN:     {key: bugprone-easily-swappable-parameters.SuppressParametersUsedTogether, value: 0}, \
9 // RUN:     {key: bugprone-easily-swappable-parameters.NamePrefixSuffixSilenceDissimilarityTreshold, value: 0} \
10 // RUN:  ]}' --
11 
implicitDoesntBreakOtherStuff(int A,int B)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 
arrayAndPtr1(int * IP,int IA[])17 void arrayAndPtr1(int *IP, int IA[]) { arrayAndPtr1(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 
arrayAndPtr2(int * IP,int IA[8])22 void arrayAndPtr2(int *IP, int IA[8]) { arrayAndPtr2(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 
arrayAndElement(int I,int IA[])27 void arrayAndElement(int I, int IA[]) {} // NO-WARN.
28 
29 typedef int Point2D[2];
30 typedef int Point3D[3];
31 
arrays1(Point2D P2D,Point3D P3D)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 
crefToArrayTypedef1(int I,const Point2D & P)37 void crefToArrayTypedef1(int I, const Point2D &P) {}
38 // NO-WARN.
39 
crefToArrayTypedef2(int * IA,const Point2D & P)40 void crefToArrayTypedef2(int *IA, const Point2D &P) {}
41 // NO-WARN.
42 
crefToArrayTypedef3(int P1[2],const Point2D & P)43 void crefToArrayTypedef3(int P1[2], const Point2D &P) {}
44 // NO-WARN.
45 
crefToArrayTypedefBoth1(const Point2D & VecDescartes,const Point3D & VecThreeD)46 void crefToArrayTypedefBoth1(const Point2D &VecDescartes, const Point3D &VecThreeD) {}
47 // NO-WARN: Distinct types.
48 
49 template <int N, int M>
templatedArrayRef(int (& Array1)[N],int (& Array2)[M])50 void templatedArrayRef(int (&Array1)[N], int (&Array2)[M]) {}
51 // NO-WARN: Distinct template types in the primary template.
52 
templatedArrayRefTest()53 void templatedArrayRefTest() {
54   int Foo[12], Bar[12];
55   templatedArrayRef(Foo, Bar);
56 
57   int Baz[12], Quux[42];
58   templatedArrayRef(Baz, Quux);
59 
60   // NO-WARN: Implicit instantiations are not checked.
61 }
62 
63 template <>
templatedArrayRef(int (& Array1)[8],int (& Array2)[8])64 void templatedArrayRef(int (&Array1)[8], int (&Array2)[8]) { templatedArrayRef(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 <>
templatedArrayRef(int (& Array1)[16],int (& Array2)[24])70 void templatedArrayRef(int (&Array1)[16], int (&Array2)[24]) {}
71 // NO-WARN: Not the same type.
72 
numericConversion1(int I,double D)73 void numericConversion1(int I, double D) { numericConversion1(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 
numericConversion2(int I,short S)79 void numericConversion2(int I, short S) { numericConversion2(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 
numericConversion3(float F,unsigned long long ULL)85 void numericConversion3(float F, unsigned long long ULL) { numericConversion3(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 
numericConversion4(int I,Unscoped U)98 void numericConversion4(int I, Unscoped U) {} // NO-WARN.
99 
numericConversion5(int I,UnscopedFixed UF)100 void numericConversion5(int I, UnscopedFixed UF) {} // NO-WARN.
101 
numericConversion6(int I,Scoped S)102 void numericConversion6(int I, Scoped S) {} // NO-WARN.
103 
numericConversion7(double D,Unscoped U)104 void numericConversion7(double D, Unscoped U) {} // NO-WARN.
105 
numericConversion8(double D,UnscopedFixed UF)106 void numericConversion8(double D, UnscopedFixed UF) {} // NO-WARN.
107 
numericConversion9(double D,Scoped S)108 void numericConversion9(double D, Scoped S) {} // NO-WARN.
109 
numericConversionMultiUnique(int I,double D1,double D2)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 
numericConversion10(MyInt MI,MyDouble MD)120 void numericConversion10(MyInt MI, MyDouble MD) { numericConversion10(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 
numericAndQualifierConversion(int I,const double CD)126 void numericAndQualifierConversion(int I, const double CD) { numericAndQualifierConversion(CD, I); }
127 // NO-WARN: Qualifier mixing is handled by a different check option.
128 
129 struct FromInt {
130   FromInt(int);
131 };
132 
oneWayConversion1(int I,FromInt FI)133 void oneWayConversion1(int I, FromInt FI) {} // NO-WARN: One-way.
134 
135 struct AmbiguousConvCtor {
136   AmbiguousConvCtor(int);
137   AmbiguousConvCtor(double);
138 };
139 
ambiguous1(long L,AmbiguousConvCtor ACC)140 void ambiguous1(long L, AmbiguousConvCtor ACC) {} // NO-WARN: Ambiguous, one-way.
141 
142 struct ToInt {
143   operator int() const;
144 };
145 
oneWayConversion2(ToInt TI,int I)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 
ambiguous2(AmbiguousConvOp ACO,long L)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 
ambiguous3(AmbiguousEverything1 AE1,AmbiguousEverything2 AE2)168 void ambiguous3(AmbiguousEverything1 AE1, AmbiguousEverything2 AE2) {} // NO-WARN: Ambiguous.
169 
170 struct Integer {
171   Integer(int);
172   operator int() const;
173 };
174 
userDefinedConversion1(int I1,Integer I2)175 void userDefinedConversion1(int I1, Integer I2) { userDefinedConversion1(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 
ambiguous3(char C,Ambiguous A)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 
userDefinedConversion2(double D,CDouble CD)197 void userDefinedConversion2(double D, CDouble CD) { userDefinedConversion2(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 
userDefinedConversion3(int I,CDouble CD)205 void userDefinedConversion3(int I, CDouble CD) { userDefinedConversion3(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 
userDefinedConversion4(int I,TDInt TDI)218 void userDefinedConversion4(int I, TDInt TDI) { userDefinedConversion4(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 
userDefinedConversion5(int I,TDIntDouble TDID)233 void userDefinedConversion5(int I, TDIntDouble TDID) { userDefinedConversion5(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 
userDefinedConversion6(double D,TDIntDouble TDID)241 void userDefinedConversion6(double D, TDIntDouble TDID) { userDefinedConversion6(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 
userDefinedConversion7(char C,TDIntDouble TDID)249 void userDefinedConversion7(char C, TDIntDouble TDID) {} // NO-WARN: Ambiguous.
250 
251 struct Forward1;
252 struct Forward2;
253 
incomplete(Forward1 * F1,Forward2 * F2)254 void incomplete(Forward1 *F1, Forward2 *F2) {} // NO-WARN: Do not compare incomplete types.
255 
pointeeConverison(int * IP,double * DP)256 void pointeeConverison(int *IP, double *DP) {} // NO-WARN.
257 
pointerConversion1(void * VP,int * IP)258 void pointerConversion1(void *VP, int *IP) {} // NO-WARN: One-way.
259 
260 struct PointerBox {
261   PointerBox(void *);
262   operator int *() const;
263 };
264 
pointerConversion2(PointerBox PB,int * IP)265 void pointerConversion2(PointerBox PB, int *IP) { pointerConversion2(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 
pointerConversion3(PointerBox PB,double * DP)273 void pointerConversion3(PointerBox PB, double *DP) {} // NO-WARN: Not convertible.
274 
275 struct Base {};
276 struct Derived : Base {};
277 
pointerConversion4(Base * BP,Derived * DP)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 
pointerConversion5(BaseAndDerivedInverter BADI,Derived D)285 void pointerConversion5(BaseAndDerivedInverter BADI, Derived D) { pointerConversion5(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 
pointerConversion6(void (* NTF)()noexcept,void (* TF)())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 
pointerConversion7(void (* NTF)()noexcept,NoexceptMaker NM)306 void pointerConversion7(void (*NTF)() noexcept, NoexceptMaker NM) { pointerConversion7(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 
f(FromType F,ToType T)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(F2, 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 
templateConversion(IntConverter IC,FloatConverter FC)347 void templateConversion(IntConverter IC, FloatConverter FC) { templateConversion(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