[clang-tools-extra] 26d864b - [clang-tidy] Extend 'bugprone-easily-swappable-parameters' with `typedef` and `const &` diagnostics

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


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

URL: https://github.com/llvm/llvm-project/commit/26d864b44b9d3326984a7041124aa0f9e8ebc5cb
DIFF: https://github.com/llvm/llvm-project/commit/26d864b44b9d3326984a7041124aa0f9e8ebc5cb.diff

LOG: [clang-tidy] Extend 'bugprone-easily-swappable-parameters' with `typedef` and `const &` diagnostics

The base patch only deals with strict (canonical) type equality, which is
merely a subset of all the dangerous function interfaces that we intend to
find.
In addition, in the base patch, canonical type equivalence is not diagnosed in
a way that is immediately apparent to the user.

This patch extends the check with two features:

 * Proper typedef diagnostics and explanations to the user.
 * "Reference bind power" matching.

Case 2 is a necessary addition because in every case someone encounters a
function `f(T t, const T& tr)`, any expression that might be passed to either
can be passed to both. Thus, such adjacent parameter sequences should be
matched.

Reviewed By: aaron.ballman

Differential Revision: http://reviews.llvm.org/D95736

Added: 
    

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

Removed: 
    


################################################################################
diff  --git a/clang-tools-extra/clang-tidy/bugprone/EasilySwappableParametersCheck.cpp b/clang-tools-extra/clang-tidy/bugprone/EasilySwappableParametersCheck.cpp
index 07c8ef486f654..d9124e6c8361b 100644
--- a/clang-tools-extra/clang-tidy/bugprone/EasilySwappableParametersCheck.cpp
+++ b/clang-tools-extra/clang-tidy/bugprone/EasilySwappableParametersCheck.cpp
@@ -11,6 +11,7 @@
 #include "clang/AST/ASTContext.h"
 #include "clang/ASTMatchers/ASTMatchFinder.h"
 #include "clang/Lex/Lexer.h"
+#include "llvm/ADT/SmallSet.h"
 
 #define DEBUG_TYPE "EasilySwappableParametersCheck"
 #include "llvm/Support/Debug.h"
