diff --git a/clang-tools-extra/clang-tidy/bugprone/EasilySwappableParametersCheck.h b/clang-tools-extra/clang-tidy/bugprone/EasilySwappableParametersCheck.h --- a/clang-tools-extra/clang-tidy/bugprone/EasilySwappableParametersCheck.h +++ b/clang-tools-extra/clang-tidy/bugprone/EasilySwappableParametersCheck.h @@ -38,6 +38,9 @@ /// The parameter typename suffixes (as written in the source code) to be /// ignored. const std::vector IgnoredParameterTypeSuffixes; + + /// Whether to consider an unqualified and a qualified type mixable. + const bool QualifiersMix; }; } // namespace bugprone diff --git a/clang-tools-extra/clang-tidy/bugprone/EasilySwappableParametersCheck.cpp b/clang-tools-extra/clang-tidy/bugprone/EasilySwappableParametersCheck.cpp --- a/clang-tools-extra/clang-tidy/bugprone/EasilySwappableParametersCheck.cpp +++ b/clang-tools-extra/clang-tidy/bugprone/EasilySwappableParametersCheck.cpp @@ -59,6 +59,9 @@ "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 @@ 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 @@ 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 @@ 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 @@ 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 @@ RType.getSingleStepDesugaredType(Ctx), Ctx); } - // Dissolve typedefs. - if (const auto *LTypedef = LType->getAs()) { - LLVM_DEBUG(llvm::dbgs() << "--- calculateMixability. LHS is typedef.\n"); - return calculateMixability(Check, LTypedef->desugar(), RType, Ctx) | - MixFlags::TypeAlias; - } - if (const auto *RTypedef = RType->getAs()) { - 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 @@ MixFlags::ReferenceBind; } + // Dissolve typedefs after the qualifiers outside the typedef are dealt with. + if (LType->getAs()) { + LLVM_DEBUG(llvm::dbgs() << "--- calculateMixability. LHS is typedef.\n"); + return calculateMixability(Check, LType.getSingleStepDesugaredType(Ctx), + RType, Ctx) | + MixFlags::TypeAlias; + } + if (RType->getAs()) { + 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 @@ Ty.dump(llvm::dbgs(), Ctx); llvm::dbgs() << '\n';); QualType ReferredType = LRef->getPointeeType(); - if (!ReferredType.isLocalConstQualified()) { + if (!ReferredType.isLocalConstQualified() && + ReferredType->getAs()) { + 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"); @@ -508,8 +589,10 @@ /// 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(M.flags() & (model::MixFlags::TypeAlias | - model::MixFlags::ReferenceBind)); + using namespace model; + return static_cast( + M.flags() & + (MixFlags::TypeAlias | MixFlags::ReferenceBind | MixFlags::Qualifiers)); } namespace { @@ -593,7 +676,8 @@ 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 @@ optutils::serializeStringList(IgnoredParameterNames)); Options.store(Opts, "IgnoredParameterTypeSuffixes", optutils::serializeStringList(IgnoredParameterTypeSuffixes)); + Options.store(Opts, "QualifiersMix", QualifiersMix); } void EasilySwappableParametersCheck::registerMatchers(MatchFinder *Finder) { @@ -708,18 +793,21 @@ 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'"; @@ -728,17 +816,18 @@ 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; } } } diff --git a/clang-tools-extra/docs/clang-tidy/checks/bugprone-easily-swappable-parameters.rst b/clang-tools-extra/docs/clang-tidy/checks/bugprone-easily-swappable-parameters.rst --- a/clang-tools-extra/docs/clang-tidy/checks/bugprone-easily-swappable-parameters.rst +++ b/clang-tools-extra/docs/clang-tidy/checks/bugprone-easily-swappable-parameters.rst @@ -32,6 +32,34 @@ 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 ^^^^^^^^^^^^^^^^^ diff --git a/clang-tools-extra/test/clang-tidy/checkers/bugprone-easily-swappable-parameters-ignore.cpp b/clang-tools-extra/test/clang-tidy/checkers/bugprone-easily-swappable-parameters-ignore.cpp --- a/clang-tools-extra/test/clang-tidy/checkers/bugprone-easily-swappable-parameters-ignore.cpp +++ b/clang-tools-extra/test/clang-tidy/checkers/bugprone-easily-swappable-parameters-ignore.cpp @@ -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. diff --git a/clang-tools-extra/test/clang-tidy/checkers/bugprone-easily-swappable-parameters-len2.cpp b/clang-tools-extra/test/clang-tidy/checkers/bugprone-easily-swappable-parameters-len2.cpp --- a/clang-tools-extra/test/clang-tidy/checkers/bugprone-easily-swappable-parameters-len2.cpp +++ b/clang-tools-extra/test/clang-tidy/checkers/bugprone-easily-swappable-parameters-len2.cpp @@ -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 @@ 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 @@ // 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 @@ // 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 @@ -166,13 +172,32 @@ // 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 +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 @@ 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; diff --git a/clang-tools-extra/test/clang-tidy/checkers/bugprone-easily-swappable-parameters-len3.cpp b/clang-tools-extra/test/clang-tidy/checkers/bugprone-easily-swappable-parameters-len3.cpp --- a/clang-tools-extra/test/clang-tidy/checkers/bugprone-easily-swappable-parameters-len3.cpp +++ b/clang-tools-extra/test/clang-tidy/checkers/bugprone-easily-swappable-parameters-len3.cpp @@ -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. diff --git a/clang-tools-extra/test/clang-tidy/checkers/bugprone-easily-swappable-parameters-qualifiermixing.cpp b/clang-tools-extra/test/clang-tidy/checkers/bugprone-easily-swappable-parameters-qualifiermixing.cpp new file mode 100644 --- /dev/null +++ b/clang-tools-extra/test/clang-tidy/checkers/bugprone-easily-swappable-parameters-qualifiermixing.cpp @@ -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 +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 diff --git a/clang-tools-extra/test/clang-tidy/checkers/bugprone-easily-swappable-parameters.c b/clang-tools-extra/test/clang-tidy/checkers/bugprone-easily-swappable-parameters.c --- a/clang-tools-extra/test/clang-tidy/checkers/bugprone-easily-swappable-parameters.c +++ b/clang-tools-extra/test/clang-tidy/checkers/bugprone-easily-swappable-parameters.c @@ -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