[clang-tidy] Extend 'bugprone-easily-swappable-parameters' with mixability because of implicit conversions

Adds a relaxation option ModelImplicitConversions which will make the check
report for cases where parameters refer to types that are implicitly
convertible to one another.

Example:

    struct IntBox { IntBox(int); operator int(); };
    void foo(int i, double d, IntBox ib) {}

Implicit conversions are the last to model in the set of things that are
reasons for the possibility of a function being called the wrong way which is
not always immediately apparent when looking at the function (signature or
call).

Reviewed By: aaron.ballman, martong

Differential Revision: http://reviews.llvm.org/D75041
This commit is contained in:
Whisperity 2019-12-17 18:00:08 +01:00
parent 961e9e6af6
commit e33d047883
11 changed files with 1544 additions and 104 deletions

View file

@ -39,8 +39,14 @@ public:
/// ignored.
const std::vector<std::string> IgnoredParameterTypeSuffixes;
/// Whether to consider an unqualified and a qualified type mixable.
/// Whether to consider differently qualified versions of the same type
/// mixable.
const bool QualifiersMix;
/// Whether to model implicit conversions "in full" (conditions apply)
/// during analysis and consider types that are implicitly convertible to
/// one another mixable.
const bool ModelImplicitConversions;
};
} // namespace bugprone

View file