@@ -76,12 +77,15 @@ namespace model {
 enum class MixFlags : unsigned char {
   Invalid = 0, //< Sentinel bit pattern. DO NOT USE!
 
-  None = 1,      //< Mix between the two parameters is not possible.
-  Trivial = 2,   //< The two mix trivially, and are the exact same type.
-  Canonical = 4, //< The two mix because the types refer to the same
-                 // CanonicalType, but we do not elaborate as to how.
+  None = 1,           //< Mix between the two parameters is not possible.
+  Trivial = 2,        //< The two mix trivially, and are the exact same type.
+  Canonical = 4,      //< The two mix because the types refer to the same
+                      // CanonicalType, but we do not elaborate as to how.
+  TypeAlias = 8,      //< The path from one type to the other involves
+                      // desugaring type aliases.
+  ReferenceBind = 16, //< The mix involves the binding power of "const &".
 
-  LLVM_MARK_AS_BITMASK_ENUM(/* LargestValue =*/Canonical)
+  LLVM_MARK_AS_BITMASK_ENUM(/* LargestValue =*/ReferenceBind)
 };
 LLVM_ENABLE_BITMASK_ENUMS_IN_NAMESPACE();
 
@@ -106,7 +110,7 @@ static inline std::string formatMixFlags(MixFlags F) {
   if (F == MixFlags::Invalid)
     return "#Inv!";
 
-  SmallString<4> Str{"---"};
+  SmallString<8> Str{"-----"};
 
   if (hasFlag(F, MixFlags::None))
     // Shows the None bit explicitly, as it can be applied in the recursion
@@ -116,6 +120,10 @@ static inline std::string formatMixFlags(MixFlags F) {
     Str[1] = 'T';
   if (hasFlag(F, MixFlags::Canonical))
     Str[2] = 'C';
+  if (hasFlag(F, MixFlags::TypeAlias))
+    Str[3] = 't';
+  if (hasFlag(F, MixFlags::ReferenceBind))
+    Str[4] = '&';
 
   return Str.str().str();
 }
@@ -129,13 +137,44 @@ static inline std::string formatMixFlags(MixFlags F);
 /// Contains the metadata for the mixability result between two types,
 /// independently of which parameters they were calculated from.
 struct MixData {
+  /// The flag bits of the mix indicating what language features allow for it.
   MixFlags Flags;
 
+  /// A potentially calculated common underlying type after desugaring, that
+  /// both sides of the mix can originate from.
+  QualType CommonType;
+
   MixData(MixFlags Flags) : Flags(Flags) {}
+  MixData(MixFlags Flags, QualType CommonType)
+      : Flags(Flags), CommonType(CommonType) {}
 
   void sanitize() {
     assert(Flags != MixFlags::Invalid && "sanitize() called on invalid bitvec");
-    // TODO: There will be statements here in further extensions of the check.
+
+    if (hasFlag(Flags, MixFlags::None)) {
+      // If anywhere down the recursion a potential mix "path" is deemed
+      // impossible, throw away all the other bits because the mix is not
+      // possible.
+      Flags = MixFlags::None;
+      return;
+    }
+
+    if (Flags == MixFlags::Trivial)
+      return;
+
+    if (static_cast<bool>(Flags ^ MixFlags::Trivial))
+      // If the mix involves somewhere trivial equivalence but down the
+      // recursion other bit(s) were set, remove the trivial bit, as it is not
+      // trivial.
+      Flags &= ~MixFlags::Trivial;
+  }
+
+  MixData operator|(MixFlags EnableFlags) const {
+    return {Flags | EnableFlags, CommonType};
+  }
+  MixData &operator|=(MixFlags EnableFlags) {
+    Flags |= EnableFlags;
+    return *this;
   }
 };
 
@@ -150,6 +189,7 @@ struct Mix {
 
   void sanitize() { Data.sanitize(); }
   MixFlags flags() const { return Data.Flags; }
+  QualType commonUnderlyingType() const { return Data.CommonType; }
 };
 
 // NOLINTNEXTLINE(misc-redundant-expression): Seems to be a bogus warning.
@@ -186,6 +226,11 @@ struct MixableParameterRange {
   }
 };
 
+static MixData isLRefEquallyBindingToType(const TheCheck &Check,
+                                          const LValueReferenceType *LRef,
+                                          QualType Ty, const ASTContext &Ctx,
+                                          bool IsRefRHS);
+
 /// Approximate the way how LType and RType might refer to "essentially the
 /// same" type, in a sense that at a particular call site, an expression of
 /// type LType and RType might be successfully passed to a variable (in our
@@ -204,24 +249,94 @@ static MixData calculateMixability(const TheCheck &Check, const QualType LType,
 
   if (LType == RType) {
     LLVM_DEBUG(llvm::dbgs() << "<<< calculateMixability. Trivial equality.\n");
-    return {MixFlags::Trivial};
+    return {MixFlags::Trivial, LType};
+  }
+
+  // Dissolve certain type sugars that do not affect the mixability of one type
+  // with the other, and also do not require any sort of elaboration for the
+  // user to understand.
+  if (isa<ParenType>(LType.getTypePtr())) {
+    LLVM_DEBUG(llvm::dbgs() << "--- calculateMixability. LHS is ParenType.\n");
+    return calculateMixability(Check, LType.getSingleStepDesugaredType(Ctx),
+                               RType, Ctx);
+  }
+  if (isa<ParenType>(RType.getTypePtr())) {
+    LLVM_DEBUG(llvm::dbgs() << "--- calculateMixability. RHS is ParenType.\n");
+    return calculateMixability(Check, 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;
   }
 
-  // TODO: Implement more elaborate logic, such as typedef, implicit
-  // conversions, etc.
+  // 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.
+  if (const auto *LRef = LType->getAs<LValueReferenceType>()) {
+    LLVM_DEBUG(llvm::dbgs() << "--- calculateMixability. LHS is &.\n");
+    return isLRefEquallyBindingToType(Check, LRef, RType, Ctx, false) |
+           MixFlags::ReferenceBind;
+  }
+  if (const auto *RRef = RType->getAs<LValueReferenceType>()) {
+    LLVM_DEBUG(llvm::dbgs() << "--- calculateMixability. RHS is &.\n");
+    return isLRefEquallyBindingToType(Check, RRef, LType, Ctx, true) |
+           MixFlags::ReferenceBind;
+  }
 
   // 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()) {
     LLVM_DEBUG(llvm::dbgs()
                << "<<< calculateMixability. Same CanonicalType.\n");
-    return {MixFlags::Canonical};
+    return {MixFlags::Canonical, LType.getCanonicalType()};
   }
 
   LLVM_DEBUG(llvm::dbgs() << "<<< calculateMixability. No match found.\n");
   return {MixFlags::None};
 }
 
+/// Calculates if the reference binds an expression of the given type. This is
+/// true iff 'LRef' is some 'const T &' type, and the 'Ty' is 'T' or 'const T'.
+static MixData isLRefEquallyBindingToType(const TheCheck &Check,
+                                          const LValueReferenceType *LRef,
+                                          QualType Ty, const ASTContext &Ctx,
+                                          bool IsRefRHS) {
+  LLVM_DEBUG(llvm::dbgs() << ">>> isLRefEquallyBindingToType for LRef:\n";
+             LRef->dump(llvm::dbgs(), Ctx); llvm::dbgs() << "\nand Type:\n";
+             Ty.dump(llvm::dbgs(), Ctx); llvm::dbgs() << '\n';);
+
+  QualType ReferredType = LRef->getPointeeType();
+  if (!ReferredType.isLocalConstQualified()) {
+    LLVM_DEBUG(llvm::dbgs()
+               << "<<< isLRefEquallyBindingToType. Not const ref.\n");
+    return {MixFlags::None};
+  };
+
+  QualType NonConstReferredType = ReferredType;
+  NonConstReferredType.removeLocalConst();
+  if (ReferredType == Ty || NonConstReferredType == Ty) {
+    LLVM_DEBUG(
+        llvm::dbgs()
+        << "<<< isLRefEquallyBindingToType. Type of referred matches.\n");
+    return {MixFlags::Trivial, ReferredType};
+  }
+
+  LLVM_DEBUG(
+      llvm::dbgs()
+      << "--- isLRefEquallyBindingToType. Checking mix for underlying type.\n");
+  return IsRefRHS ? calculateMixability(Check, Ty, NonConstReferredType, Ctx)
+                  : calculateMixability(Check, NonConstReferredType, Ty, Ctx);
+}
+
 static MixableParameterRange modelMixingRange(const TheCheck &Check,
                                               const FunctionDecl *FD,
                                               std::size_t StartIndex) {
@@ -390,6 +505,85 @@ static SmallString<64> getNameOrUnnamed(const NamedDecl *ND) {
   return Name;
 }
 
+/// 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));
+}
+
+namespace {
+
+/// Retains the elements called with and returns whether the call is done with
+/// a new element.
+template <typename E, std::size_t N> class InsertOnce {
+  llvm::SmallSet<E, N> CalledWith;
+
+public:
+  bool operator()(E El) { return CalledWith.insert(std::move(El)).second; }
+
+  bool calledWith(const E &El) const { return CalledWith.contains(El); }
+};
+
+struct SwappedEqualQualTypePair {
+  QualType LHSType, RHSType;
+
+  bool operator==(const SwappedEqualQualTypePair &Other) const {
+    return (LHSType == Other.LHSType && RHSType == Other.RHSType) ||
+           (LHSType == Other.RHSType && RHSType == Other.LHSType);
+  }
+
+  bool operator<(const SwappedEqualQualTypePair &Other) const {
+    return LHSType < Other.LHSType && RHSType < Other.RHSType;
+  }
+};
+
+struct TypeAliasDiagnosticTuple {
+  QualType LHSType, RHSType, CommonType;
+
+  bool operator==(const TypeAliasDiagnosticTuple &Other) const {
+    return CommonType == Other.CommonType &&
+           ((LHSType == Other.LHSType && RHSType == Other.RHSType) ||
+            (LHSType == Other.RHSType && RHSType == Other.LHSType));
+  }
+
+  bool operator<(const TypeAliasDiagnosticTuple &Other) const {
+    return CommonType < Other.CommonType && LHSType < Other.LHSType &&
+           RHSType < Other.RHSType;
+  }
+};
+
+/// Helper class to only emit a diagnostic related to MixFlags::TypeAlias once.
+class UniqueTypeAliasDiagnosticHelper
+    : public InsertOnce<TypeAliasDiagnosticTuple, 8> {
+  using Base = InsertOnce<TypeAliasDiagnosticTuple, 8>;
+
+public:
+  /// Returns whether the diagnostic for LHSType and RHSType which are both
+  /// referring to CommonType being the same has not been emitted already.
+  bool operator()(QualType LHSType, QualType RHSType, QualType CommonType) {
+    if (CommonType.isNull() || CommonType == LHSType || CommonType == RHSType)
+      return Base::operator()({LHSType, RHSType, {}});
+
+    TypeAliasDiagnosticTuple ThreeTuple{LHSType, RHSType, CommonType};
+    if (!Base::operator()(ThreeTuple))
+      return false;
+
+    bool AlreadySaidLHSAndCommonIsSame = calledWith({LHSType, CommonType, {}});
+    bool AlreadySaidRHSAndCommonIsSame = calledWith({RHSType, CommonType, {}});
+    if (AlreadySaidLHSAndCommonIsSame && AlreadySaidRHSAndCommonIsSame) {
+      // "SomeInt == int" && "SomeOtherInt == int" => "Common(SomeInt,
+      // SomeOtherInt) == int", no need to diagnose it. Save the 3-tuple only
+      // for shortcut if it ever appears again.
+      return false;
+    }
+
+    return true;
+  }
+};
+
+} // namespace
+
 EasilySwappableParametersCheck::EasilySwappableParametersCheck(
     StringRef Name, ClangTidyContext *Context)
     : ClangTidyCheck(Name, Context),
@@ -430,6 +624,9 @@ void EasilySwappableParametersCheck::registerMatchers(MatchFinder *Finder) {
 
 void EasilySwappableParametersCheck::check(
     const MatchFinder::MatchResult &Result) {
+  using namespace model;
+  using namespace filter;
+
   const auto *FD = Result.Nodes.getNodeAs<FunctionDecl>("func");
   assert(FD);
 
@@ -440,16 +637,15 @@ void EasilySwappableParametersCheck::check(
   LLVM_DEBUG(llvm::dbgs() << "Begin analysis of " << getName(FD) << " with "
                           << NumParams << " parameters...\n");
   while (MixableRangeStartIndex < NumParams) {
-    if (filter::isIgnoredParameter(*this,
-                                   FD->getParamDecl(MixableRangeStartIndex))) {
+    if (isIgnoredParameter(*this, FD->getParamDecl(MixableRangeStartIndex))) {
       LLVM_DEBUG(llvm::dbgs()
                  << "Parameter #" << MixableRangeStartIndex << " ignored.\n");
       ++MixableRangeStartIndex;
       continue;
     }
 
-    model::MixableParameterRange R =
-        model::modelMixingRange(*this, FD, MixableRangeStartIndex);
+    MixableParameterRange R =
+        modelMixingRange(*this, FD, MixableRangeStartIndex);
     assert(R.NumParamsChecked > 0 && "Ensure forward progress!");
     MixableRangeStartIndex += R.NumParamsChecked;
     if (R.NumParamsChecked < MinimumLength) {
@@ -458,16 +654,23 @@ void EasilySwappableParametersCheck::check(
       continue;
     }
 
+    bool NeedsAnyTypeNote = llvm::any_of(R.Mixes, needsToPrintTypeInDiagnostic);
     const ParmVarDecl *First = R.getFirstParam(), *Last = R.getLastParam();
     std::string FirstParamTypeAsWritten = First->getType().getAsString(PP);
     {
-      StringRef DiagText = "%0 adjacent parameters of %1 of similar type "
-                           "('%2') are easily swapped by mistake";
-      // TODO: This logic will get extended here with future flags.
+      StringRef DiagText;
+
+      if (NeedsAnyTypeNote)
+        DiagText = "%0 adjacent parameters of %1 of similar type are easily "
+                   "swapped by mistake";
+      else
+        DiagText = "%0 adjacent parameters of %1 of similar type ('%2') are "
+                   "easily swapped by mistake";
 
       auto Diag = diag(First->getOuterLocStart(), DiagText)
-                  << static_cast<unsigned>(R.NumParamsChecked) << FD
-                  << FirstParamTypeAsWritten;
+                  << static_cast<unsigned>(R.NumParamsChecked) << FD;
+      if (!NeedsAnyTypeNote)
+        Diag << FirstParamTypeAsWritten;
 
       CharSourceRange HighlightRange = CharSourceRange::getTokenRange(
           First->getBeginLoc(), Last->getEndLoc());
@@ -487,6 +690,58 @@ void EasilySwappableParametersCheck::check(
         << getNameOrUnnamed(Last)
         << CharSourceRange::getTokenRange(Last->getLocation(),
                                           Last->getLocation());
+
+    // Helper classes to silence elaborative diagnostic notes that would be
+    // too verbose.
+    UniqueTypeAliasDiagnosticHelper UniqueTypeAlias;
+    InsertOnce<SwappedEqualQualTypePair, 8> UniqueBindPower;
+
+    for (const Mix &M : R.Mixes) {
+      assert(M.flags() >= MixFlags::Trivial &&
+             "Sentinel or false mix in result.");
+
+      if (needsToPrintTypeInDiagnostic(M)) {
+        // Typedefs might result in the type of the variable needing to be
+        // emitted to a note diagnostic, so prepare it.
+        const ParmVarDecl *LVar = M.First;
+        const ParmVarDecl *RVar = M.Second;
+        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 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";
+          else {
+            DiagText = "after resolving type aliases, the common type of '%0' "
+                       "and '%1' is '%2'";
+            ExplicitlyPrintCommonType = true;
+          }
+
+          auto Diag =
+              diag(LVar->getOuterLocStart(), DiagText, DiagnosticIDs::Note)
+              << LTypeAsWritten << RTypeAsWritten;
+          if (ExplicitlyPrintCommonType)
+            Diag << CommonTypeStr;
+        }
+
+        if (hasFlag(M.flags(), MixFlags::ReferenceBind) &&
+            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;
+        }
+      }
+    }
   }
 }
 

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 bc4e3220428d3..22224f39beab0 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
@@ -111,3 +111,28 @@ None of the following cases produce a diagnostic:
 
         add(1, 2); // Instantiates 'add<int, int>', but that's not a user-defined function.
     }
+
+Due to the limitation above, parameters which type are further dependent upon
+template instantiations to *prove* that they mix with another parameter's is
+not diagnosed.
+
+.. code-block:: c++
+
+    template <typename T>
+    struct Vector {
+      typedef T element_type;
+    };
+
+    // Diagnosed: Explicit instantiation was done by the user, we can prove it
+    // is the same type.
+    void Explicit(int A, Vector<int>::element_type B) { /* ... */ }
+
+    // Diagnosed: The two parameter types are exactly the same.
+    template <typename T>
+    void Exact(typename Vector<T>::element_type A,
+               typename Vector<T>::element_type B) { /* ... */ }
+
+    // Skipped: The two parameters are both 'T' but we can not prove this
+    // without actually instantiating.
+    template <typename T>
+    void FalseNegative(T A, typename Vector<T>::element_type B) { /* ... */ }

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 f1c8c277d50a0..9de0787b971d1 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
@@ -115,20 +115,38 @@ void typedefAndTypedef2(MyInt2 I1, MyInt2 I2) {}
 // CHECK-MESSAGES: :[[@LINE-2]]:32: note: the first parameter in the range is 'I1'
 // CHECK-MESSAGES: :[[@LINE-3]]:43: note: the last parameter in the range is 'I2'
 
-void throughTypedef(int I, MyInt1 J) {}
-// CHECK-MESSAGES: :[[@LINE-1]]:21: warning: 2 adjacent parameters of 'throughTypedef' of similar type ('int')
-// CHECK-MESSAGES: :[[@LINE-2]]:25: note: the first parameter in the range is 'I'
-// CHECK-MESSAGES: :[[@LINE-3]]:35: note: the last parameter in the range is 'J'
+void typedefMultiple(MyInt1 I1, MyInt2 I2x, MyInt2 I2y) {}
+// CHECK-MESSAGES: :[[@LINE-1]]:22: warning: 3 adjacent parameters of 'typedefMultiple' of similar type are
+// CHECK-MESSAGES: :[[@LINE-2]]:29: note: the first parameter in the range is 'I1'
+// CHECK-MESSAGES: :[[@LINE-3]]:52: note: the last parameter in the range is 'I2y'
+// CHECK-MESSAGES: :[[@LINE-4]]:22: note: after resolving type aliases, the common type of 'MyInt1' and 'MyInt2' is 'int'
+
+void throughTypedef1(int I, MyInt1 J) {}
+// CHECK-MESSAGES: :[[@LINE-1]]:22: warning: 2 adjacent parameters of 'throughTypedef1' of similar type are
+// CHECK-MESSAGES: :[[@LINE-2]]:26: note: the first parameter in the range is 'I'
+// CHECK-MESSAGES: :[[@LINE-3]]:36: note: the last parameter in the range is 'J'
+// CHECK-MESSAGES: :[[@LINE-4]]:22: note: after resolving type aliases, 'int' and 'MyInt1' are the same
+
+void betweenTypedef2(MyInt1 I, MyInt2 J) {}
+// CHECK-MESSAGES: :[[@LINE-1]]:22: warning: 2 adjacent parameters of 'betweenTypedef2' of similar type are
+// CHECK-MESSAGES: :[[@LINE-2]]:29: note: the first parameter in the range is 'I'
+// CHECK-MESSAGES: :[[@LINE-3]]:39: note: the last parameter in the range is 'J'
+// CHECK-MESSAGES: :[[@LINE-4]]:22: note: after resolving type aliases, the common type of 'MyInt1' and 'MyInt2' is 'int'
+
+typedef MyInt2 MyInt2b;
 
-void betweenTypedef(MyInt1 I, MyInt2 J) {}
-// CHECK-MESSAGES: :[[@LINE-1]]:21: warning: 2 adjacent parameters of 'betweenTypedef' of similar type ('MyInt1')
-// CHECK-MESSAGES: :[[@LINE-2]]:28: note: the first parameter in the range is 'I'
-// CHECK-MESSAGES: :[[@LINE-3]]:38: note: the last parameter in the range is 'J'
+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-3]]:58: note: the last parameter in the range is 'MI2b'
+// CHECK-MESSAGES: :[[@LINE-4]]:19: note: after resolving type aliases, 'int' and 'MyInt1' are the same
+// 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: Not the same type.
+void throughTypedefToOtherType(MyInt1 I, MyLong1 J) {} // NO-WARN: int and long.
 
 void qualified1(int I, const int CI) {} // NO-WARN: Not the same type.
 
@@ -142,18 +160,73 @@ using CInt = const int;
 
 void qualifiedThroughTypedef1(int I, CInt CI) {} // NO-WARN: Not the same type.
 
-void qualifiedThroughTypedef2(CInt CI1, const int CI2) {} // NO-WARN: Not the same type.
-// CHECK-MESSAGES: :[[@LINE-1]]:31: warning: 2 adjacent parameters of 'qualifiedThroughTypedef2' of similar type ('CInt')
+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'
-
-void reference1(int I, int &IR) {} // NO-WARN: Not the same type.
-
-void reference2(int I, const int &CIR) {} // NO-WARN: Not the same type.
-
-void reference3(int I, int &&IRR) {} // NO-WARN: Not the same type.
-
-void reference4(int I, const int &&CIRR) {} // NO-WARN: Not the same type.
+// 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 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-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'
+
+void reference1(int I, int &IR) {} // NO-WARN: Distinct semantics when called.
+
+void reference2(int I, const int &CIR) {}
+// CHECK-MESSAGES: :[[@LINE-1]]:17: warning: 2 adjacent parameters of 'reference2' of similar type are
+// CHECK-MESSAGES: :[[@LINE-2]]:21: note: the first parameter in the range is 'I'
+// CHECK-MESSAGES: :[[@LINE-3]]:35: note: the last parameter in the range is 'CIR'
+// CHECK-MESSAGES: :[[@LINE-4]]:24: note: 'int' and 'const int &' parameters accept and bind the same kind of values
+
+void reference3(int I, int &&IRR) {} // NO-WARN: Distinct semantics when called.
+
+void reference4(int I, const int &&CIRR) {} // NO-WARN: Distinct semantics when called.
+
+void reference5(const int CI, const int &CIR) {}
+// CHECK-MESSAGES: :[[@LINE-1]]:17: warning: 2 adjacent parameters of 'reference5' of similar type are
+// CHECK-MESSAGES: :[[@LINE-2]]:27: note: the first parameter in the range is 'CI'
+// CHECK-MESSAGES: :[[@LINE-3]]:42: note: the last parameter in the range is 'CIR'
+// CHECK-MESSAGES: :[[@LINE-4]]:31: note: 'const int' and 'const int &' parameters accept and bind the same kind of values
+
+void reference6(int I, const int &CIR, int J, const int &CJR) {}
+// CHECK-MESSAGES: :[[@LINE-1]]:17: warning: 4 adjacent parameters of 'reference6' of similar type are
+// CHECK-MESSAGES: :[[@LINE-2]]:21: note: the first parameter in the range is 'I'
+// CHECK-MESSAGES: :[[@LINE-3]]:58: note: the last parameter in the range is 'CJR'
+// CHECK-MESSAGES: :[[@LINE-4]]:24: note: 'int' and 'const int &' parameters accept and bind the same kind of values
+
+using ICRTy = const int &;
+using MyIntCRTy = const MyInt1 &;
+
+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
+
+short const typedef int unsigned Eldritch;
+typedef const unsigned short Holy;
+
+void collapse(Eldritch Cursed, Holy Blessed) {}
+// CHECK-MESSAGES: :[[@LINE-1]]:15: warning: 2 adjacent parameters of 'collapse' of similar type are
+// CHECK-MESSAGES: :[[@LINE-2]]:24: note: the first parameter in the range is 'Cursed'
+// CHECK-MESSAGES: :[[@LINE-3]]:37: note: the last parameter in the range is 'Blessed'
+// CHECK-MESSAGES: :[[@LINE-4]]:15: note: after resolving type aliases, the common type of 'Eldritch' and 'Holy' is 'const unsigned short'
+
+void collapseAndTypedef(Eldritch Cursed, const Holy &Blessed) {}
+// CHECK-MESSAGES: :[[@LINE-1]]:25: warning: 2 adjacent parameters of 'collapseAndTypedef' of similar type are
+// CHECK-MESSAGES: :[[@LINE-2]]:34: note: the first parameter in the range is 'Cursed'
+// CHECK-MESSAGES: :[[@LINE-3]]:54: note: the last parameter in the range is 'Blessed'
+// CHECK-MESSAGES: :[[@LINE-4]]:25: note: after resolving type aliases, the common type of 'Eldritch' and 'const Holy &' is 'const unsigned short'
+// CHECK-MESSAGES: :[[@LINE-5]]:42: note: 'Eldritch' and 'const Holy &' parameters accept and bind the same kind of values
 
 template <typename T1, typename T2>
 struct Pair {};
@@ -186,3 +259,54 @@ void templateVariadic2(int TVar, int UVars1, int UVars2) {}
 // CHECK-MESSAGES: :[[@LINE-1]]:24: warning: 3 adjacent parameters of 'templateVariadic2<int, int, int>' of similar type ('int')
 // CHECK-MESSAGES: :[[@LINE-2]]:28: note: the first parameter in the range is 'TVar'
 // CHECK-MESSAGES: :[[@LINE-3]]:50: note: the last parameter in the range is 'UVars2'
+
+template <typename T>
+using TwoOf = Pair<T, T>;
+
+void templateAndAliasTemplate(Pair<int, int> P, TwoOf<int> I) {}
+// CHECK-MESSAGES: :[[@LINE-1]]:31: warning: 2 adjacent parameters of 'templateAndAliasTemplate' of similar type ('Pair<int, int>')
+// CHECK-MESSAGES: :[[@LINE-2]]:46: note: the first parameter in the range is 'P'
+// CHECK-MESSAGES: :[[@LINE-3]]:60: note: the last parameter in the range is 'I'
+
+template <typename T>
+struct Vector {
+  typedef T element_type;
+  typedef T &reference_type;
+  typedef const T const_element_type;
+  typedef const T &const_reference_type;
+};
+
+void memberTypedef(int I, Vector<int>::element_type E) {}
+// CHECK-MESSAGES: :[[@LINE-1]]:20: warning: 2 adjacent parameters of 'memberTypedef' of similar type are
+// CHECK-MESSAGES: :[[@LINE-2]]:24: note: the first parameter in the range is 'I'
+// CHECK-MESSAGES: :[[@LINE-3]]:53: note: the last parameter in the range is 'E'
+// CHECK-MESSAGES: :[[@LINE-4]]:20: note: after resolving type aliases, 'int' and 'Vector<int>::element_type' are the same
+
+template <typename T>
+void memberTypedefDependent1(T T1, typename Vector<T>::element_type T2) {} // NO-WARN: Dependent name is not instantiated and resolved against other type.
+
+template <typename T>
+void memberTypedefDependent2(typename Vector<T>::element_type E1,
+                             typename Vector<T>::element_type E2) {}
+// CHECK-MESSAGES: :[[@LINE-2]]:30: warning: 2 adjacent parameters of 'memberTypedefDependent2' of similar type ('typename Vector<T>::element_type')
+// CHECK-MESSAGES: :[[@LINE-3]]:63: note: the first parameter in the range is 'E1'
+// CHECK-MESSAGES: :[[@LINE-3]]:63: note: the last parameter in the range is 'E2'
+
+template <typename T>
+void memberTypedefDependentReference1(
+    typename Vector<T>::element_type E,
+    typename Vector<T>::const_element_type &R) {} // NO-WARN: Not instantiated.
+
+template <typename T>
+void memberTypedefDependentReference2(
+    typename Vector<T>::element_type E,
+    typename Vector<T>::const_reference_type R) {} // NO-WARN: Not instantiated.
+
+template <typename T>
+void memberTypedefDependentReference3(
+    typename Vector<T>::element_type E,
+    const typename Vector<T>::element_type &R) {}
+// CHECK-MESSAGES: :[[@LINE-2]]:5: warning: 2 adjacent parameters of 'memberTypedefDependentReference3' of similar type are
+// CHECK-MESSAGES: :[[@LINE-3]]:38: note: the first parameter in the range is 'E'
+// CHECK-MESSAGES: :[[@LINE-3]]:45: note: the last parameter in the range is 'R'
+// CHECK-MESSAGES: :[[@LINE-4]]:5: note: 'typename Vector<T>::element_type' and 'const typename Vector<T>::element_type &' parameters accept and bind the same kind of values


        


More information about the cfe-commits mailing list