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
12void 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
17void 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
22void 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
27void arrayAndElement(int I, int IA[]) {} // NO-WARN.
28
29typedef int Point2D[2];
30typedef int Point3D[3];
31
32void 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
37void crefToArrayTypedef1(int I, const Point2D &P) {}
38// NO-WARN.
39
40void crefToArrayTypedef2(int *IA, const Point2D &P) {}
41// NO-WARN.
42
43void crefToArrayTypedef3(int P1[2], const Point2D &P) {}
44// NO-WARN.
45
46void crefToArrayTypedefBoth1(const Point2D &VecDescartes, const Point3D &VecThreeD) {}
47// NO-WARN: Distinct types.
48
49template <int N, int M>
50void templatedArrayRef(int (&Array1)[N], int (&Array2)[M]) {}
51// NO-WARN: Distinct template types in the primary template.
52
53void 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
63template <>
64void 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
69template <>
70void templatedArrayRef(int (&Array1)[16], int (&Array2)[24]) {}
71// NO-WARN: Not the same type.
72
73void 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
79void 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
85void 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
91enum Unscoped { U_A,
92 U_B };
93enum UnscopedFixed : char { UF_A,
94 UF_B };
95enum struct Scoped { A,
96 B };
97
98void numericConversion4(int I, Unscoped U) {} // NO-WARN.
99
100void numericConversion5(int I, UnscopedFixed UF) {} // NO-WARN.
101
102void numericConversion6(int I, Scoped S) {} // NO-WARN.
103
104void numericConversion7(double D, Unscoped U) {} // NO-WARN.
105
106void numericConversion8(double D, UnscopedFixed UF) {} // NO-WARN.
107
108void numericConversion9(double D, Scoped S) {} // NO-WARN.
109
110void 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
117typedef int MyInt;
118using MyDouble = double;
119
120void 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
126void numericAndQualifierConversion(int I, const double CD) { numericAndQualifierConversion(I: CD, CD: I); }
127// NO-WARN: Qualifier mixing is handled by a different check option.
128
129struct FromInt {
130 FromInt(int);
131};
132
133void oneWayConversion1(int I, FromInt FI) {} // NO-WARN: One-way.
134
135struct AmbiguousConvCtor {
136 AmbiguousConvCtor(int);
137 AmbiguousConvCtor(double);
138};
139
140void ambiguous1(long L, AmbiguousConvCtor ACC) {} // NO-WARN: Ambiguous, one-way.
141
142struct ToInt {
143 operator int() const;
144};
145
146void oneWayConversion2(ToInt TI, int I) {} // NO-WARN: One-way.
147
148struct AmbiguousConvOp {
149 operator int() const;
150 operator double() const;
151};
152
153void ambiguous2(AmbiguousConvOp ACO, long L) {} // NO-WARN: Ambiguous, one-way.
154
155struct AmbiguousEverything1;
156struct AmbiguousEverything2;
157struct AmbiguousEverything1 {
158 AmbiguousEverything1();
159 AmbiguousEverything1(AmbiguousEverything2);
160 operator AmbiguousEverything2() const;
161};
162struct AmbiguousEverything2 {
163 AmbiguousEverything2();
164 AmbiguousEverything2(AmbiguousEverything1);
165 operator AmbiguousEverything1() const;
166};
167
168void ambiguous3(AmbiguousEverything1 AE1, AmbiguousEverything2 AE2) {} // NO-WARN: Ambiguous.
169
170struct Integer {
171 Integer(int);
172 operator int() const;
173};
174
175void 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
183struct Ambiguous {
184 Ambiguous(int);
185 Ambiguous(double);
186 operator long() const;
187 operator float() const;
188};
189
190void ambiguous3(char C, Ambiguous A) {} // NO-WARN: Ambiguous.
191
192struct CDouble {
193 CDouble(const double &);
194 operator const double &() const;
195};
196
197void 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
205void 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
213struct TDInt {
214 TDInt(const MyInt &);
215 operator MyInt() const;
216};
217
218void 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
226struct TDIntDouble {
227 TDIntDouble(const MyInt &);
228 TDIntDouble(const MyDouble &);
229 operator MyInt() const;
230 operator MyDouble() const;
231};
232
233void 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
241void 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
249void userDefinedConversion7(char C, TDIntDouble TDID) {} // NO-WARN: Ambiguous.
250
251struct Forward1;
252struct Forward2;
253
254void incomplete(Forward1 *F1, Forward2 *F2) {} // NO-WARN: Do not compare incomplete types.
255
256void pointeeConverison(int *IP, double *DP) {} // NO-WARN.
257
258void pointerConversion1(void *VP, int *IP) {} // NO-WARN: One-way.
259
260struct PointerBox {
261 PointerBox(void *);
262 operator int *() const;
263};
264
265void 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
273void pointerConversion3(PointerBox PB, double *DP) {} // NO-WARN: Not convertible.
274
275struct Base {};
276struct Derived : Base {};
277
278void pointerConversion4(Base *BP, Derived *DP) {} // NO-WARN: One-way.
279
280struct BaseAndDerivedInverter {
281 BaseAndDerivedInverter(Base); // Takes a Base
282 operator Derived() const; // and becomes a Derived.
283};
284
285void 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
293void pointerConversion6(void (*NTF)() noexcept, void (*TF)()) {}
294// NO-WARN: This call cannot be swapped, even if "getCanonicalType()" believes otherwise.
295
296using NonThrowingFunction = void (*)() noexcept;
297
298struct 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
306void 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
314struct ToType;
315struct MiddleStep1 {
316 operator ToType() const;
317};
318struct FromType {
319 operator MiddleStep1() const;
320};
321struct MiddleStep2 {
322 operator FromType() const;
323};
324struct ToType {
325 operator MiddleStep2() const;
326};
327
328void 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.
339template <typename T>
340struct TemplateConversion {
341 template <typename T2>
342 operator TemplateConversion<T2>() const;
343};
344using IntConverter = TemplateConversion<int>;
345using FloatConverter = TemplateConversion<float>;
346
347void 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

source code of clang-tools-extra/test/clang-tidy/checkers/bugprone/easily-swappable-parameters-implicits.cpp