"Constreverseiterator",
"constreverseiterator"});
+/// The default value for the QualifiersMix check option.
+static constexpr bool DefaultQualifiersMix = false;
+
using namespace clang::ast_matchers;
namespace clang {
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();
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
Str[3] = 't';
if (hasFlag(F, MixFlags::ReferenceBind))
Str[4] = '&';
+ if (hasFlag(F, MixFlags::Qualifiers))
+ Str[5] = 'Q';
return Str.str().str();
}
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
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.
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()) {
Ty.dump(llvm::dbgs(), Ctx); llvm::dbgs() << '\n';);
QualType ReferredType = LRef->getPointeeType();
- if (!ReferredType.isLocalConstQualified()) {
+ 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. 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 ref.\n");
+ << "<<< 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");
/// 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 {
Options.get("IgnoredParameterNames", DefaultIgnoredParameterNames))),
IgnoredParameterTypeSuffixes(optutils::parseStringList(
Options.get("IgnoredParameterTypeSuffixes",
- DefaultIgnoredParameterTypeSuffixes))) {}
+ DefaultIgnoredParameterTypeSuffixes))),
+ QualifiersMix(Options.get("QualifiersMix", DefaultQualifiersMix)) {}
void EasilySwappableParametersCheck::storeOptions(
ClangTidyOptions::OptionMap &Opts) {
optutils::serializeStringList(IgnoredParameterNames));
Options.store(Opts, "IgnoredParameterTypeSuffixes",
optutils::serializeStringList(IgnoredParameterTypeSuffixes));
+ Options.store(Opts, "QualifiersMix", QualifiersMix);
}
void EasilySwappableParametersCheck::registerMatchers(MatchFinder *Finder) {
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)
- DiagText =
- "after resolving type aliases, '%0' and '%1' are the same";
+ 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 {
DiagText = "after resolving type aliases, the common type of '%0' "
"and '%1' is '%2'";
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;
}
}
}
/// 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
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
^^^^^^^^^^^^^^^^^
// 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.
// 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 {
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')
// 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'
// 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 qualifiedTypeAndQualifiedPtr1(const int *CIP, int *const volatile IPCV) {} // NO-WARN: Not the same type.
-void qualifiedThroughTypedef1(int I, CInt CI) {} // 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
// 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.
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;
// 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.
--- /dev/null
+// 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
// 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