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

via cfe-commits cfe-commits at lists.llvm.org
Mon Jun 28 01:50:43 PDT 2021


Author: Whisperity
Date: 2021-06-28T10:49:37+02:00
New Revision: 961e9e6af65ef097678c57fe5f1c18b825eb723f

URL: https://github.com/llvm/llvm-project/commit/961e9e6af65ef097678c57fe5f1c18b825eb723f
DIFF: https://github.com/llvm/llvm-project/commit/961e9e6af65ef097678c57fe5f1c18b825eb723f.diff

LOG: [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

Added: 
    clang-tools-extra/test/clang-tidy/checkers/bugprone-easily-swappable-parameters-qualifiermixing.cpp

Modified: 
    clang-tools-extra/clang-tidy/bugprone/EasilySwappableParametersCheck.cpp
    clang-tools-extra/clang-tidy/bugprone/EasilySwappableParametersCheck.h
    clang-tools-extra/docs/clang-tidy/checks/bugprone-easily-swappable-parameters.rst
    clang-tools-extra/test/clang-tidy/checkers/bugprone-easily-swappable-parameters-ignore.cpp
    clang-tools-extra/test/clang-tidy/checkers/bugprone-easily-swappable-parameters-len2.cpp
    clang-tools-extra/test/clang-tidy/checkers/bugprone-easily-swappable-parameters-len3.cpp
    clang-tools-extra/test/clang-tidy/checkers/bugprone-easily-swappable-parameters.c

Removed: 
    


################################################################################
diff  --git a/clang-tools-extra/clang-tidy/bugprone/EasilySwappableParametersCheck.cpp b/clang-tools-extra/clang-tidy/bugprone/EasilySwappableParametersCheck.cpp
index d9124e6c8361b..8266ee62ada64 100644
--- a/clang-tools-extra/clang-tidy/bugprone/EasilySwappableParametersCheck.cpp
+++ b/clang-tools-extra/clang-tidy/bugprone/EasilySwappableParametersCheck.cpp
@@ -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 
diff erently
+  // 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()) {
+  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");
@@ -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,18 +793,21 @@ 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)
-            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 @@ 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;
         }
       }
     }

diff  --git a/clang-tools-extra/clang-tidy/bugprone/EasilySwappableParametersCheck.h b/clang-tools-extra/clang-tidy/bugprone/EasilySwappableParametersCheck.h
index 6d236a205cef5..e3c58eb5d7013 100644
--- a/clang-tools-extra/clang-tidy/bugprone/EasilySwappableParametersCheck.h
+++ b/clang-tools-extra/clang-tidy/bugprone/EasilySwappableParametersCheck.h
@@ -38,6 +38,9 @@ class EasilySwappableParametersCheck : public ClangTidyCheck {
   /// 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

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
index 22224f39beab0..b9dafd3b32602 100644
--- 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 @@ 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
+    
diff erently *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 
diff erently 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
index c04b1bab74e31..a61c666ac9682 100644
--- 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
index 9de0787b971d1..f0c1c57e25848 100644
--- 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 @@ void 
diff erentPtrs(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 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 @@ 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;

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
index 10f8841368dfa..c833077285be9 100644
--- 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
index 0000000000000..7b9fdceda7465
--- /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 <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

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
index f52652e38636b..591a5cb353ee8 100644
--- 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


        


More information about the cfe-commits mailing list