@ -57,8 +57,40 @@ report longer mixable ranges.
.. code-block:: c++
void *memcpy(const void *Destination, void *Source, std::size_t N) {}
void *memcpy(const void *Destination, void *Source, std::size_t N) { /* ... */ }
.. option:: ModelImplicitConversions
Whether to consider parameters of type ``T`` and ``U`` mixable if there
exists an implicit conversion from ``T`` to ``U`` and ``U`` to ``T``.
If `false`, the check will not consider implicitly convertible types for
mixability.
`True` turns warnings for implicit conversions on.
Defaults to `true`.
The following examples produce a diagnostic only if
`ModelImplicitConversions` is enabled:
.. code-block:: c++
void fun(int Int, double Double) { /* ... */ }
void compare(const char *CharBuf, std::string String) { /* ... */ }
.. note::
Changing the qualifiers of an expression's type (e.g. from ``int`` to
``const int``) is defined as an *implicit conversion* in the C++
Standard.
However, the check separates this decision-making on the mixability of
differently qualified types based on whether `QualifiersMix` was
enabled.
For example, the following code snippet will only produce a diagnostic
if **both** `QualifiersMix` and `ModelImplicitConversions` are enabled:
.. code-block:: c++
void fun2(int Int, const double Double) { /* ... */ }
Filtering options
^^^^^^^^^^^^^^^^^
@ -133,7 +165,7 @@ None of the following cases produce a diagnostic:
template <typename T, typename U>
int add(T X, U Y) { return X + Y };
void TheseAreNotWarnedAbout() {
void theseAreNotWarnedAbout() {
printf("%d %d\n", 1, 2); // Two ints passed, they could be swapped.
someOldCFunction(1, 2, 3); // Similarly, multiple ints passed.
@ -153,14 +185,43 @@ not diagnosed.
// Diagnosed: Explicit instantiation was done by the user, we can prove it
// is the same type.
void Explicit(int A, Vector<int>::element_type B) { /* ... */ }
void instantiated(int A, Vector<int>::element_type B) { /* ... */ }
// Diagnosed: The two parameter types are exactly the same.
template <typename T>
void Exact(typename Vector<T>::element_type A,
void exact(typename Vector<T>::element_type A,
typename Vector<T>::element_type B) { /* ... */ }
// Skipped: The two parameters are both 'T' but we can not prove this
// without actually instantiating.
template <typename T>
void FalseNegative(T A, typename Vector<T>::element_type B) { /* ... */ }
void falseNegative(T A, typename Vector<T>::element_type B) { /* ... */ }
In the context of *implicit conversions* (when `ModelImplicitConversions` is
enabled), the modelling performed by the check
warns if the parameters are swappable and the swapped order matches implicit
conversions.
It does not model whether there exists an unrelated third type from which
*both* parameters can be given in a function call.
This means that in the following example, even while ``strs()`` clearly carries
the possibility to be called with swapped arguments (as long as the arguments
are string literals), will not be warned about.
.. code-block:: c++
struct String {
String(const char *Buf);
};
struct StringView {
StringView(const char *Buf);
operator const char *() const;
};
// Skipped: Directly swapping expressions of the two type cannot mix.
// (Note: StringView -> const char * -> String would be **two**
// user-defined conversions, which is disallowed by the language.)
void strs(String Str, StringView SV) { /* ... */ }
// Diagnosed: StringView implicitly converts to and from a buffer.
void cStr(StringView SV, const char *Buf() { /* ... */ }

View file

@ -3,7 +3,8 @@
// RUN: {key: bugprone-easily-swappable-parameters.MinimumLength, value: 2}, \
// RUN: {key: bugprone-easily-swappable-parameters.IgnoredParameterNames, value: "\"\";Foo;Bar"}, \
// RUN: {key: bugprone-easily-swappable-parameters.IgnoredParameterTypeSuffixes, value: "T"}, \
// RUN: {key: bugprone-easily-swappable-parameters.QualifiersMix, value: 0} \
// RUN: {key: bugprone-easily-swappable-parameters.QualifiersMix, value: 0}, \
// RUN: {key: bugprone-easily-swappable-parameters.ModelImplicitConversions, value: 0} \
// RUN: ]}' --
void ignoredUnnamed(int I, int, int) {} // NO-WARN: No >= 2 length of non-unnamed.

View file

@ -0,0 +1,15 @@
// RUN: %check_clang_tidy %s bugprone-easily-swappable-parameters %t \
// RUN: -config='{CheckOptions: [ \
// RUN: {key: bugprone-easily-swappable-parameters.MinimumLength, value: 2}, \
// RUN: {key: bugprone-easily-swappable-parameters.IgnoredParameterNames, value: ""}, \
// RUN: {key: bugprone-easily-swappable-parameters.IgnoredParameterTypeSuffixes, value: ""}, \
// RUN: {key: bugprone-easily-swappable-parameters.QualifiersMix, value: 1}, \
// RUN: {key: bugprone-easily-swappable-parameters.ModelImplicitConversions, value: 1} \
// RUN: ]}' --
void numericAndQualifierConversion(int I, const double CD) { numericAndQualifierConversion(CD, I); }
// CHECK-MESSAGES: :[[@LINE-1]]:36: warning: 2 adjacent parameters of 'numericAndQualifierConversion' of convertible types are easily swapped by mistake [bugprone-easily-swappable-parameters]
// CHECK-MESSAGES: :[[@LINE-2]]:40: note: the first parameter in the range is 'I'
// CHECK-MESSAGES: :[[@LINE-3]]:56: note: the last parameter in the range is 'CD'
// CHECK-MESSAGES: :[[@LINE-4]]:43: note: 'int' and 'const double' parameters accept and bind the same kind of values
// CHECK-MESSAGES: :[[@LINE-5]]:43: note: 'int' and 'const double' may be implicitly converted: 'int' -> 'const double' (as 'double'), 'const double' (as 'double') -> 'int'

View file

@ -0,0 +1,75 @@
// RUN: %check_clang_tidy %s bugprone-easily-swappable-parameters %t \
// RUN: -config='{CheckOptions: [ \
// RUN: {key: bugprone-easily-swappable-parameters.MinimumLength, value: 2}, \
// RUN: {key: bugprone-easily-swappable-parameters.IgnoredParameterNames, value: ""}, \
// RUN: {key: bugprone-easily-swappable-parameters.IgnoredParameterTypeSuffixes, value: ""}, \
// RUN: {key: bugprone-easily-swappable-parameters.QualifiersMix, value: 0}, \
// RUN: {key: bugprone-easily-swappable-parameters.ModelImplicitConversions, value: 1} \
// RUN: ]}' --
void implicitDoesntBreakOtherStuff(int A, int B) {}
// CHECK-MESSAGES: :[[@LINE-1]]:36: warning: 2 adjacent parameters of 'implicitDoesntBreakOtherStuff' of similar type ('int') are easily swapped by mistake [bugprone-easily-swappable-parameters]
// CHECK-MESSAGES: :[[@LINE-2]]:40: note: the first parameter in the range is 'A'
// CHECK-MESSAGES: :[[@LINE-3]]:47: note: the last parameter in the range is 'B'
void arrayAndPtr1(int *IP, int IA[]) { arrayAndPtr1(IA, IP); }
// CHECK-MESSAGES: :[[@LINE-1]]:19: warning: 2 adjacent parameters of 'arrayAndPtr1' of similar type ('int *')
// CHECK-MESSAGES: :[[@LINE-2]]:24: note: the first parameter in the range is 'IP'
// CHECK-MESSAGES: :[[@LINE-3]]:32: note: the last parameter in the range is 'IA'
void arrayAndPtr2(int *IP, int IA[8]) { arrayAndPtr2(IA, IP); }
// CHECK-MESSAGES: :[[@LINE-1]]:19: warning: 2 adjacent parameters of 'arrayAndPtr2' of similar type ('int *')
// CHECK-MESSAGES: :[[@LINE-2]]:24: note: the first parameter in the range is 'IP'
// CHECK-MESSAGES: :[[@LINE-3]]:32: note: the last parameter in the range is 'IA'
void arrayAndElement(int I, int IA[]) {} // NO-WARN.
void numericConversion1(int I, double D) { numericConversion1(D, I); }
// CHECK-MESSAGES: :[[@LINE-1]]:25: warning: 2 adjacent parameters of 'numericConversion1' of convertible types are easily swapped by mistake [bugprone-easily-swappable-parameters]
// CHECK-MESSAGES: :[[@LINE-2]]:29: note: the first parameter in the range is 'I'
// CHECK-MESSAGES: :[[@LINE-3]]:39: note: the last parameter in the range is 'D'
// CHECK-MESSAGES: :[[@LINE-4]]:32: note: 'int' and 'double' may be implicitly converted{{$}}
void numericConversion2(int I, short S) { numericConversion2(S, I); }
// CHECK-MESSAGES: :[[@LINE-1]]:25: warning: 2 adjacent parameters of 'numericConversion2' of convertible types
// CHECK-MESSAGES: :[[@LINE-2]]:29: note: the first parameter in the range is 'I'
// CHECK-MESSAGES: :[[@LINE-3]]:38: note: the last parameter in the range is 'S'
// CHECK-MESSAGES: :[[@LINE-4]]:32: note: 'int' and 'short' may be implicitly converted{{$}}
void numericConversion3(float F, unsigned long UL) { numericConversion3(UL, F); }
// CHECK-MESSAGES: :[[@LINE-1]]:25: warning: 2 adjacent parameters of 'numericConversion3' of convertible types
// CHECK-MESSAGES: :[[@LINE-2]]:31: note: the first parameter in the range is 'F'
// CHECK-MESSAGES: :[[@LINE-3]]:48: note: the last parameter in the range is 'UL'
// CHECK-MESSAGES: :[[@LINE-4]]:34: note: 'float' and 'unsigned long' may be implicitly converted{{$}}
enum Unscoped { U_A,
U_B };
enum UnscopedFixed : char { UF_A,
UF_B };
void numericConversion4(int I, enum Unscoped U) { numericConversion4(U, I); }
// CHECK-MESSAGES: :[[@LINE-1]]:25: warning: 2 adjacent parameters of 'numericConversion4' of convertible types
// CHECK-MESSAGES: :[[@LINE-2]]:29: note: the first parameter in the range is 'I'
// CHECK-MESSAGES: :[[@LINE-3]]:46: note: the last parameter in the range is 'U'
// CHECK-MESSAGES: :[[@LINE-4]]:32: note: 'int' and 'enum Unscoped' may be implicitly converted{{$}}
void numericConversion5(int I, enum UnscopedFixed UF) { numericConversion5(UF, I); }
// CHECK-MESSAGES: :[[@LINE-1]]:25: warning: 2 adjacent parameters of 'numericConversion5' of convertible types
// CHECK-MESSAGES: :[[@LINE-2]]:29: note: the first parameter in the range is 'I'
// CHECK-MESSAGES: :[[@LINE-3]]:51: note: the last parameter in the range is 'UF'
// CHECK-MESSAGES: :[[@LINE-4]]:32: note: 'int' and 'enum UnscopedFixed' may be implicitly converted{{$}}
void numericConversion7(double D, enum Unscoped U) { numericConversion7(U, D); }
// CHECK-MESSAGES: :[[@LINE-1]]:25: warning: 2 adjacent parameters of 'numericConversion7' of convertible types
// CHECK-MESSAGES: :[[@LINE-2]]:32: note: the first parameter in the range is 'D'
// CHECK-MESSAGES: :[[@LINE-3]]:49: note: the last parameter in the range is 'U'
// CHECK-MESSAGES: :[[@LINE-4]]:35: note: 'double' and 'enum Unscoped' may be implicitly converted{{$}}
void numericConversion8(double D, enum UnscopedFixed UF) { numericConversion8(UF, D); }
// CHECK-MESSAGES: :[[@LINE-1]]:25: warning: 2 adjacent parameters of 'numericConversion8' of convertible types
// CHECK-MESSAGES: :[[@LINE-2]]:32: note: the first parameter in the range is 'D'
// CHECK-MESSAGES: :[[@LINE-3]]:54: note: the last parameter in the range is 'UF'
// CHECK-MESSAGES: :[[@LINE-4]]:35: note: 'double' and 'enum UnscopedFixed' may be implicitly converted{{$}}
void pointeeConverison(int *IP, double *DP) { pointeeConversion(DP, IP); }
// NO-WARN: Even though this is possible in C, a swap is diagnosed by the compiler.

View file

@ -0,0 +1,303 @@
// RUN: %check_clang_tidy -std=c++17 %s bugprone-easily-swappable-parameters %t \
// RUN: -config='{CheckOptions: [ \
// RUN: {key: bugprone-easily-swappable-parameters.MinimumLength, value: 2}, \
// RUN: {key: bugprone-easily-swappable-parameters.IgnoredParameterNames, value: ""}, \
// RUN: {key: bugprone-easily-swappable-parameters.IgnoredParameterTypeSuffixes, value: ""}, \
// RUN: {key: bugprone-easily-swappable-parameters.QualifiersMix, value: 0}, \
// RUN: {key: bugprone-easily-swappable-parameters.ModelImplicitConversions, value: 1} \
// RUN: ]}' --
void implicitDoesntBreakOtherStuff(int A, int B) {}
// CHECK-MESSAGES: :[[@LINE-1]]:36: warning: 2 adjacent parameters of 'implicitDoesntBreakOtherStuff' of similar type ('int') are easily swapped by mistake [bugprone-easily-swappable-parameters]
// CHECK-MESSAGES: :[[@LINE-2]]:40: note: the first parameter in the range is 'A'
// CHECK-MESSAGES: :[[@LINE-3]]:47: note: the last parameter in the range is 'B'
void arrayAndPtr1(int *IP, int IA[]) { arrayAndPtr1(IA, IP); }
// CHECK-MESSAGES: :[[@LINE-1]]:19: warning: 2 adjacent parameters of 'arrayAndPtr1' of similar type ('int *')
// CHECK-MESSAGES: :[[@LINE-2]]:24: note: the first parameter in the range is 'IP'
// CHECK-MESSAGES: :[[@LINE-3]]:32: note: the last parameter in the range is 'IA'
void arrayAndPtr2(int *IP, int IA[8]) { arrayAndPtr2(IA, IP); }
// CHECK-MESSAGES: :[[@LINE-1]]:19: warning: 2 adjacent parameters of 'arrayAndPtr2' of similar type ('int *')
// CHECK-MESSAGES: :[[@LINE-2]]:24: note: the first parameter in the range is 'IP'
// CHECK-MESSAGES: :[[@LINE-3]]:32: note: the last parameter in the range is 'IA'
void arrayAndElement(int I, int IA[]) {} // NO-WARN.
void numericConversion1(int I, double D) { numericConversion1(D, I); }
// CHECK-MESSAGES: :[[@LINE-1]]:25: warning: 2 adjacent parameters of 'numericConversion1' of convertible types are easily swapped by mistake [bugprone-easily-swappable-parameters]
// CHECK-MESSAGES: :[[@LINE-2]]:29: note: the first parameter in the range is 'I'
// CHECK-MESSAGES: :[[@LINE-3]]:39: note: the last parameter in the range is 'D'
// CHECK-MESSAGES: :[[@LINE-4]]:32: note: 'int' and 'double' may be implicitly converted{{$}}
void numericConversion2(int I, short S) { numericConversion2(S, I); }
// CHECK-MESSAGES: :[[@LINE-1]]:25: warning: 2 adjacent parameters of 'numericConversion2' of convertible types
// CHECK-MESSAGES: :[[@LINE-2]]:29: note: the first parameter in the range is 'I'
// CHECK-MESSAGES: :[[@LINE-3]]:38: note: the last parameter in the range is 'S'
// CHECK-MESSAGES: :[[@LINE-4]]:32: note: 'int' and 'short' may be implicitly converted{{$}}
void numericConversion3(float F, unsigned long long ULL) { numericConversion3(ULL, F); }
// CHECK-MESSAGES: :[[@LINE-1]]:25: warning: 2 adjacent parameters of 'numericConversion3' of convertible types
// CHECK-MESSAGES: :[[@LINE-2]]:31: note: the first parameter in the range is 'F'
// CHECK-MESSAGES: :[[@LINE-3]]:53: note: the last parameter in the range is 'ULL'
// CHECK-MESSAGES: :[[@LINE-4]]:34: note: 'float' and 'unsigned long long' may be implicitly converted{{$}}
enum Unscoped { U_A,
U_B };
enum UnscopedFixed : char { UF_A,
UF_B };
enum struct Scoped { A,
B };
void numericConversion4(int I, Unscoped U) {} // NO-WARN.
void numericConversion5(int I, UnscopedFixed UF) {} // NO-WARN.
void numericConversion6(int I, Scoped S) {} // NO-WARN.
void numericConversion7(double D, Unscoped U) {} // NO-WARN.
void numericConversion8(double D, UnscopedFixed UF) {} // NO-WARN.
void numericConversion9(double D, Scoped S) {} // NO-WARN.
void numericConversionMultiUnique(int I, double D1, double D2) {}
// CHECK-MESSAGES: :[[@LINE-1]]:35: warning: 3 adjacent parameters of 'numericConversionMultiUnique' of convertible types
// CHECK-MESSAGES: :[[@LINE-2]]:39: note: the first parameter in the range is 'I'
// CHECK-MESSAGES: :[[@LINE-3]]:60: note: the last parameter in the range is 'D2'
// CHECK-MESSAGES: :[[@LINE-4]]:42: note: 'int' and 'double' may be implicitly converted{{$}}
// (Note: int<->double conversion for I<->D2 not diagnosed again.)
typedef int MyInt;
using MyDouble = double;
void numericConversion10(MyInt MI, MyDouble MD) { numericConversion10(MD, MI); }
// CHECK-MESSAGES: :[[@LINE-1]]:26: warning: 2 adjacent parameters of 'numericConversion10' of convertible types
// CHECK-MESSAGES: :[[@LINE-2]]:32: note: the first parameter in the range is 'MI'
// CHECK-MESSAGES: :[[@LINE-3]]:45: note: the last parameter in the range is 'MD'
// 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')
void numericAndQualifierConversion(int I, const double CD) { numericAndQualifierConversion(CD, I); }
// NO-WARN: Qualifier mixing is handled by a different check option.
struct FromInt {
FromInt(int);
};
void oneWayConversion1(int I, FromInt FI) {} // NO-WARN: One-way.
struct AmbiguousConvCtor {
AmbiguousConvCtor(int);
AmbiguousConvCtor(double);
};
void ambiguous1(long L, AmbiguousConvCtor ACC) {} // NO-WARN: Ambiguous, one-way.
struct ToInt {
operator int() const;
};
void oneWayConversion2(ToInt TI, int I) {} // NO-WARN: One-way.
struct AmbiguousConvOp {
operator int() const;
operator double() const;
};
void ambiguous2(AmbiguousConvOp ACO, long L) {} // NO-WARN: Ambiguous, one-way.
struct AmbiguousEverything1;
struct AmbiguousEverything2;
struct AmbiguousEverything1 {
AmbiguousEverything1();
AmbiguousEverything1(AmbiguousEverything2);
operator AmbiguousEverything2() const;
};
struct AmbiguousEverything2 {
AmbiguousEverything2();
AmbiguousEverything2(AmbiguousEverything1);
operator AmbiguousEverything1() const;
};
void ambiguous3(AmbiguousEverything1 AE1, AmbiguousEverything2 AE2) {} // NO-WARN: Ambiguous.
struct Integer {
Integer(int);
operator int() const;
};
void userDefinedConversion1(int I1, Integer I2) { userDefinedConversion1(I2, I1); }
// CHECK-MESSAGES: :[[@LINE-1]]:29: warning: 2 adjacent parameters of 'userDefinedConversion1' of convertible types
// CHECK-MESSAGES: :[[@LINE-2]]:33: note: the first parameter in the range is 'I1'
// CHECK-MESSAGES: :[[@LINE-3]]:45: note: the last parameter in the range is 'I2'
// CHECK-MESSAGES: :[[@LINE-4]]:37: note: 'int' and 'Integer' may be implicitly converted{{$}}
// CHECK-MESSAGES: :[[@LINE-9]]:3: note: the implicit conversion involves the converting constructor declared here
// CHECK-MESSAGES: :[[@LINE-9]]:3: note: the implicit conversion involves the conversion operator declared here
struct Ambiguous {
Ambiguous(int);
Ambiguous(double);
operator long() const;
operator float() const;
};
void ambiguous3(char C, Ambiguous A) {} // NO-WARN: Ambiguous.
struct CDouble {
CDouble(const double &);
operator const double &() const;
};
void userDefinedConversion2(double D, CDouble CD) { userDefinedConversion2(CD, D); }
// CHECK-MESSAGES: :[[@LINE-1]]:29: warning: 2 adjacent parameters of 'userDefinedConversion2' of convertible types
// CHECK-MESSAGES: :[[@LINE-2]]:36: note: the first parameter in the range is 'D'
// CHECK-MESSAGES: :[[@LINE-3]]:47: note: the last parameter in the range is 'CD'
// CHECK-MESSAGES: :[[@LINE-4]]:39: note: 'double' and 'CDouble' may be implicitly converted: 'double' -> 'const double &' -> 'CDouble', 'CDouble' -> 'const double &' -> 'double'
// CHECK-MESSAGES: :[[@LINE-9]]:3: note: the implicit conversion involves the converting constructor declared here
// CHECK-MESSAGES: :[[@LINE-9]]:3: note: the implicit conversion involves the conversion operator declared here
void userDefinedConversion3(int I, CDouble CD) { userDefinedConversion3(CD, I); }
// CHECK-MESSAGES: :[[@LINE-1]]:29: warning: 2 adjacent parameters of 'userDefinedConversion3' of convertible types
// CHECK-MESSAGES: :[[@LINE-2]]:33: note: the first parameter in the range is 'I'
// CHECK-MESSAGES: :[[@LINE-3]]:44: note: the last parameter in the range is 'CD'
// CHECK-MESSAGES: :[[@LINE-4]]:36: note: 'int' and 'CDouble' may be implicitly converted: 'int' -> 'double' -> 'const double &' -> 'CDouble', 'CDouble' -> 'const double &' -> 'int'
// CHECK-MESSAGES: :[[@LINE-17]]:3: note: the implicit conversion involves the converting constructor declared here
// CHECK-MESSAGES: :[[@LINE-17]]:3: note: the implicit conversion involves the conversion operator declared here
struct TDInt {
TDInt(const MyInt &);
operator MyInt() const;
};
void userDefinedConversion4(int I, TDInt TDI) { userDefinedConversion4(TDI, I); }
// CHECK-MESSAGES: :[[@LINE-1]]:29: warning: 2 adjacent parameters of 'userDefinedConversion4' of convertible types
// CHECK-MESSAGES: :[[@LINE-2]]:33: note: the first parameter in the range is 'I'
// CHECK-MESSAGES: :[[@LINE-3]]:42: note: the last parameter in the range is 'TDI'
// CHECK-MESSAGES: :[[@LINE-4]]:36: note: 'int' and 'TDInt' may be implicitly converted: 'int' -> 'const MyInt &' -> 'TDInt', 'TDInt' -> 'MyInt' -> 'int'
// CHECK-MESSAGES: :[[@LINE-9]]:3: note: the implicit conversion involves the converting constructor declared here
// CHECK-MESSAGES: :[[@LINE-9]]:3: note: the implicit conversion involves the conversion operator declared here
struct TDIntDouble {
TDIntDouble(const MyInt &);
TDIntDouble(const MyDouble &);
operator MyInt() const;
operator MyDouble() const;
};
void userDefinedConversion5(int I, TDIntDouble TDID) { userDefinedConversion5(TDID, I); }
// CHECK-MESSAGES: :[[@LINE-1]]:29: warning: 2 adjacent parameters of 'userDefinedConversion5' of convertible types
// CHECK-MESSAGES: :[[@LINE-2]]:33: note: the first parameter in the range is 'I'
// CHECK-MESSAGES: :[[@LINE-3]]:48: note: the last parameter in the range is 'TDID'
// CHECK-MESSAGES: :[[@LINE-4]]:36: note: 'int' and 'TDIntDouble' may be implicitly converted: 'int' -> 'const MyInt &' -> 'TDIntDouble', 'TDIntDouble' -> 'MyInt' -> 'int'
// CHECK-MESSAGES: :[[@LINE-11]]:3: note: the implicit conversion involves the converting constructor declared here
// CHECK-MESSAGES: :[[@LINE-10]]:3: note: the implicit conversion involves the conversion operator declared here
void userDefinedConversion6(double D, TDIntDouble TDID) { userDefinedConversion6(TDID, D); }
// CHECK-MESSAGES: :[[@LINE-1]]:29: warning: 2 adjacent parameters of 'userDefinedConversion6' of convertible types
// CHECK-MESSAGES: :[[@LINE-2]]:36: note: the first parameter in the range is 'D'
// CHECK-MESSAGES: :[[@LINE-3]]:51: note: the last parameter in the range is 'TDID'
// CHECK-MESSAGES: :[[@LINE-4]]:39: note: 'double' and 'TDIntDouble' may be implicitly converted: 'double' -> 'const MyDouble &' -> 'TDIntDouble', 'TDIntDouble' -> 'MyDouble' -> 'double'
// CHECK-MESSAGES: :[[@LINE-18]]:3: note: the implicit conversion involves the converting constructor declared here
// CHECK-MESSAGES: :[[@LINE-17]]:3: note: the implicit conversion involves the conversion operator declared here
void userDefinedConversion7(char C, TDIntDouble TDID) {} // NO-WARN: Ambiguous.
struct Forward1;
struct Forward2;
void incomplete(Forward1 *F1, Forward2 *F2) {} // NO-WARN: Do not compare incomplete types.
void pointeeConverison(int *IP, double *DP) {} // NO-WARN.
void pointerConversion1(void *VP, int *IP) {} // NO-WARN: One-way.
struct PointerBox {
PointerBox(void *);
operator int *() const;
};
void pointerConversion2(PointerBox PB, int *IP) { pointerConversion2(IP, PB); }
// CHECK-MESSAGES: :[[@LINE-1]]:25: warning: 2 adjacent parameters of 'pointerConversion2' of convertible types
// CHECK-MESSAGES: :[[@LINE-2]]:36: note: the first parameter in the range is 'PB'
// CHECK-MESSAGES: :[[@LINE-3]]:45: note: the last parameter in the range is 'IP'
// CHECK-MESSAGES: :[[@LINE-4]]:40: note: 'PointerBox' and 'int *' may be implicitly converted: 'PointerBox' -> 'int *', 'int *' -> 'void *' -> 'PointerBox'
// CHECK-MESSAGES: :[[@LINE-8]]:3: note: the implicit conversion involves the conversion operator declared here
// CHECK-MESSAGES: :[[@LINE-10]]:3: note: the implicit conversion involves the converting constructor declared here
void pointerConversion3(PointerBox PB, double *DP) {} // NO-WARN: Not convertible.
struct Base {};
struct Derived : Base {};
void pointerConversion4(Base *BP, Derived *DP) {} // NO-WARN: One-way.
struct BaseAndDerivedInverter {
BaseAndDerivedInverter(Base); // Takes a Base
operator Derived() const; // and becomes a Derived.
};
void pointerConversion5(BaseAndDerivedInverter BADI, Derived D) { pointerConversion5(D, BADI); }
// CHECK-MESSAGES: :[[@LINE-1]]:25: warning: 2 adjacent parameters of 'pointerConversion5' of convertible types
// CHECK-MESSAGES: :[[@LINE-2]]:48: note: the first parameter in the range is 'BADI'
// CHECK-MESSAGES: :[[@LINE-3]]:62: note: the last parameter in the range is 'D'
// CHECK-MESSAGES: :[[@LINE-4]]:54: note: 'BaseAndDerivedInverter' and 'Derived' may be implicitly converted: 'BaseAndDerivedInverter' -> 'Derived', 'Derived' -> 'Base' -> 'BaseAndDerivedInverter'
// CHECK-MESSAGES: :[[@LINE-8]]:3: note: the implicit conversion involves the conversion operator declared here
// CHECK-MESSAGES: :[[@LINE-10]]:3: note: the implicit conversion involves the converting constructor declared here
void pointerConversion6(void (*NTF)() noexcept, void (*TF)()) {}
// NO-WARN: This call cannot be swapped, even if "getCanonicalType()" believes otherwise.
using NonThrowingFunction = void (*)() noexcept;
struct NoexceptMaker {
NoexceptMaker(void (*ThrowingFunction)());
// Need to use a typedef here because
// "conversion function cannot convert to a function type".
// operator (void (*)() noexcept) () const;
operator NonThrowingFunction() const;
};
void pointerConversion7(void (*NTF)() noexcept, NoexceptMaker NM) { pointerConversion7(NM, NTF); }
// CHECK-MESSAGES: :[[@LINE-1]]:25: warning: 2 adjacent parameters of 'pointerConversion7' of convertible types
// CHECK-MESSAGES: :[[@LINE-2]]:32: note: the first parameter in the range is 'NTF'
// CHECK-MESSAGES: :[[@LINE-3]]:63: note: the last parameter in the range is 'NM'
// CHECK-MESSAGES: :[[@LINE-4]]:49: note: 'void (*)() noexcept' and 'NoexceptMaker' may be implicitly converted: 'void (*)() noexcept' -> 'void (*)()' -> 'NoexceptMaker', 'NoexceptMaker' -> 'NonThrowingFunction' -> 'void (*)() noexcept'
// CHECK-MESSAGES: :[[@LINE-12]]:3: note: the implicit conversion involves the converting constructor declared here
// CHECK-MESSAGES: :[[@LINE-9]]:3: note: the implicit conversion involves the conversion operator declared here
struct ToType;
struct MiddleStep1 {
operator ToType() const;
};
struct FromType {
operator MiddleStep1() const;
};
struct MiddleStep2 {
operator FromType() const;
};
struct ToType {
operator MiddleStep2() const;
};
void f(FromType F, ToType T) { // NO-WARN: The path takes two steps.
MiddleStep2 MS2 = T;
FromType F2 = MS2;
MiddleStep1 MS1 = F;
ToType T2 = MS1;
f(F2, T2);
}
// Synthesised example from OpenCV.
template <typename T>
struct TemplateConversion {
template <typename T2>
operator TemplateConversion<T2>() const;
};
using IntConverter = TemplateConversion<int>;
using FloatConverter = TemplateConversion<float>;
void templateConversion(IntConverter IC, FloatConverter FC) { templateConversion(FC, IC); }
// Note: even though this swap is possible, we do not model things when it comes to "template magic".
// But at least the check should not crash!

View file

@ -3,7 +3,8 @@
// RUN: {key: bugprone-easily-swappable-parameters.MinimumLength, value: 2}, \
// RUN: {key: bugprone-easily-swappable-parameters.IgnoredParameterNames, value: ""}, \
// RUN: {key: bugprone-easily-swappable-parameters.IgnoredParameterTypeSuffixes, value: ""}, \
// RUN: {key: bugprone-easily-swappable-parameters.QualifiersMix, value: 0} \
// RUN: {key: bugprone-easily-swappable-parameters.QualifiersMix, value: 0}, \
// RUN: {key: bugprone-easily-swappable-parameters.ModelImplicitConversions, value: 0} \
// RUN: ]}' --
namespace std {
@ -340,3 +341,6 @@ void memberTypedefDependentReference3(
// CHECK-MESSAGES: :[[@LINE-3]]:38: note: the first parameter in the range is 'E'
// CHECK-MESSAGES: :[[@LINE-3]]:45: note: the last parameter in the range is 'R'
// CHECK-MESSAGES: :[[@LINE-4]]:5: note: 'typename Vector<T>::element_type' and 'const typename Vector<T>::element_type &' parameters accept and bind the same kind of values
void functionPrototypeLosesNoexcept(void (*NonThrowing)() noexcept, void (*Throwing)()) {}
// NO-WARN: This call cannot be swapped, even if "getCanonicalType()" believes otherwise.

View file

@ -3,7 +3,8 @@
// RUN: {key: bugprone-easily-swappable-parameters.MinimumLength, value: 3}, \
// RUN: {key: bugprone-easily-swappable-parameters.IgnoredParameterNames, value: ""}, \
// RUN: {key: bugprone-easily-swappable-parameters.IgnoredParameterTypeSuffixes, value: ""}, \
// RUN: {key: bugprone-easily-swappable-parameters.QualifiersMix, value: 0} \
// RUN: {key: bugprone-easily-swappable-parameters.QualifiersMix, value: 0}, \
// RUN: {key: bugprone-easily-swappable-parameters.ModelImplicitConversions, value: 0} \
// RUN: ]}' --
int add(int Left, int Right) { return Left + Right; } // NO-WARN: Only 2 parameters.

View file

@ -3,7 +3,8 @@
// RUN: {key: bugprone-easily-swappable-parameters.MinimumLength, value: 2}, \
// RUN: {key: bugprone-easily-swappable-parameters.IgnoredParameterNames, value: ""}, \
// RUN: {key: bugprone-easily-swappable-parameters.IgnoredParameterTypeSuffixes, value: ""}, \
// RUN: {key: bugprone-easily-swappable-parameters.QualifiersMix, value: 1} \
// RUN: {key: bugprone-easily-swappable-parameters.QualifiersMix, value: 1}, \
// RUN: {key: bugprone-easily-swappable-parameters.ModelImplicitConversions, value: 0} \
// RUN: ]}' --
typedef int MyInt1;

View file

@ -3,7 +3,8 @@
// RUN: {key: bugprone-easily-swappable-parameters.MinimumLength, value: 2}, \
// RUN: {key: bugprone-easily-swappable-parameters.IgnoredParameterNames, value: ""}, \
// RUN: {key: bugprone-easily-swappable-parameters.IgnoredParameterTypeSuffixes, value: "bool;MyBool;struct U;MAKE_LOGICAL_TYPE(int)"}, \
// RUN: {key: bugprone-easily-swappable-parameters.QualifiersMix, value: 0} \
// RUN: {key: bugprone-easily-swappable-parameters.QualifiersMix, value: 0}, \
// RUN: {key: bugprone-easily-swappable-parameters.ModelImplicitConversions, value: 0} \
// RUN: ]}' -- -x c
#define bool _Bool