[clang-tidy] Extend 'bugprone-easily-swappable-parameters' with optionally considering differently qualified types mixable

Adds a relaxation option QualifiersMix which will make the check report for
cases where parameters refer to the same type if they only differ in qualifiers.

This makes cases, such as the following, not warned about by default, produce
a warning.

    void* memcpy(void* dst, const void* src, unsigned size) {}

However, unless people meticulously const their local variables, unfortunately,
even such a function carry a potential swap:

    T* obj = new T; // Not const!!!
    void* buf = malloc(sizeof(T));

    memcpy(obj, buf, sizeof(T));
    //     ^~~  ^~~ accidental swap here, even though the interface "specified" a const.

Reviewed By: aaron.ballman

Differential Revision: http://reviews.llvm.org/D96355
This commit is contained in:
Whisperity 2019-11-20 14:12:57 +01:00
parent 26d864b44b
commit 961e9e6af6
8 changed files with 320 additions and 55 deletions

View file

@ -59,6 +59,9 @@ static const std::string DefaultIgnoredParameterTypeSuffixes =
"Constreverseiterator",
"constreverseiterator"});
/// The default value for the QualifiersMix check option.
static constexpr bool DefaultQualifiersMix = false;
using namespace clang::ast_matchers;
namespace clang {
@ -84,8 +87,9 @@ enum class MixFlags : unsigned char {
TypeAlias = 8, //< The path from one type to the other involves
// desugaring type aliases.
ReferenceBind = 16, //< The mix involves the binding power of "const &".
Qualifiers = 32, //< The mix involves change in the qualifiers.
LLVM_MARK_AS_BITMASK_ENUM(/* LargestValue =*/ReferenceBind)
LLVM_MARK_AS_BITMASK_ENUM(/* LargestValue =*/Qualifiers)
};
LLVM_ENABLE_BITMASK_ENUMS_IN_NAMESPACE();
@ -110,7 +114,7 @@ static inline std::string formatMixFlags(MixFlags F) {
if (F == MixFlags::Invalid)
return "#Inv!";
SmallString<8> Str{"-----"};
SmallString<8> Str{"------"};
if (hasFlag(F, MixFlags::None))
// Shows the None bit explicitly, as it can be applied in the recursion
@ -124,6 +128,8 @@ static inline std::string formatMixFlags(MixFlags F) {
Str[3] = 't';
if (hasFlag(F, MixFlags::ReferenceBind))
Str[4] = '&';
if (hasFlag(F, MixFlags::Qualifiers))
Str[5] = 'Q';
return Str.str().str();
}
@ -169,13 +175,24 @@ struct MixData {
Flags &= ~MixFlags::Trivial;
}
/// Add the specified flag bits to the flags.
MixData operator|(MixFlags EnableFlags) const {
return {Flags | EnableFlags, CommonType};
}
/// Add the specified flag bits to the flags.
MixData &operator|=(MixFlags EnableFlags) {
Flags |= EnableFlags;
return *this;
}
/// Add the specified qualifiers to the common type in the Mix.
MixData qualify(Qualifiers Quals) const {
SplitQualType Split = CommonType.split();
Split.Quals.addQualifiers(Quals);
return {Flags, QualType(Split.Ty, Split.Quals.getAsOpaqueValue())};
}
};
/// A named tuple that contains the information for a mix between two concrete
@ -266,18 +283,6 @@ static MixData calculateMixability(const TheCheck &Check, const QualType LType,
RType.getSingleStepDesugaredType(Ctx), Ctx);
}
// Dissolve typedefs.
if (const auto *LTypedef = LType->getAs<TypedefType>()) {
LLVM_DEBUG(llvm::dbgs() << "--- calculateMixability. LHS is typedef.\n");
return calculateMixability(Check, LTypedef->desugar(), RType, Ctx) |
MixFlags::TypeAlias;
}
if (const auto *RTypedef = RType->getAs<TypedefType>()) {
LLVM_DEBUG(llvm::dbgs() << "--- calculateMixability. RHS is typedef.\n");
return calculateMixability(Check, LType, RTypedef->desugar(), Ctx) |
MixFlags::TypeAlias;
}
// At a particular call site, what could be passed to a 'T' or 'const T' might
// also be passed to a 'const T &' without the call site putting a direct
// side effect on the passed expressions.
@ -292,6 +297,59 @@ static MixData calculateMixability(const TheCheck &Check, const QualType LType,
MixFlags::ReferenceBind;
}
// Dissolve typedefs after the qualifiers outside the typedef are dealt with.
if (LType->getAs<TypedefType>()) {
LLVM_DEBUG(llvm::dbgs() << "--- calculateMixability. LHS is typedef.\n");
return calculateMixability(Check, LType.getSingleStepDesugaredType(Ctx),
RType, Ctx) |
MixFlags::TypeAlias;
}
if (RType->getAs<TypedefType>()) {
LLVM_DEBUG(llvm::dbgs() << "--- calculateMixability. RHS is typedef.\n");
return calculateMixability(Check, LType,
RType.getSingleStepDesugaredType(Ctx), Ctx) |
MixFlags::TypeAlias;
}
// A parameter of type 'cvr1 T' and another of potentially differently
// qualified 'cvr2 T' may bind with the same power, if the user so requested.
if (LType.getLocalCVRQualifiers() != RType.getLocalCVRQualifiers()) {
LLVM_DEBUG(if (LType.getLocalCVRQualifiers()) llvm::dbgs()
<< "--- calculateMixability. LHS is CVR.\n");
LLVM_DEBUG(if (RType.getLocalCVRQualifiers()) llvm::dbgs()
<< "--- calculateMixability. RHS is CVR.\n");
if (!Check.QualifiersMix) {
LLVM_DEBUG(llvm::dbgs()
<< "<<< calculateMixability. QualifiersMix turned off.\n");
return {MixFlags::None};
}
return calculateMixability(Check, LType.getLocalUnqualifiedType(),
RType.getLocalUnqualifiedType(), Ctx) |
MixFlags::Qualifiers;
}
if (LType.getLocalCVRQualifiers() == RType.getLocalCVRQualifiers() &&
LType.getLocalCVRQualifiers() != 0) {
LLVM_DEBUG(llvm::dbgs()
<< "--- calculateMixability. LHS and RHS same CVR.\n");
// Apply the same qualifier back into the found common type if we found
// a common type between the unqualified versions.
return calculateMixability(Check, LType.getLocalUnqualifiedType(),
RType.getLocalUnqualifiedType(), Ctx)
.qualify(LType.getLocalQualifiers());
}
if (LType->isPointerType() && RType->isPointerType()) {
// If both types are pointers, and pointed to the exact same type,
// LType == RType took care of that.
// Try to see if the pointee type has some other match.
LLVM_DEBUG(llvm::dbgs()
<< "--- calculateMixability. LHS and RHS are Ptrs.\n");
return calculateMixability(Check, LType->getPointeeType(),
RType->getPointeeType(), Ctx);
}
// If none of the previous logic found a match, try if Clang otherwise
// believes the types to be the same.
if (LType.getCanonicalType() == RType.getCanonicalType()) {
@ -315,21 +373,44 @@ static MixData isLRefEquallyBindingToType(const TheCheck &Check,
Ty.dump(llvm::dbgs(), Ctx); llvm::dbgs() << '\n';);
QualType ReferredType = LRef->getPointeeType();
if (!ReferredType.isLocalConstQualified() &&
ReferredType->getAs<TypedefType>()) {
LLVM_DEBUG(
llvm::dbgs()
<< "--- isLRefEquallyBindingToType. Non-const LRef to Typedef.\n");
ReferredType = ReferredType.getDesugaredType(Ctx);
if (!ReferredType.isLocalConstQualified()) {
LLVM_DEBUG(llvm::dbgs()
<< "<<< isLRefEquallyBindingToType. Not const ref.\n");
<< "<<< isLRefEquallyBindingToType. Typedef is not const.\n");
return {MixFlags::None};
}
LLVM_DEBUG(llvm::dbgs() << "--- isLRefEquallyBindingToType. Typedef is "
"const, considering as const LRef.\n");
} else if (!ReferredType.isLocalConstQualified()) {
LLVM_DEBUG(llvm::dbgs()
<< "<<< isLRefEquallyBindingToType. Not const LRef.\n");
return {MixFlags::None};
};
QualType NonConstReferredType = ReferredType;
NonConstReferredType.removeLocalConst();
if (ReferredType == Ty || NonConstReferredType == Ty) {
assert(ReferredType.isLocalConstQualified() &&
"Reaching this point means we are sure LRef is effectively a const&.");
if (ReferredType == Ty) {
LLVM_DEBUG(
llvm::dbgs()
<< "<<< isLRefEquallyBindingToType. Type of referred matches.\n");
return {MixFlags::Trivial, ReferredType};
}
QualType NonConstReferredType = ReferredType;
NonConstReferredType.removeLocalConst();
if (NonConstReferredType == Ty) {
LLVM_DEBUG(llvm::dbgs() << "<<< isLRefEquallyBindingToType. Type of "
"referred matches to non-const qualified.\n");
return {MixFlags::Trivial, NonConstReferredType};
}
LLVM_DEBUG(
llvm::dbgs()
<< "--- isLRefEquallyBindingToType. Checking mix for underlying type.\n");
@ -508,8 +589,10 @@ static SmallString<64> getNameOrUnnamed(const NamedDecl *ND) {
/// Returns whether a particular Mix between two parameters should have the
/// types involved diagnosed to the user. This is only a flag check.
static inline bool needsToPrintTypeInDiagnostic(const model::Mix &M) {
return static_cast<bool>(M.flags() & (model::MixFlags::TypeAlias |
model::MixFlags::ReferenceBind));
using namespace model;
return static_cast<bool>(
M.flags() &
(MixFlags::TypeAlias | MixFlags::ReferenceBind | MixFlags::Qualifiers));
}
namespace {
@ -593,7 +676,8 @@ EasilySwappableParametersCheck::EasilySwappableParametersCheck(
Options.get("IgnoredParameterNames", DefaultIgnoredParameterNames))),
IgnoredParameterTypeSuffixes(optutils::parseStringList(
Options.get("IgnoredParameterTypeSuffixes",
DefaultIgnoredParameterTypeSuffixes))) {}
DefaultIgnoredParameterTypeSuffixes))),
QualifiersMix(Options.get("QualifiersMix", DefaultQualifiersMix)) {}
void EasilySwappableParametersCheck::storeOptions(
ClangTidyOptions::OptionMap &Opts) {
@ -602,6 +686,7 @@ void EasilySwappableParametersCheck::storeOptions(
optutils::serializeStringList(IgnoredParameterNames));
Options.store(Opts, "IgnoredParameterTypeSuffixes",
optutils::serializeStringList(IgnoredParameterTypeSuffixes));
Options.store(Opts, "QualifiersMix", QualifiersMix);
}
void EasilySwappableParametersCheck::registerMatchers(MatchFinder *Finder) {
@ -708,16 +793,19 @@ void EasilySwappableParametersCheck::check(
QualType LType = LVar->getType();
QualType RType = RVar->getType();
QualType CommonType = M.commonUnderlyingType();
std::string LTypeAsWritten = LType.getAsString(PP);
std::string RTypeAsWritten = RType.getAsString(PP);
std::string LTypeStr = LType.getAsString(PP);
std::string RTypeStr = RType.getAsString(PP);
std::string CommonTypeStr = CommonType.getAsString(PP);
if (hasFlag(M.flags(), MixFlags::TypeAlias) &&
UniqueTypeAlias(LType, RType, CommonType)) {
StringRef DiagText;
bool ExplicitlyPrintCommonType = false;
if (LTypeAsWritten == CommonTypeStr ||
RTypeAsWritten == CommonTypeStr)
if (LTypeStr == CommonTypeStr || RTypeStr == CommonTypeStr)
if (hasFlag(M.flags(), MixFlags::Qualifiers))
DiagText = "after resolving type aliases, '%0' and '%1' share a "
"common type";
else
DiagText =
"after resolving type aliases, '%0' and '%1' are the same";
else {
@ -728,17 +816,18 @@ void EasilySwappableParametersCheck::check(
auto Diag =
diag(LVar->getOuterLocStart(), DiagText, DiagnosticIDs::Note)
<< LTypeAsWritten << RTypeAsWritten;
<< LTypeStr << RTypeStr;
if (ExplicitlyPrintCommonType)
Diag << CommonTypeStr;
}
if (hasFlag(M.flags(), MixFlags::ReferenceBind) &&
if ((hasFlag(M.flags(), MixFlags::ReferenceBind) ||
hasFlag(M.flags(), MixFlags::Qualifiers)) &&
UniqueBindPower({LType, RType})) {
StringRef DiagText = "'%0' and '%1' parameters accept and bind the "
"same kind of values";
diag(RVar->getOuterLocStart(), DiagText, DiagnosticIDs::Note)
<< LTypeAsWritten << RTypeAsWritten;
<< LTypeStr << RTypeStr;
}
}
}

View file

@ -38,6 +38,9 @@ public:
/// The parameter typename suffixes (as written in the source code) to be
/// ignored.
const std::vector<std::string> IgnoredParameterTypeSuffixes;
/// Whether to consider an unqualified and a qualified type mixable.
const bool QualifiersMix;
};
} // namespace bugprone

View file

@ -32,6 +32,34 @@ to strengthen the type safety of a project, no automatic fix-its are offered.
Options
-------
Extension/relaxation options
^^^^^^^^^^^^^^^^^^^^^^^^^^^^
Relaxation (or extension) options can be used to broaden the scope of the
analysis and fine-tune the enabling of more mixes between types.
Some mixes may depend on coding style or preference specific to a project,
however, it should be noted that enabling *all* of these relaxations model the
way of mixing at call sites the most.
These options are expected to make the check report for more functions, and
report longer mixable ranges.
.. option:: QualifiersMix
Whether to consider parameters of some *cvr-qualified* ``T`` and a
differently *cvr-qualified* ``T`` (i.e. ``T`` and ``const T``, ``const T``
and ``volatile T``, etc.) mixable between one another.
If `false`, the check will consider differently qualified types unmixable.
`True` turns the warnings on.
Defaults to `false`.
The following example produces a diagnostic only if `QualifiersMix` is
enabled:
.. code-block:: c++
void *memcpy(const void *Destination, void *Source, std::size_t N) {}
Filtering options
^^^^^^^^^^^^^^^^^

View file

@ -2,7 +2,8 @@
// RUN: -config='{CheckOptions: [ \
// 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.IgnoredParameterTypeSuffixes, value: "T"}, \
// RUN: {key: bugprone-easily-swappable-parameters.QualifiersMix, value: 0} \
// RUN: ]}' --
void ignoredUnnamed(int I, int, int) {} // NO-WARN: No >= 2 length of non-unnamed.

View file

@ -2,7 +2,8 @@
// 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.IgnoredParameterTypeSuffixes, value: ""}, \
// RUN: {key: bugprone-easily-swappable-parameters.QualifiersMix, value: 0} \
// RUN: ]}' --
namespace std {
@ -104,6 +105,14 @@ void differentPtrs(int *IP, long *LP) {} // NO-WARN: Not the same type.
typedef int MyInt1;
using MyInt2 = int;
typedef MyInt2 MyInt2b;
using CInt = const int;
using CMyInt1 = const MyInt1;
using CMyInt2 = const MyInt2;
typedef long MyLong1;
using MyLong2 = long;
void typedefAndTypedef1(MyInt1 I1, MyInt1 I2) {}
// CHECK-MESSAGES: :[[@LINE-1]]:25: warning: 2 adjacent parameters of 'typedefAndTypedef1' of similar type ('MyInt1')
@ -133,8 +142,6 @@ void betweenTypedef2(MyInt1 I, MyInt2 J) {}
// CHECK-MESSAGES: :[[@LINE-3]]:39: note: the last parameter in the range is 'J'
// CHECK-MESSAGES: :[[@LINE-4]]:22: note: after resolving type aliases, the common type of 'MyInt1' and 'MyInt2' is 'int'
typedef MyInt2 MyInt2b;
void typedefChain(int I, MyInt1 MI1, MyInt2 MI2, MyInt2b MI2b) {}
// CHECK-MESSAGES: :[[@LINE-1]]:19: warning: 4 adjacent parameters of 'typedefChain' of similar type are
// CHECK-MESSAGES: :[[@LINE-2]]:23: note: the first parameter in the range is 'I'
@ -143,22 +150,21 @@ void typedefChain(int I, MyInt1 MI1, MyInt2 MI2, MyInt2b MI2b) {}
// CHECK-MESSAGES: :[[@LINE-5]]:19: note: after resolving type aliases, 'int' and 'MyInt2' are the same
// CHECK-MESSAGES: :[[@LINE-6]]:19: note: after resolving type aliases, 'int' and 'MyInt2b' are the same
typedef long MyLong1;
using MyLong2 = long;
void throughTypedefToOtherType(MyInt1 I, MyLong1 J) {} // NO-WARN: int and long.
void qualified1(int I, const int CI) {} // NO-WARN: Not the same type.
void qualified1(int I, const int CI) {} // NO-WARN: Different qualifiers.
void qualified2(int I, volatile int VI) {} // NO-WARN: Not the same type.
void qualified2(int I, volatile int VI) {} // NO-WARN: Different qualifiers.
void qualified3(int *IP, const int *CIP) {} // NO-WARN: Not the same type.
void qualified3(int *IP, const int *CIP) {} // NO-WARN: Different qualifiers.
void qualified4(const int CI, const long CL) {} // NO-WARN: Not the same type.
using CInt = const int;
void qualifiedPtr1(int *IP, int *const IPC) {} // NO-WARN: Different qualifiers.
void qualifiedThroughTypedef1(int I, CInt CI) {} // NO-WARN: Not the same type.
void qualifiedTypeAndQualifiedPtr1(const int *CIP, int *const volatile IPCV) {} // NO-WARN: Not the same type.
void qualifiedThroughTypedef1(int I, CInt CI) {} // NO-WARN: Different qualifiers.
void qualifiedThroughTypedef2(CInt CI1, const int CI2) {}
// CHECK-MESSAGES: :[[@LINE-1]]:31: warning: 2 adjacent parameters of 'qualifiedThroughTypedef2' of similar type are
@ -166,13 +172,32 @@ void qualifiedThroughTypedef2(CInt CI1, const int CI2) {}
// CHECK-MESSAGES: :[[@LINE-3]]:51: note: the last parameter in the range is 'CI2'
// CHECK-MESSAGES: :[[@LINE-4]]:31: note: after resolving type aliases, 'CInt' and 'const int' are the same
void qualifiedThroughTypedef3(CInt CI1, const MyInt1 CI2, const int CI3) {} // NO-WARN: Not the same type.
void qualifiedThroughTypedef3(CInt CI1, const MyInt1 CI2, const int CI3) {}
// CHECK-MESSAGES: :[[@LINE-1]]:31: warning: 3 adjacent parameters of 'qualifiedThroughTypedef3' of similar type are
// CHECK-MESSAGES: :[[@LINE-2]]:36: note: the first parameter in the range is 'CI1'
// CHECK-MESSAGES: :[[@LINE-3]]:69: note: the last parameter in the range is 'CI3'
// CHECK-MESSAGES: :[[@LINE-4]]:31: note: after resolving type aliases, the common type of 'CInt' and 'const MyInt1' is 'const int'
// CHECK-MESSAGES: :[[@LINE-5]]:31: note: after resolving type aliases, 'CInt' and 'const int' are the same
// CHECK-MESSAGES: :[[@LINE-6]]:41: note: after resolving type aliases, 'const MyInt1' and 'const int' are the same
void qualifiedThroughTypedef4(CInt CI1, const MyInt1 CI2, const MyInt2 CI3) {}
// CHECK-MESSAGES: :[[@LINE-1]]:41: warning: 2 adjacent parameters of 'qualifiedThroughTypedef4' of similar type are
// CHECK-MESSAGES: :[[@LINE-2]]:54: note: the first parameter in the range is 'CI2'
// CHECK-MESSAGES: :[[@LINE-1]]:31: warning: 3 adjacent parameters of 'qualifiedThroughTypedef4' of similar type are
// CHECK-MESSAGES: :[[@LINE-2]]:36: note: the first parameter in the range is 'CI1'
// CHECK-MESSAGES: :[[@LINE-3]]:72: note: the last parameter in the range is 'CI3'
// CHECK-MESSAGES: :[[@LINE-4]]:41: note: after resolving type aliases, the common type of 'const MyInt1' and 'const MyInt2' is 'int'
// CHECK-MESSAGES: :[[@LINE-4]]:31: note: after resolving type aliases, the common type of 'CInt' and 'const MyInt1' is 'const int'
// CHECK-MESSAGES: :[[@LINE-5]]:31: note: after resolving type aliases, the common type of 'CInt' and 'const MyInt2' is 'const int'
// CHECK-MESSAGES: :[[@LINE-6]]:41: note: after resolving type aliases, the common type of 'const MyInt1' and 'const MyInt2' is 'const int'
void qualifiedThroughTypedef5(CMyInt1 CMI1, CMyInt2 CMI2) {}
// CHECK-MESSAGES: :[[@LINE-1]]:31: warning: 2 adjacent parameters of 'qualifiedThroughTypedef5' of similar type are
// CHECK-MESSAGES: :[[@LINE-2]]:39: note: the first parameter in the range is 'CMI1'
// CHECK-MESSAGES: :[[@LINE-3]]:53: note: the last parameter in the range is 'CMI2'
// CHECK-MESSAGES: :[[@LINE-4]]:31: note: after resolving type aliases, the common type of 'CMyInt1' and 'CMyInt2' is 'const int'
void qualifiedThroughTypedef6(CMyInt1 CMI1, int I) {} // NO-WARN: Different qualifiers.
template <typename T>
void copy(const T *Dest, T *Source) {} // NO-WARN: Different qualifiers.
void reference1(int I, int &IR) {} // NO-WARN: Distinct semantics when called.
@ -201,16 +226,21 @@ void reference6(int I, const int &CIR, int J, const int &CJR) {}
using ICRTy = const int &;
using MyIntCRTy = const MyInt1 &;
void referenceToTypedef1(CInt &CIR, int I) {}
// CHECK-MESSAGES: :[[@LINE-1]]:26: warning: 2 adjacent parameters of 'referenceToTypedef1' of similar type are
// CHECK-MESSAGES: :[[@LINE-2]]:32: note: the first parameter in the range is 'CIR'
// CHECK-MESSAGES: :[[@LINE-3]]:41: note: the last parameter in the range is 'I'
// CHECK-MESSAGES: :[[@LINE-4]]:37: note: 'CInt &' and 'int' parameters accept and bind the same kind of values
void referenceThroughTypedef(int I, ICRTy Builtin, MyIntCRTy MyInt) {}
// CHECK-MESSAGES: :[[@LINE-1]]:30: warning: 3 adjacent parameters of 'referenceThroughTypedef' of similar type are
// CHECK-MESSAGES: :[[@LINE-2]]:34: note: the first parameter in the range is 'I'
// CHECK-MESSAGES: :[[@LINE-3]]:62: note: the last parameter in the range is 'MyInt'
// CHECK-MESSAGES: :[[@LINE-4]]:30: note: after resolving type aliases, the common type of 'int' and 'ICRTy' is 'const int'
// CHECK-MESSAGES: :[[@LINE-5]]:37: note: 'int' and 'ICRTy' parameters accept and bind the same kind of values
// CHECK-MESSAGES: :[[@LINE-6]]:30: note: after resolving type aliases, 'int' and 'MyIntCRTy' are the same
// CHECK-MESSAGES: :[[@LINE-7]]:52: note: 'int' and 'MyIntCRTy' parameters accept and bind the same kind of values
// CHECK-MESSAGES: :[[@LINE-8]]:37: note: after resolving type aliases, the common type of 'ICRTy' and 'MyIntCRTy' is 'int'
// CHECK-MESSAGES: :[[@LINE-9]]:52: note: 'ICRTy' and 'MyIntCRTy' parameters accept and bind the same kind of values
// CHECK-MESSAGES: :[[@LINE-4]]:37: note: 'int' and 'ICRTy' parameters accept and bind the same kind of values
// CHECK-MESSAGES: :[[@LINE-5]]:30: note: after resolving type aliases, 'int' and 'MyIntCRTy' are the same
// CHECK-MESSAGES: :[[@LINE-6]]:52: note: 'int' and 'MyIntCRTy' parameters accept and bind the same kind of values
// CHECK-MESSAGES: :[[@LINE-7]]:37: note: after resolving type aliases, the common type of 'ICRTy' and 'MyIntCRTy' is 'int'
// CHECK-MESSAGES: :[[@LINE-8]]:52: note: 'ICRTy' and 'MyIntCRTy' parameters accept and bind the same kind of values
short const typedef int unsigned Eldritch;
typedef const unsigned short Holy;

View file

@ -2,7 +2,8 @@
// RUN: -config='{CheckOptions: [ \
// 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.IgnoredParameterTypeSuffixes, value: ""}, \
// RUN: {key: bugprone-easily-swappable-parameters.QualifiersMix, value: 0} \
// RUN: ]}' --
int add(int Left, int Right) { return Left + Right; } // NO-WARN: Only 2 parameters.

View file

@ -0,0 +1,112 @@
// 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: ]}' --
typedef int MyInt1;
typedef int MyInt2;
using CInt = const int;
using CMyInt1 = const MyInt1;
using CMyInt2 = const MyInt2;
void qualified1(int I, const int CI) {}
// CHECK-MESSAGES: :[[@LINE-1]]:17: warning: 2 adjacent parameters of 'qualified1' of similar type are easily swapped by mistake [bugprone-easily-swappable-parameters]
// CHECK-MESSAGES: :[[@LINE-2]]:21: note: the first parameter in the range is 'I'
// CHECK-MESSAGES: :[[@LINE-3]]:34: note: the last parameter in the range is 'CI'
// CHECK-MESSAGES: :[[@LINE-4]]:24: note: 'int' and 'const int' parameters accept and bind the same kind of values
void qualified2(int I, volatile int VI) {}
// CHECK-MESSAGES: :[[@LINE-1]]:17: warning: 2 adjacent parameters of 'qualified2' of similar type are
// CHECK-MESSAGES: :[[@LINE-2]]:21: note: the first parameter in the range is 'I'
// CHECK-MESSAGES: :[[@LINE-3]]:37: note: the last parameter in the range is 'VI'
// CHECK-MESSAGES: :[[@LINE-4]]:24: note: 'int' and 'volatile int' parameters accept and bind the same kind of values
void qualified3(int I, const volatile int CVI) {}
// CHECK-MESSAGES: :[[@LINE-1]]:17: warning: 2 adjacent parameters of 'qualified3' of similar type are
// CHECK-MESSAGES: :[[@LINE-2]]:21: note: the first parameter in the range is 'I'
// CHECK-MESSAGES: :[[@LINE-3]]:43: note: the last parameter in the range is 'CVI'
// CHECK-MESSAGES: :[[@LINE-4]]:24: note: 'int' and 'const volatile int' parameters accept and bind the same kind of values
void qualified4(int *IP, const int *CIP) {}
// CHECK-MESSAGES: :[[@LINE-1]]:17: warning: 2 adjacent parameters of 'qualified4' of similar type are
// CHECK-MESSAGES: :[[@LINE-2]]:22: note: the first parameter in the range is 'IP'
// CHECK-MESSAGES: :[[@LINE-3]]:37: note: the last parameter in the range is 'CIP'
// CHECK-MESSAGES: :[[@LINE-4]]:26: note: 'int *' and 'const int *' parameters accept and bind the same kind of values
void qualified5(const int CI, const long CL) {} // NO-WARN: Not the same type
void qualifiedPtr1(int *IP, int *const IPC) {}
// CHECK-MESSAGES: :[[@LINE-1]]:20: warning: 2 adjacent parameters of 'qualifiedPtr1' of similar type are
// CHECK-MESSAGES: :[[@LINE-2]]:25: note: the first parameter in the range is 'IP'
// CHECK-MESSAGES: :[[@LINE-3]]:40: note: the last parameter in the range is 'IPC'
// CHECK-MESSAGES: :[[@LINE-4]]:29: note: 'int *' and 'int *const' parameters accept and bind the same kind of values
void qualifiedPtr2(int *IP, int *volatile IPV) {}
// CHECK-MESSAGES: :[[@LINE-1]]:20: warning: 2 adjacent parameters of 'qualifiedPtr2' of similar type are
// CHECK-MESSAGES: :[[@LINE-2]]:25: note: the first parameter in the range is 'IP'
// CHECK-MESSAGES: :[[@LINE-3]]:43: note: the last parameter in the range is 'IPV'
// CHECK-MESSAGES: :[[@LINE-4]]:29: note: 'int *' and 'int *volatile' parameters accept and bind the same kind of values
void qualifiedTypeAndQualifiedPtr1(const int *CIP, int *const volatile IPCV) {}
// CHECK-MESSAGES: :[[@LINE-1]]:36: warning: 2 adjacent parameters of 'qualifiedTypeAndQualifiedPtr1' of similar type are
// CHECK-MESSAGES: :[[@LINE-2]]:47: note: the first parameter in the range is 'CIP'
// CHECK-MESSAGES: :[[@LINE-3]]:72: note: the last parameter in the range is 'IPCV'
// CHECK-MESSAGES: :[[@LINE-4]]:52: note: 'const int *' and 'int *const volatile' parameters accept and bind the same kind of values
void qualifiedThroughTypedef1(int I, CInt CI) {}
// CHECK-MESSAGES: :[[@LINE-1]]:31: warning: 2 adjacent parameters of 'qualifiedThroughTypedef1' of similar type are
// CHECK-MESSAGES: :[[@LINE-2]]:35: note: the first parameter in the range is 'I'
// CHECK-MESSAGES: :[[@LINE-3]]:43: note: the last parameter in the range is 'CI'
// CHECK-MESSAGES: :[[@LINE-4]]:31: note: after resolving type aliases, 'int' and 'CInt' share a common type
// CHECK-MESSAGES: :[[@LINE-5]]:38: note: 'int' and 'CInt' parameters accept and bind the same kind of values
void qualifiedThroughTypedef2(CInt CI1, const int CI2) {}
// CHECK-MESSAGES: :[[@LINE-1]]:31: warning: 2 adjacent parameters of 'qualifiedThroughTypedef2' of similar type are
// CHECK-MESSAGES: :[[@LINE-2]]:36: note: the first parameter in the range is 'CI1'
// CHECK-MESSAGES: :[[@LINE-3]]:51: note: the last parameter in the range is 'CI2'
// CHECK-MESSAGES: :[[@LINE-4]]:31: note: after resolving type aliases, 'CInt' and 'const int' are the same
void qualifiedThroughTypedef3(CInt CI1, const MyInt1 CI2, const int CI3) {}
// CHECK-MESSAGES: :[[@LINE-1]]:31: warning: 3 adjacent parameters of 'qualifiedThroughTypedef3' of similar type are
// CHECK-MESSAGES: :[[@LINE-2]]:36: note: the first parameter in the range is 'CI1'
// CHECK-MESSAGES: :[[@LINE-3]]:69: note: the last parameter in the range is 'CI3'
// CHECK-MESSAGES: :[[@LINE-4]]:31: note: after resolving type aliases, the common type of 'CInt' and 'const MyInt1' is 'const int'
// CHECK-MESSAGES: :[[@LINE-5]]:31: note: after resolving type aliases, 'CInt' and 'const int' are the same
// CHECK-MESSAGES: :[[@LINE-6]]:41: note: after resolving type aliases, 'const MyInt1' and 'const int' are the same
void qualifiedThroughTypedef4(CInt CI1, const MyInt1 CI2, const MyInt2 CI3) {}
// CHECK-MESSAGES: :[[@LINE-1]]:31: warning: 3 adjacent parameters of 'qualifiedThroughTypedef4' of similar type are
// CHECK-MESSAGES: :[[@LINE-2]]:36: note: the first parameter in the range is 'CI1'
// CHECK-MESSAGES: :[[@LINE-3]]:72: note: the last parameter in the range is 'CI3'
// CHECK-MESSAGES: :[[@LINE-4]]:31: note: after resolving type aliases, the common type of 'CInt' and 'const MyInt1' is 'const int'
// CHECK-MESSAGES: :[[@LINE-5]]:31: note: after resolving type aliases, the common type of 'CInt' and 'const MyInt2' is 'const int'
// CHECK-MESSAGES: :[[@LINE-6]]:41: note: after resolving type aliases, the common type of 'const MyInt1' and 'const MyInt2' is 'const int'
void qualifiedThroughTypedef5(CMyInt1 CMI1, CMyInt2 CMI2) {}
// CHECK-MESSAGES: :[[@LINE-1]]:31: warning: 2 adjacent parameters of 'qualifiedThroughTypedef5' of similar type are
// CHECK-MESSAGES: :[[@LINE-2]]:39: note: the first parameter in the range is 'CMI1'
// CHECK-MESSAGES: :[[@LINE-3]]:53: note: the last parameter in the range is 'CMI2'
// CHECK-MESSAGES: :[[@LINE-4]]:31: note: after resolving type aliases, the common type of 'CMyInt1' and 'CMyInt2' is 'const int'
void qualifiedThroughTypedef6(CMyInt1 CMI1, int I) {}
// CHECK-MESSAGES: :[[@LINE-1]]:31: warning: 2 adjacent parameters of 'qualifiedThroughTypedef6' of similar type are
// CHECK-MESSAGES: :[[@LINE-2]]:39: note: the first parameter in the range is 'CMI1'
// CHECK-MESSAGES: :[[@LINE-3]]:49: note: the last parameter in the range is 'I'
// CHECK-MESSAGES: :[[@LINE-4]]:31: note: after resolving type aliases, 'CMyInt1' and 'int' share a common type
// CHECK-MESSAGES: :[[@LINE-5]]:45: note: 'CMyInt1' and 'int' parameters accept and bind the same kind of values
void referenceToTypedef1(CInt &CIR, int I) {}
// CHECK-MESSAGES: :[[@LINE-1]]:26: warning: 2 adjacent parameters of 'referenceToTypedef1' of similar type are
// CHECK-MESSAGES: :[[@LINE-2]]:32: note: the first parameter in the range is 'CIR'
// CHECK-MESSAGES: :[[@LINE-3]]:41: note: the last parameter in the range is 'I'
// CHECK-MESSAGES: :[[@LINE-4]]:37: note: 'CInt &' and 'int' parameters accept and bind the same kind of values
template <typename T>
void copy(const T *Dest, T *Source) {}
// CHECK-MESSAGES: :[[@LINE-1]]:11: warning: 2 adjacent parameters of 'copy' of similar type are
// CHECK-MESSAGES: :[[@LINE-2]]:20: note: the first parameter in the range is 'Dest'
// CHECK-MESSAGES: :[[@LINE-3]]:29: note: the last parameter in the range is 'Source'
// CHECK-MESSAGES: :[[@LINE-4]]:26: note: 'const T *' and 'T *' parameters accept and bind the same kind of values

View file

@ -2,7 +2,8 @@
// 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: "bool;MyBool;struct U;MAKE_LOGICAL_TYPE(int)"} \
// 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: ]}' -- -x c
#define bool _Bool