[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