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 | |