11#include "clang/AST/ASTContext.h"
12#include "clang/AST/RecursiveASTVisitor.h"
13#include "clang/ASTMatchers/ASTMatchFinder.h"
14#include "clang/Lex/Lexer.h"
15#include "llvm/ADT/SmallSet.h"
17#define DEBUG_TYPE "EasilySwappableParametersCheck"
18#include "llvm/Support/Debug.h"
68 "reverse_const_iterator;"
69 "ConstReverseIterator;"
70 "Const_Reverse_Iterator;"
71 "const_reverse_iterator;"
72 "Constreverseiterator;"
73 "constreverseiterator";
87static constexpr std::size_t
102 const ParmVarDecl *Param1,
const ParmVarDecl *Param2);
104 StringRef Str1, StringRef Str2);
137 "can't be used to detect lack of all bits!");
140 return (Data & SearchedFlag) == SearchedFlag;
155 SmallString<8> Str{
"-------"};
177 return Str.str().str();
229 const CXXConstructorDecl *
Fun;
235 const CXXConversionDecl *
Fun;
265 explicit operator bool()
const {
273 SmallVector<QualType, 4> Ret;
274 auto EmplaceIfDifferent = [&Ret](QualType QT) {
278 Ret.emplace_back(QT);
279 else if (Ret.back() != QT)
280 Ret.emplace_back(QT);
286 EmplaceIfDifferent(
UDConvCtor.ConstructorParameterType);
287 EmplaceIfDifferent(
UDConvCtor.UserDefinedType);
290 EmplaceIfDifferent(
UDConvOp.UserDefinedType);
291 EmplaceIfDifferent(
UDConvOp.ConversionOperatorResultType);
346 return UDConvOp.ConversionOperatorResultType;
350 llvm_unreachable(
"Invalid UDConv kind.");
362 llvm_unreachable(
"Invalid UDConv kind.");
372 return UDConvCtor.Fun->getParamDecl(0)->getSourceRange();
376 if (
const FunctionTypeLoc FTL =
UDConvOp.Fun->getFunctionTypeLoc())
377 if (
const TypeLoc RetLoc = FTL.getReturnLoc())
378 return RetLoc.getSourceRange();
383 llvm_unreachable(
"Invalid UDConv kind.");
420 const MixFlags CanonicalAndWorkaround =
422 if ((
Flags & CanonicalAndWorkaround) == CanonicalAndWorkaround) {
447 bool ShouldHaveImplicitConvFlag =
false;
449 ShouldHaveImplicitConvFlag =
true;
454 ShouldHaveImplicitConvFlag =
true;
456 if (ShouldHaveImplicitConvFlag)
478 Flags |= EnableFlags;
486 const QualType NewCommonType = Func(
CommonType);
513 return Data.Conversion;
516 return Data.ConversionRTL;
521static_assert(std::is_trivially_copyable_v<Mix> &&
522 std::is_trivially_move_constructible_v<Mix> &&
523 std::is_trivially_move_assignable_v<Mix>,
524 "Keep frequently used data simple!");
539 assert(!
Mixes.empty());
540 return Mixes.front().First;
549 assert(!
Mixes.empty());
550 return Mixes.back().Second;
570 const LValueReferenceType *LRef, QualType Ty,
571 const ASTContext &Ctx,
bool IsRefRHS,
576 QualType RType,
const ASTContext &Ctx,
580 return isa<AttributedType, DecayedType, ParenType>(T);
585struct NonCVRQualifiersResult {
590 bool HasMixabilityBreakingQualifiers;
602static NonCVRQualifiersResult
604 LLVM_DEBUG(llvm::dbgs() <<
">>> getNonCVRQualifiers for LType:\n";
605 LType.dump(llvm::dbgs(), Ctx); llvm::dbgs() <<
"\nand RType:\n";
606 RType.dump(llvm::dbgs(), Ctx); llvm::dbgs() <<
'\n';);
607 Qualifiers LQual = LType.getLocalQualifiers(),
608 RQual = RType.getLocalQualifiers();
611 LQual.removeCVRQualifiers();
612 RQual.removeCVRQualifiers();
614 NonCVRQualifiersResult Ret;
615 Ret.CommonQualifiers = Qualifiers::removeCommonQualifiers(LQual, RQual);
617 LLVM_DEBUG(llvm::dbgs() <<
"--- hasNonCVRMixabilityBreakingQualifiers. "
618 "Removed common qualifiers: ";
619 Ret.CommonQualifiers.print(llvm::dbgs(), Ctx.getPrintingPolicy());
620 llvm::dbgs() <<
"\n\tremaining on LType: ";
621 LQual.print(llvm::dbgs(), Ctx.getPrintingPolicy());
622 llvm::dbgs() <<
"\n\tremaining on RType: ";
623 RQual.print(llvm::dbgs(), Ctx.getPrintingPolicy());
624 llvm::dbgs() <<
'\n';);
628 Ret.HasMixabilityBreakingQualifiers =
629 LQual.hasQualifiers() || RQual.hasQualifiers();
645 const ASTContext &Ctx,
647 LLVM_DEBUG(llvm::dbgs() <<
">>> calculateMixability for LType:\n";
648 LType.dump(llvm::dbgs(), Ctx); llvm::dbgs() <<
"\nand RType:\n";
649 RType.dump(llvm::dbgs(), Ctx); llvm::dbgs() <<
'\n';);
650 if (LType == RType) {
651 LLVM_DEBUG(llvm::dbgs() <<
"<<< calculateMixability. Trivial equality.\n");
659 LLVM_DEBUG(llvm::dbgs()
660 <<
"--- calculateMixability. LHS is useless sugar.\n");
662 RType, Ctx, ImplicitMode);
665 LLVM_DEBUG(llvm::dbgs()
666 <<
"--- calculateMixability. RHS is useless sugar.\n");
668 Check, LType, RType.getSingleStepDesugaredType(Ctx), Ctx, ImplicitMode);
671 const auto *LLRef = LType->getAs<LValueReferenceType>();
672 const auto *RLRef = RType->getAs<LValueReferenceType>();
673 if (LLRef && RLRef) {
674 LLVM_DEBUG(llvm::dbgs() <<
"--- calculateMixability. LHS and RHS are &.\n");
677 RLRef->getPointeeType(), Ctx, ImplicitMode)
679 [&Ctx](QualType QT) {
return Ctx.getLValueReferenceType(QT); });
685 LLVM_DEBUG(llvm::dbgs() <<
"--- calculateMixability. LHS is &.\n");
691 LLVM_DEBUG(llvm::dbgs() <<
"--- calculateMixability. RHS is &.\n");
697 if (LType->getAs<TypedefType>()) {
698 LLVM_DEBUG(llvm::dbgs() <<
"--- calculateMixability. LHS is typedef.\n");
700 RType, Ctx, ImplicitMode) |
703 if (RType->getAs<TypedefType>()) {
704 LLVM_DEBUG(llvm::dbgs() <<
"--- calculateMixability. RHS is typedef.\n");
706 RType.getSingleStepDesugaredType(Ctx), Ctx,
715 bool CompareUnqualifiedTypes =
false;
716 if (LType.getLocalCVRQualifiers() != RType.getLocalCVRQualifiers()) {
717 LLVM_DEBUG(
if (LType.getLocalCVRQualifiers()) {
718 llvm::dbgs() <<
"--- calculateMixability. LHS has CVR-Qualifiers: ";
719 Qualifiers::fromCVRMask(LType.getLocalCVRQualifiers())
720 .print(llvm::dbgs(), Ctx.getPrintingPolicy());
721 llvm::dbgs() <<
'\n';
723 LLVM_DEBUG(
if (RType.getLocalCVRQualifiers()) {
724 llvm::dbgs() <<
"--- calculateMixability. RHS has CVR-Qualifiers: ";
725 Qualifiers::fromCVRMask(RType.getLocalCVRQualifiers())
726 .print(llvm::dbgs(), Ctx.getPrintingPolicy());
727 llvm::dbgs() <<
'\n';
731 LLVM_DEBUG(llvm::dbgs()
732 <<
"<<< calculateMixability. QualifiersMix turned off - not "
737 CompareUnqualifiedTypes =
true;
740 bool OriginallySameQualifiers =
false;
741 if (LType.getLocalCVRQualifiers() == RType.getLocalCVRQualifiers() &&
742 LType.getLocalCVRQualifiers() != 0) {
743 LLVM_DEBUG(
if (LType.getLocalCVRQualifiers()) {
745 <<
"--- calculateMixability. LHS and RHS have same CVR-Qualifiers: ";
746 Qualifiers::fromCVRMask(LType.getLocalCVRQualifiers())
747 .print(llvm::dbgs(), Ctx.getPrintingPolicy());
748 llvm::dbgs() <<
'\n';
751 CompareUnqualifiedTypes =
true;
752 OriginallySameQualifiers =
true;
755 if (CompareUnqualifiedTypes) {
756 NonCVRQualifiersResult AdditionalQuals =
758 if (AdditionalQuals.HasMixabilityBreakingQualifiers) {
759 LLVM_DEBUG(llvm::dbgs() <<
"<<< calculateMixability. Additional "
760 "non-equal incompatible qualifiers.\n");
764 const MixData UnqualifiedMixability =
766 RType.getLocalUnqualifiedType(), Ctx, ImplicitMode)
770 return Ctx.getQualifiedType(QT, AdditionalQuals.CommonQualifiers);
773 if (!OriginallySameQualifiers)
780 [&Ctx, LType](QualType QT) {
781 return Ctx.getQualifiedType(QT, LType.getLocalQualifiers());
788 bool RecursiveReturnDiscardingCanonicalType =
false;
790 if (LType->isPointerType() && RType->isPointerType()) {
794 LLVM_DEBUG(llvm::dbgs()
795 <<
"--- calculateMixability. LHS and RHS are Ptrs.\n");
798 RType->getPointeeType(), Ctx,
801 [&Ctx](QualType QT) {
return Ctx.getPointerType(QT); });
804 RecursiveReturnDiscardingCanonicalType =
true;
808 LLVM_DEBUG(llvm::dbgs()
809 <<
"<<< calculateMixability. Pointees are mixable.\n");
815 LLVM_DEBUG(llvm::dbgs() <<
"--- calculateMixability. Start implicit...\n");
820 <<
"--- calculateMixability. Implicit Left -> Right found.\n";);
829 LLVM_DEBUG(llvm::dbgs() <<
"<<< calculateMixability. Implicit "
830 "conversion, one-way, standard-only.\n");
840 <<
"--- calculateMixability. Implicit Right -> Left found.\n";);
845 <<
"<<< calculateMixability. Implicit conversion, bidirectional.\n");
851 if (RecursiveReturnDiscardingCanonicalType)
852 LLVM_DEBUG(llvm::dbgs() <<
"--- calculateMixability. Before CanonicalType, "
853 "Discard was enabled.\n");
857 if (LType->getAs<FunctionProtoType>() || RType->getAs<FunctionProtoType>()) {
861 LLVM_DEBUG(llvm::dbgs()
862 <<
"--- calculateMixability. Discarding potential canonical "
863 "equivalence on FunctionProtoTypes.\n");
864 RecursiveReturnDiscardingCanonicalType =
true;
871 const QualType LCanonical = LType.getCanonicalType();
872 if (LCanonical == RType.getCanonicalType()) {
873 LLVM_DEBUG(llvm::dbgs()
874 <<
"<<< calculateMixability. Same CanonicalType.\n");
878 if (RecursiveReturnDiscardingCanonicalType)
882 <<
"<<< calculateMixability. No match found.\n");
893 const LValueReferenceType *LRef, QualType Ty,
894 const ASTContext &Ctx,
bool IsRefRHS,
896 LLVM_DEBUG(llvm::dbgs() <<
">>> isLRefEquallyBindingToType for LRef:\n";
897 LRef->dump(llvm::dbgs(), Ctx); llvm::dbgs() <<
"\nand Type:\n";
898 Ty.dump(llvm::dbgs(), Ctx); llvm::dbgs() <<
'\n';);
900 QualType ReferredType = LRef->getPointeeType();
901 if (!ReferredType.isLocalConstQualified() &&
902 ReferredType->getAs<TypedefType>()) {
905 <<
"--- isLRefEquallyBindingToType. Non-const LRef to Typedef.\n");
906 ReferredType = ReferredType.getDesugaredType(Ctx);
907 if (!ReferredType.isLocalConstQualified()) {
908 LLVM_DEBUG(llvm::dbgs()
909 <<
"<<< isLRefEquallyBindingToType. Typedef is not const.\n");
913 LLVM_DEBUG(llvm::dbgs() <<
"--- isLRefEquallyBindingToType. Typedef is "
914 "const, considering as const LRef.\n");
915 }
else if (!ReferredType.isLocalConstQualified()) {
916 LLVM_DEBUG(llvm::dbgs()
917 <<
"<<< isLRefEquallyBindingToType. Not const LRef.\n");
921 assert(ReferredType.isLocalConstQualified() &&
922 "Reaching this point means we are sure LRef is effectively a const&.");
924 if (ReferredType == Ty) {
927 <<
"<<< isLRefEquallyBindingToType. Type of referred matches.\n");
931 QualType NonConstReferredType = ReferredType;
932 NonConstReferredType.removeLocalConst();
933 if (NonConstReferredType == Ty) {
934 LLVM_DEBUG(llvm::dbgs() <<
"<<< isLRefEquallyBindingToType. Type of "
935 "referred matches to non-const qualified.\n");
941 <<
"--- isLRefEquallyBindingToType. Checking mix for underlying type.\n");
949 const CXXRecordDecl *Base) {
950 return Derived && Base && Derived->isCompleteDefinition() &&
951 Base->isCompleteDefinition() && Derived->isDerivedFrom(Base);
954static std::optional<QualType>
956 QualType To,
const ASTContext &Ctx) {
957 LLVM_DEBUG(llvm::dbgs() <<
">>> approximateStdConv for LType:\n";
958 From.dump(llvm::dbgs(), Ctx); llvm::dbgs() <<
"\nand RType:\n";
959 To.dump(llvm::dbgs(), Ctx); llvm::dbgs() <<
'\n';);
966 QualType WorkType = From;
970 auto FastQualifiersToApply =
static_cast<unsigned>(
971 From.split().Quals.getAsOpaqueValue() & Qualifiers::FastMask);
984 const auto *FromBuiltin = WorkType->getAs<BuiltinType>();
985 const auto *ToBuiltin = To->getAs<BuiltinType>();
986 const bool FromNumeric = FromBuiltin && (FromBuiltin->isIntegerType() ||
987 FromBuiltin->isFloatingType());
988 const bool ToNumeric =
989 ToBuiltin && (ToBuiltin->isIntegerType() || ToBuiltin->isFloatingType());
990 if (FromNumeric && ToNumeric) {
995 LLVM_DEBUG(llvm::dbgs()
996 <<
"--- approximateStdConv. Conversion between numerics.\n");
997 WorkType = QualType{ToBuiltin, FastQualifiersToApply};
1000 const auto *FromEnum = WorkType->getAsCanonical<EnumType>();
1001 const auto *ToEnum = To->getAs<EnumType>();
1002 if (FromEnum && ToNumeric && FromEnum->isUnscopedEnumerationType()) {
1004 LLVM_DEBUG(llvm::dbgs()
1005 <<
"--- approximateStdConv. Unscoped enum to numeric.\n");
1006 WorkType = QualType{ToBuiltin, FastQualifiersToApply};
1007 }
else if (FromNumeric && ToEnum && ToEnum->isUnscopedEnumerationType()) {
1009 if (Ctx.getLangOpts().CPlusPlus) {
1010 LLVM_DEBUG(llvm::dbgs() <<
"<<< approximateStdConv. Numeric to unscoped "
1011 "enum, not possible in C++!\n");
1015 LLVM_DEBUG(llvm::dbgs()
1016 <<
"--- approximateStdConv. Numeric to unscoped enum.\n");
1017 WorkType = QualType{ToEnum, FastQualifiersToApply};
1021 const auto *FromPtr = WorkType->getAs<PointerType>();
1022 const auto *ToPtr = To->getAs<PointerType>();
1023 if (FromPtr && ToPtr) {
1024 if (ToPtr->isVoidPointerType()) {
1025 LLVM_DEBUG(llvm::dbgs() <<
"--- approximateStdConv. To void pointer.\n");
1026 WorkType = QualType{ToPtr, FastQualifiersToApply};
1029 const auto *FromRecordPtr = FromPtr->getPointeeCXXRecordDecl();
1030 const auto *ToRecordPtr = ToPtr->getPointeeCXXRecordDecl();
1032 LLVM_DEBUG(llvm::dbgs() <<
"--- approximateStdConv. Derived* to Base*\n");
1033 WorkType = QualType{ToPtr, FastQualifiersToApply};
1039 const auto *FromRecord = WorkType->getAsCXXRecordDecl();
1040 const auto *ToRecord = To->getAsCXXRecordDecl();
1042 LLVM_DEBUG(llvm::dbgs() <<
"--- approximateStdConv. Derived To Base.\n");
1043 WorkType = QualType{
1044 ToRecord->getASTContext().getCanonicalTagType(ToRecord)->getTypePtr(),
1045 FastQualifiersToApply};
1048 if (Ctx.getLangOpts().CPlusPlus17 && FromPtr && ToPtr) {
1051 const auto *FromFunctionPtr =
1052 FromPtr->getPointeeType()->getAs<FunctionProtoType>();
1053 const auto *ToFunctionPtr =
1054 ToPtr->getPointeeType()->getAs<FunctionProtoType>();
1055 if (FromFunctionPtr && ToFunctionPtr &&
1056 FromFunctionPtr->hasNoexceptExceptionSpec() &&
1057 !ToFunctionPtr->hasNoexceptExceptionSpec()) {
1058 LLVM_DEBUG(llvm::dbgs() <<
"--- approximateStdConv. noexcept function "
1059 "pointer to non-noexcept.\n");
1060 WorkType = QualType{ToPtr, FastQualifiersToApply};
1066 LLVM_DEBUG(llvm::dbgs()
1067 <<
"--- approximateStdConv. Trying qualifier adjustment...\n");
1072 LLVM_DEBUG(llvm::dbgs()
1073 <<
"<<< approximateStdConv. Qualifiers adjusted.\n");
1077 if (ASTContext::hasSameType(WorkType, To)) {
1078 LLVM_DEBUG(llvm::dbgs() <<
"<<< approximateStdConv. Reached 'To' type.\n");
1079 return {Ctx.getCommonSugaredType(WorkType, To)};
1082 LLVM_DEBUG(llvm::dbgs() <<
"<<< approximateStdConv. Did not reach 'To'.\n");
1091class UserDefinedConversionSelector {
1097 struct PreparedConversion {
1098 const CXXMethodDecl *ConversionFun;
1100 ConversionSequence Seq;
1102 PreparedConversion(
const CXXMethodDecl *CMD, MixFlags F,
1103 ConversionSequence S)
1104 : ConversionFun(CMD), Flags(F), Seq(S) {}
1107 UserDefinedConversionSelector(
const TheCheck &Check) : Check(Check) {}
1114 void addConversion(
const CXXMethodDecl *ConvFun, QualType FromType,
1119 Check, FromType, ToType, ConvFun->getASTContext(),
1122 if (!Mix.indicatesMixability())
1125 LLVM_DEBUG(llvm::dbgs() <<
"--- tryConversion. Found viable with flags: "
1127 FlaggedConversions.emplace_back(ConvFun, Mix.Flags, Mix.Conversion);
1132 std::optional<PreparedConversion> operator()()
const {
1133 if (FlaggedConversions.empty()) {
1134 LLVM_DEBUG(llvm::dbgs() <<
"--- selectUserDefinedConv. Empty.\n");
1137 if (FlaggedConversions.size() == 1) {
1138 LLVM_DEBUG(llvm::dbgs() <<
"--- selectUserDefinedConv. Single.\n");
1139 return FlaggedConversions.front();
1142 std::optional<PreparedConversion> BestConversion;
1143 unsigned short HowManyGoodConversions = 0;
1144 for (
const auto &Prepared : FlaggedConversions) {
1145 LLVM_DEBUG(llvm::dbgs() <<
"--- selectUserDefinedConv. Candidate flags: "
1147 if (!BestConversion) {
1148 BestConversion = Prepared;
1149 ++HowManyGoodConversions;
1153 const bool BestConversionHasImplicit =
1155 const bool ThisConversionHasImplicit =
1157 if (!BestConversionHasImplicit && ThisConversionHasImplicit)
1161 if (BestConversionHasImplicit && !ThisConversionHasImplicit) {
1166 BestConversion = Prepared;
1167 HowManyGoodConversions = 1;
1171 if (BestConversionHasImplicit == ThisConversionHasImplicit)
1174 ++HowManyGoodConversions;
1177 if (HowManyGoodConversions == 1) {
1178 LLVM_DEBUG(llvm::dbgs()
1179 <<
"--- selectUserDefinedConv. Unique result. Flags: "
1181 return BestConversion;
1184 LLVM_DEBUG(llvm::dbgs()
1185 <<
"--- selectUserDefinedConv. No, or ambiguous.\n");
1190 llvm::SmallVector<PreparedConversion, 2> FlaggedConversions;
1196static std::optional<ConversionSequence>
1199 if (!RD || !RD->isCompleteDefinition())
1201 RD = RD->getDefinition();
1203 LLVM_DEBUG(llvm::dbgs() <<
">>> tryConversionOperators: " << RD->getName()
1205 ToType.dump(llvm::dbgs(), RD->getASTContext());
1206 llvm::dbgs() <<
'\n';);
1208 UserDefinedConversionSelector ConversionSet{Check};
1210 for (
const NamedDecl *Method : RD->getVisibleConversionFunctions()) {
1211 const auto *Con = dyn_cast<CXXConversionDecl>(Method);
1212 if (!Con || Con->isExplicit())
1214 LLVM_DEBUG(llvm::dbgs() <<
"--- tryConversionOperators. Trying:\n";
1215 Con->dump(llvm::dbgs()); llvm::dbgs() <<
'\n';);
1219 ConversionSet.addConversion(Con, Con->getConversionType(), ToType);
1222 if (std::optional<UserDefinedConversionSelector::PreparedConversion>
1223 SelectedConversion = ConversionSet()) {
1224 const CanQualType RecordType = RD->getASTContext().getCanonicalTagType(RD);
1234 ConvOp.
Fun = cast<CXXConversionDecl>(SelectedConversion->ConversionFun);
1239 LLVM_DEBUG(llvm::dbgs() <<
"<<< tryConversionOperators. Found result.\n");
1243 LLVM_DEBUG(llvm::dbgs() <<
"<<< tryConversionOperators. No conversion.\n");
1247static std::optional<ConversionSequence>
1249 const CXXRecordDecl *RD) {
1250 if (!RD || !RD->isCompleteDefinition())
1252 RD = RD->getDefinition();
1254 LLVM_DEBUG(llvm::dbgs() <<
">>> tryConveringConstructors: " << RD->getName()
1256 FromType.dump(llvm::dbgs(), RD->getASTContext());
1257 llvm::dbgs() <<
'\n';);
1259 UserDefinedConversionSelector ConversionSet{Check};
1261 for (
const CXXConstructorDecl *Con : RD->ctors()) {
1262 if (Con->isCopyOrMoveConstructor() ||
1263 !Con->isConvertingConstructor(
false))
1265 LLVM_DEBUG(llvm::dbgs() <<
"--- tryConvertingConstructors. Trying:\n";
1266 Con->dump(llvm::dbgs()); llvm::dbgs() <<
'\n';);
1270 ConversionSet.addConversion(Con, FromType, Con->getParamDecl(0)->getType());
1273 if (std::optional<UserDefinedConversionSelector::PreparedConversion>
1274 SelectedConversion = ConversionSet()) {
1275 const CanQualType RecordType = RD->getASTContext().getCanonicalTagType(RD);
1281 Ctor.
Fun = cast<CXXConstructorDecl>(SelectedConversion->ConversionFun);
1286 LLVM_DEBUG(llvm::dbgs()
1287 <<
"<<< tryConvertingConstructors. Found result.\n");
1291 LLVM_DEBUG(llvm::dbgs() <<
"<<< tryConvertingConstructors. No conversion.\n");
1302 QualType RType,
const ASTContext &Ctx,
1304 LLVM_DEBUG(llvm::dbgs() <<
">>> approximateImplicitConversion for LType:\n";
1305 LType.dump(llvm::dbgs(), Ctx); llvm::dbgs() <<
"\nand RType:\n";
1306 RType.dump(llvm::dbgs(), Ctx);
1307 llvm::dbgs() <<
"\nimplicit mode: ";
switch (ImplicitMode) {
1309 llvm::dbgs() <<
"None";
1312 llvm::dbgs() <<
"All";
1315 llvm::dbgs() <<
"OneWay, Single, STD Only";
1317 } llvm::dbgs() <<
'\n';);
1326 QualType WorkType = LType;
1328 std::optional<QualType> AfterFirstStdConv =
1330 if (AfterFirstStdConv) {
1331 LLVM_DEBUG(llvm::dbgs() <<
"--- approximateImplicitConversion. Standard "
1332 "Pre-Conversion found!\n");
1344 if (Ctx.getLangOpts().CPlusPlus) {
1345 bool FoundConversionOperator =
false, FoundConvertingCtor =
false;
1347 if (
const auto *LRD = WorkType->getAsCXXRecordDecl()) {
1348 std::optional<ConversionSequence> ConversionOperatorResult =
1350 if (ConversionOperatorResult) {
1351 LLVM_DEBUG(llvm::dbgs() <<
"--- approximateImplicitConversion. Found "
1352 "conversion operator.\n");
1353 ImplicitSeq.
update(*ConversionOperatorResult);
1355 FoundConversionOperator =
true;
1359 if (
const auto *RRD = RType->getAsCXXRecordDecl()) {
1363 std::optional<ConversionSequence> ConvCtorResult =
1365 if (ConvCtorResult) {
1366 LLVM_DEBUG(llvm::dbgs() <<
"--- approximateImplicitConversion. Found "
1367 "converting constructor.\n");
1368 ImplicitSeq.
update(*ConvCtorResult);
1370 FoundConvertingCtor =
true;
1374 if (FoundConversionOperator && FoundConvertingCtor) {
1376 LLVM_DEBUG(llvm::dbgs()
1377 <<
"<<< approximateImplicitConversion. Found both "
1378 "user-defined conversion kinds in the same sequence!\n");
1387 <<
"--- approximateImplicitConversion. Try to find post-conversion.\n");
1389 Check, WorkType, RType, Ctx,
1392 LLVM_DEBUG(llvm::dbgs() <<
"--- approximateImplicitConversion. Standard "
1393 "Post-Conversion found!\n");
1404 LLVM_DEBUG(llvm::dbgs()
1405 <<
"<<< approximateImplicitConversion. Found a conversion.\n");
1410 llvm::dbgs() <<
"<<< approximateImplicitConversion. No match found.\n");
1415 const TheCheck &Check,
const FunctionDecl *FD, std::size_t StartIndex,
1417 const std::size_t NumParams = FD->getNumParams();
1418 assert(StartIndex < NumParams &&
"out of bounds for start");
1419 const ASTContext &Ctx = FD->getASTContext();
1425 for (std::size_t I = StartIndex + 1; I < NumParams; ++I) {
1426 const ParmVarDecl *Ith = FD->getParamDecl(I);
1427 const StringRef ParamName = Ith->getName();
1428 LLVM_DEBUG(llvm::dbgs()
1429 <<
"Check param #" << I <<
" '" << ParamName <<
"'...\n");
1431 LLVM_DEBUG(llvm::dbgs() <<
"Param #" << I <<
" is ignored. Break!\n");
1435 const StringRef PrevParamName = FD->getParamDecl(I - 1)->getName();
1436 if (!ParamName.empty() && !PrevParamName.empty() &&
1440 LLVM_DEBUG(llvm::dbgs() <<
"Parameter '" << ParamName
1441 <<
"' follows a pattern with previous parameter '"
1442 << PrevParamName <<
"'. Break!\n");
1449 for (std::size_t J = StartIndex; J < I; ++J) {
1450 const ParmVarDecl *Jth = FD->getParamDecl(J);
1451 LLVM_DEBUG(llvm::dbgs()
1452 <<
"Check mix of #" << J <<
" against #" << I <<
"...\n");
1454 if (isSimilarlyUsedParameter(UsageBasedSuppressor, Ith, Jth)) {
1457 LLVM_DEBUG(llvm::dbgs() <<
"Parameters #" << I <<
" and #" << J
1458 <<
" deemed related, ignoring...\n");
1472 LLVM_DEBUG(llvm::dbgs() <<
"Mix flags (raw) : "
1475 LLVM_DEBUG(llvm::dbgs() <<
"Mix flags (after sanitize): "
1478 assert(M.
flagsValid() &&
"All flags decayed!");
1481 MixesOfIth.emplace_back(std::move(M));
1484 if (MixesOfIth.empty()) {
1487 LLVM_DEBUG(llvm::dbgs()
1489 <<
" does not mix with any in the current range. Break!\n");
1493 Ret.
Mixes.insert(Ret.
Mixes.end(), MixesOfIth.begin(), MixesOfIth.end());
1504AST_MATCHER_FUNCTION(ast_matchers::internal::Matcher<Stmt>, paramRefExpr) {
1505 return expr(ignoringParenImpCasts(ignoringElidableConstructorCall(
1506 declRefExpr(to(parmVarDecl().bind(
"param"))))));
1515 LLVM_DEBUG(llvm::dbgs() <<
"Checking if '" << Node->getName()
1516 <<
"' is ignored.\n");
1518 if (!Node->getIdentifier())
1521 const StringRef NodeName = Node->getName();
1523 LLVM_DEBUG(llvm::dbgs() <<
"\tName ignored.\n");
1527 const StringRef NodeTypeName = [Node] {
1528 const ASTContext &Ctx = Node->getASTContext();
1529 const SourceManager &SM = Ctx.getSourceManager();
1530 SourceLocation B = Node->getTypeSpecStartLoc();
1531 SourceLocation E = Node->getTypeSpecEndLoc();
1532 const LangOptions LO;
1534 LLVM_DEBUG(llvm::dbgs() <<
"\tType name code is '"
1535 << Lexer::getSourceText(
1536 CharSourceRange::getTokenRange(B, E), SM, LO)
1538 if (B.isMacroID()) {
1539 LLVM_DEBUG(llvm::dbgs() <<
"\t\tBeginning is macro.\n");
1540 B = SM.getTopMacroCallerLoc(B);
1542 if (E.isMacroID()) {
1543 LLVM_DEBUG(llvm::dbgs() <<
"\t\tEnding is macro.\n");
1544 E = Lexer::getLocForEndOfToken(SM.getTopMacroCallerLoc(E), 0, SM, LO);
1546 LLVM_DEBUG(llvm::dbgs() <<
"\tType name code is '"
1547 << Lexer::getSourceText(
1548 CharSourceRange::getTokenRange(B, E), SM, LO)
1551 return Lexer::getSourceText(CharSourceRange::getTokenRange(B, E), SM, LO);
1554 LLVM_DEBUG(llvm::dbgs() <<
"\tType name is '" << NodeTypeName <<
"'\n");
1555 if (!NodeTypeName.empty()) {
1557 [NodeTypeName](StringRef E) {
1558 return !E.empty() && NodeTypeName.ends_with(E);
1560 LLVM_DEBUG(llvm::dbgs() <<
"\tType suffix ignored.\n");
1574template <
typename T, std::
size_t N = SmallDataStructureSize>
1576 llvm::DenseMap<const ParmVarDecl *, llvm::SmallSet<T, N>>;
1578template <
typename T, std::
size_t N = SmallDataStructureSize>
1580 llvm::DenseMap<const ParmVarDecl *, llvm::SmallPtrSet<T, N>>;
1584template <
typename MapTy,
typename ElemTy>
1587 auto E1Iterator = Map.find(E1);
1588 auto E2Iterator = Map.find(E2);
1589 if (E1Iterator == Map.end() || E2Iterator == Map.end())
1592 return llvm::any_of(E1Iterator->second, [&E2Iterator](
const auto &E1SetElem) {
1593 return E2Iterator->second.contains(E1SetElem);
1602 using Base = RecursiveASTVisitor<AppearsInSameExpr>;
1604 const FunctionDecl *FD;
1605 const Expr *CurrentExprOnlyTreeRoot =
nullptr;
1606 llvm::DenseMap<
const ParmVarDecl *,
1607 llvm::SmallPtrSet<const Expr *, SmallDataStructureSize>>
1608 ParentExprsForParamRefs;
1613 TraverseFunctionDecl(
const_cast<FunctionDecl *
>(FD));
1616 bool operator()(
const ParmVarDecl *Param1,
const ParmVarDecl *Param2)
const {
1622 CurrentExprOnlyTreeRoot =
nullptr;
1623 return Base::TraverseDecl(D);
1627 if (
auto *E = dyn_cast_or_null<Expr>(S)) {
1628 bool RootSetInCurrentStackFrame =
false;
1629 if (!CurrentExprOnlyTreeRoot) {
1630 CurrentExprOnlyTreeRoot = E;
1631 RootSetInCurrentStackFrame =
true;
1634 const bool Ret = Base::TraverseStmt(S);
1636 if (RootSetInCurrentStackFrame)
1637 CurrentExprOnlyTreeRoot =
nullptr;
1643 CurrentExprOnlyTreeRoot =
nullptr;
1644 return Base::TraverseStmt(S);
1648 if (!CurrentExprOnlyTreeRoot)
1651 if (
auto *PVD = dyn_cast<ParmVarDecl>(DRE->getDecl()))
1652 if (llvm::find(FD->parameters(), PVD))
1653 ParentExprsForParamRefs[PVD].insert(CurrentExprOnlyTreeRoot);
1668 auto ParamsAsArgsInFnCalls =
1669 match(functionDecl(forEachDescendant(
1670 callExpr(forEachArgumentWithParam(
1671 paramRefExpr(), parmVarDecl().bind(
"passed-to")))
1672 .bind(
"call-expr"))),
1673 *FD, FD->getASTContext());
1674 for (
const auto &Match : ParamsAsArgsInFnCalls) {
1675 const auto *PassedParamOfThisFn = Match.getNodeAs<ParmVarDecl>(
"param");
1676 const auto *CE = Match.getNodeAs<CallExpr>(
"call-expr");
1677 const auto *PassedToParam = Match.getNodeAs<ParmVarDecl>(
"passed-to");
1678 assert(PassedParamOfThisFn && CE && PassedToParam);
1680 const FunctionDecl *CalledFn = CE->getDirectCallee();
1684 std::optional<unsigned> TargetIdx;
1685 const unsigned NumFnParams = CalledFn->getNumParams();
1686 for (
unsigned Idx = 0; Idx < NumFnParams; ++Idx)
1687 if (CalledFn->getParamDecl(Idx) == PassedToParam)
1688 TargetIdx.emplace(Idx);
1690 assert(TargetIdx &&
"Matched, but didn't find index?");
1691 TargetParams[PassedParamOfThisFn].insert(
1692 {CalledFn->getCanonicalDecl(), *TargetIdx});
1696 bool operator()(
const ParmVarDecl *Param1,
const ParmVarDecl *Param2)
const {
1708 auto MembersCalledOnParams = match(
1709 functionDecl(forEachDescendant(
1710 memberExpr(hasObjectExpression(paramRefExpr())).bind(
"mem-expr"))),
1711 *FD, FD->getASTContext());
1713 for (
const auto &Match : MembersCalledOnParams) {
1714 const auto *AccessedParam = Match.getNodeAs<ParmVarDecl>(
"param");
1715 const auto *ME = Match.getNodeAs<MemberExpr>(
"mem-expr");
1716 assert(AccessedParam && ME);
1717 AccessedMembers[AccessedParam].insert(
1718 ME->getMemberDecl()->getCanonicalDecl());
1722 bool operator()(
const ParmVarDecl *Param1,
const ParmVarDecl *Param2)
const {
1730 llvm::SmallVector<const ParmVarDecl *, SmallDataStructureSize> ReturnedParams;
1735 auto ParamReturns = match(functionDecl(forEachDescendant(
1736 returnStmt(hasReturnValue(paramRefExpr())))),
1737 *FD, FD->getASTContext());
1738 for (
const auto &Match : ParamReturns) {
1739 const auto *ReturnedParam = Match.getNodeAs<ParmVarDecl>(
"param");
1740 assert(ReturnedParam);
1742 if (find(FD->parameters(), ReturnedParam) == FD->param_end())
1748 ReturnedParams.emplace_back(ReturnedParam);
1752 bool operator()(
const ParmVarDecl *Param1,
const ParmVarDecl *Param2)
const {
1753 return llvm::is_contained(ReturnedParams, Param1) &&
1754 llvm::is_contained(ReturnedParams, Param2);
1776 PassToFun.setup(FD);
1777 SameMember.setup(FD);
1783 bool operator()(
const ParmVarDecl *Param1,
const ParmVarDecl *Param2)
const {
1787 LLVM_DEBUG(llvm::dbgs()
1788 <<
"::: Matching similar usage / relatedness heuristic...\n");
1790 if (SameExpr(Param1, Param2)) {
1791 LLVM_DEBUG(llvm::dbgs() <<
"::: Used in the same expression.\n");
1795 if (PassToFun(Param1, Param2)) {
1796 LLVM_DEBUG(llvm::dbgs()
1797 <<
"::: Passed to same function in different calls.\n");
1801 if (SameMember(Param1, Param2)) {
1802 LLVM_DEBUG(llvm::dbgs()
1803 <<
"::: Same member field access or method called.\n");
1807 if (Returns(Param1, Param2)) {
1808 LLVM_DEBUG(llvm::dbgs() <<
"::: Both parameter returned.\n");
1812 LLVM_DEBUG(llvm::dbgs() <<
"::: None.\n");
1821 const ParmVarDecl *Param1,
const ParmVarDecl *Param2) {
1822 return Suppressor(Param1, Param2);
1826 while (Str.size() < ToLen)
1827 Str.emplace_back(
'\0');
1831 while (Str.size() < ToLen)
1832 Str.insert(Str.begin(),
'\0');
1837 assert(S1.size() >= N && S2.size() >= N);
1838 const StringRef S1Prefix = S1.take_front(S1.size() - N),
1839 S2Prefix = S2.take_front(S2.size() - N);
1840 return S1Prefix == S2Prefix && !S1Prefix.empty();
1845 assert(S1.size() >= N && S2.size() >= N);
1846 const StringRef S1Suffix = S1.take_back(S1.size() - N),
1847 S2Suffix = S2.take_back(S2.size() - N);
1848 return S1Suffix == S2Suffix && !S1Suffix.empty();
1854 StringRef Str1, StringRef Str2) {
1859 const std::size_t BiggerLength = std::max(Str1.size(), Str2.size());
1861 if (BiggerLength <= Threshold)
1868 SmallString<32> S1PadE{Str1}, S2PadE{Str2};
1873 Threshold, StringRef{S1PadE.begin(), BiggerLength},
1874 StringRef{S2PadE.begin(), BiggerLength}))
1877 SmallString<32> S1PadB{Str1}, S2PadB{Str2};
1882 Threshold, StringRef{S1PadB.begin(), BiggerLength},
1883 StringRef{S2PadB.begin(), BiggerLength}))
1894AST_MATCHER_P(FunctionDecl, parameterCountGE,
unsigned, N) {
1895 return Node.getNumParams() >= N;
1899AST_MATCHER(FunctionDecl, isOverloadedUnaryOrBinaryOperator) {
1900 switch (Node.getOverloadedOperator()) {
1905 case OO_Array_Delete:
1906 case OO_Conditional:
1911 return Node.getNumParams() <= 2;
1926static SmallString<64>
getName(
const NamedDecl *ND) {
1927 SmallString<64> Name;
1928 llvm::raw_svector_ostream OS{Name};
1929 ND->getNameForDiagnostic(OS, ND->getASTContext().getPrintingPolicy(),
false);
1944 using namespace model;
1945 return static_cast<bool>(
1947 (MixFlags::TypeAlias | MixFlags::ReferenceBind | MixFlags::Qualifiers));
1960struct FormattedConversionSequence {
1961 std::string DiagnosticText;
1966 bool Trivial =
true;
1968 FormattedConversionSequence(
const PrintingPolicy &
PP,
1969 StringRef StartTypeAsDiagnosed,
1970 const model::ConversionSequence &Conv,
1971 StringRef DestinationTypeAsDiagnosed) {
1972 llvm::raw_string_ostream OS{DiagnosticText};
1975 OS <<
'\'' << StartTypeAsDiagnosed <<
'\'';
1976 std::string LastAddedType = StartTypeAsDiagnosed.str();
1977 std::size_t NumElementsAdded = 1;
1981 const std::string SeqBeginTypeStr = Conv.Begin.getAsString(
PP);
1982 std::string SeqEndTypeStr = Conv.End.getAsString(
PP);
1983 if (StartTypeAsDiagnosed != SeqBeginTypeStr) {
1984 OS <<
" (as '" << SeqBeginTypeStr <<
"')";
1985 LastAddedType = SeqBeginTypeStr;
1989 auto AddType = [&](StringRef ToAdd) {
1990 if (LastAddedType != ToAdd && ToAdd != SeqEndTypeStr) {
1991 OS <<
" -> '" << ToAdd <<
"'";
1992 LastAddedType = ToAdd.str();
1996 for (
const QualType InvolvedType : Conv.getInvolvedTypesInSequence())
1998 AddType(InvolvedType.getAsString(
PP));
2000 if (LastAddedType != DestinationTypeAsDiagnosed) {
2001 OS <<
" -> '" << DestinationTypeAsDiagnosed <<
"'";
2002 LastAddedType = DestinationTypeAsDiagnosed.str();
2009 if (DestinationTypeAsDiagnosed != SeqEndTypeStr) {
2010 OS <<
" (as '" << SeqEndTypeStr <<
"')";
2011 LastAddedType = SeqEndTypeStr;
2015 if (Trivial && NumElementsAdded > 2)
2025template <
typename E, std::
size_t N>
class InsertOnce {
2026 llvm::SmallSet<E, N> CalledWith;
2029 bool operator()(E El) {
return CalledWith.insert(std::move(El)).second; }
2031 bool calledWith(
const E &El)
const {
return CalledWith.contains(El); }
2034struct SwappedEqualQualTypePair {
2035 QualType LHSType, RHSType;
2037 bool operator==(
const SwappedEqualQualTypePair &Other)
const {
2038 return (LHSType == Other.LHSType && RHSType == Other.RHSType) ||
2039 (LHSType == Other.RHSType && RHSType == Other.LHSType);
2042 bool operator<(
const SwappedEqualQualTypePair &Other)
const {
2043 return LHSType < Other.LHSType && RHSType < Other.RHSType;
2047struct TypeAliasDiagnosticTuple {
2048 QualType LHSType, RHSType, CommonType;
2050 bool operator==(
const TypeAliasDiagnosticTuple &Other)
const {
2051 return CommonType == Other.CommonType &&
2052 ((LHSType == Other.LHSType && RHSType == Other.RHSType) ||
2053 (LHSType == Other.RHSType && RHSType == Other.LHSType));
2056 bool operator<(
const TypeAliasDiagnosticTuple &Other)
const {
2057 return CommonType < Other.CommonType && LHSType < Other.LHSType &&
2058 RHSType < Other.RHSType;
2063class UniqueTypeAliasDiagnosticHelper
2064 :
public InsertOnce<TypeAliasDiagnosticTuple, 8> {
2065 using Base = InsertOnce<TypeAliasDiagnosticTuple, 8>;
2070 bool operator()(QualType LHSType, QualType RHSType, QualType CommonType) {
2071 if (CommonType.isNull() || CommonType == LHSType || CommonType == RHSType)
2072 return Base::operator()({LHSType, RHSType, {}});
2074 const TypeAliasDiagnosticTuple ThreeTuple{LHSType, RHSType, CommonType};
2075 if (!Base::operator()(ThreeTuple))
2078 const bool AlreadySaidLHSAndCommonIsSame =
2079 calledWith({LHSType, CommonType, {}});
2080 const bool AlreadySaidRHSAndCommonIsSame =
2081 calledWith({RHSType, CommonType, {}});
2082 if (AlreadySaidLHSAndCommonIsSame && AlreadySaidRHSAndCommonIsSame) {
2103 Options.get(
"IgnoredParameterTypeSuffixes",
2109 Options.get(
"SuppressParametersUsedTogether",
2112 Options.get(
"NamePrefixSuffixSilenceDissimilarityThreshold",
2118 Options.store(Opts,
"IgnoredParameterNames",
2120 Options.store(Opts,
"IgnoredParameterTypeSuffixes",
2124 Options.store(Opts,
"SuppressParametersUsedTogether",
2126 Options.store(Opts,
"NamePrefixSuffixSilenceDissimilarityThreshold",
2131 const auto BaseConstraints = functionDecl(
2135 unless(isOverloadedUnaryOrBinaryOperator()));
2138 functionDecl(BaseConstraints,
2139 unless(ast_matchers::isTemplateInstantiation()))
2143 functionDecl(BaseConstraints, isExplicitTemplateSpecialization())
2149 const MatchFinder::MatchResult &Result) {
2150 using namespace model;
2153 const auto *FD = Result.Nodes.getNodeAs<FunctionDecl>(
"func");
2156 const PrintingPolicy &
PP = FD->getASTContext().getPrintingPolicy();
2157 const std::size_t NumParams = FD->getNumParams();
2158 std::size_t MixableRangeStartIndex = 0;
2165 LLVM_DEBUG(llvm::dbgs() <<
"Begin analysis of " <<
getName(FD) <<
" with "
2166 << NumParams <<
" parameters...\n");
2167 while (MixableRangeStartIndex < NumParams) {
2168 if (isIgnoredParameter(*
this, FD->getParamDecl(MixableRangeStartIndex))) {
2169 LLVM_DEBUG(llvm::dbgs()
2170 <<
"Parameter #" << MixableRangeStartIndex <<
" ignored.\n");
2171 ++MixableRangeStartIndex;
2175 MixableParameterRange R = modelMixingRange(
2176 *
this, FD, MixableRangeStartIndex, UsageBasedSuppressor);
2177 assert(R.NumParamsChecked > 0 &&
"Ensure forward progress!");
2178 MixableRangeStartIndex += R.NumParamsChecked;
2180 LLVM_DEBUG(llvm::dbgs() <<
"Ignoring range of " << R.NumParamsChecked
2181 <<
" lower than limit.\n");
2185 const bool NeedsAnyTypeNote =
2187 const bool HasAnyImplicits =
2189 const ParmVarDecl *First = R.getFirstParam(), *Last = R.getLastParam();
2190 const std::string FirstParamTypeAsWritten =
2191 First->getType().getAsString(
PP);
2195 if (HasAnyImplicits)
2196 DiagText =
"%0 adjacent parameters of %1 of convertible types are "
2197 "easily swapped by mistake";
2198 else if (NeedsAnyTypeNote)
2199 DiagText =
"%0 adjacent parameters of %1 of similar type are easily "
2200 "swapped by mistake";
2202 DiagText =
"%0 adjacent parameters of %1 of similar type ('%2') are "
2203 "easily swapped by mistake";
2205 auto Diag = diag(First->getOuterLocStart(), DiagText)
2206 <<
static_cast<unsigned>(R.NumParamsChecked) << FD;
2207 if (!NeedsAnyTypeNote)
2208 Diag << FirstParamTypeAsWritten;
2210 const CharSourceRange HighlightRange = CharSourceRange::getTokenRange(
2211 First->getBeginLoc(), Last->getEndLoc());
2212 Diag << HighlightRange;
2218 diag(First->getLocation(),
"the first parameter in the range is '%0'",
2219 DiagnosticIDs::Note)
2221 << CharSourceRange::getTokenRange(First->getLocation(),
2222 First->getLocation());
2223 diag(Last->getLocation(),
"the last parameter in the range is '%0'",
2224 DiagnosticIDs::Note)
2226 << CharSourceRange::getTokenRange(Last->getLocation(),
2227 Last->getLocation());
2231 UniqueTypeAliasDiagnosticHelper UniqueTypeAlias;
2232 InsertOnce<SwappedEqualQualTypePair, 8> UniqueBindPower;
2233 InsertOnce<SwappedEqualQualTypePair, 8> UniqueImplicitConversion;
2236 assert(M.
mixable() &&
"Sentinel or false mix in result.");
2243 const ParmVarDecl *LVar = M.
First;
2244 const ParmVarDecl *RVar = M.
Second;
2245 const QualType LType = LVar->getType();
2246 const QualType RType = RVar->getType();
2248 const std::string LTypeStr = LType.getAsString(
PP);
2249 const std::string RTypeStr = RType.getAsString(
PP);
2250 const std::string CommonTypeStr = CommonType.getAsString(
PP);
2252 if (hasFlag(M.
flags(), MixFlags::TypeAlias) &&
2253 UniqueTypeAlias(LType, RType, CommonType)) {
2255 bool ExplicitlyPrintCommonType =
false;
2256 if (LTypeStr == CommonTypeStr || RTypeStr == CommonTypeStr) {
2257 if (hasFlag(M.
flags(), MixFlags::Qualifiers))
2258 DiagText =
"after resolving type aliases, '%0' and '%1' share a "
2262 "after resolving type aliases, '%0' and '%1' are the same";
2263 }
else if (!CommonType.isNull()) {
2264 DiagText =
"after resolving type aliases, the common type of '%0' "
2266 ExplicitlyPrintCommonType =
true;
2270 diag(LVar->getOuterLocStart(), DiagText, DiagnosticIDs::Note)
2271 << LTypeStr << RTypeStr;
2272 if (ExplicitlyPrintCommonType)
2273 Diag << CommonTypeStr;
2276 if ((hasFlag(M.
flags(), MixFlags::ReferenceBind) ||
2277 hasFlag(M.
flags(), MixFlags::Qualifiers)) &&
2278 UniqueBindPower({LType, RType})) {
2279 const StringRef DiagText =
2280 "'%0' and '%1' parameters accept and bind the "
2281 "same kind of values";
2282 diag(RVar->getOuterLocStart(), DiagText, DiagnosticIDs::Note)
2283 << LTypeStr << RTypeStr;
2287 UniqueImplicitConversion({LType, RType})) {
2292 const FormattedConversionSequence LTRFmt{
PP, LTypeStr, LTR, RTypeStr};
2293 const FormattedConversionSequence RTLFmt{
PP, RTypeStr, RTL, LTypeStr};
2295 StringRef DiagText =
"'%0' and '%1' may be implicitly converted";
2296 if (!LTRFmt.Trivial || !RTLFmt.Trivial)
2297 DiagText =
"'%0' and '%1' may be implicitly converted: %2, %3";
2301 diag(RVar->getOuterLocStart(), DiagText, DiagnosticIDs::Note)
2302 << LTypeStr << RTypeStr;
2304 if (!LTRFmt.Trivial || !RTLFmt.Trivial)
2305 Diag << LTRFmt.DiagnosticText << RTLFmt.DiagnosticText;
2308 const StringRef ConversionFunctionDiagText =
2309 "the implicit conversion involves the "
2310 "%select{|converting constructor|conversion operator}0 "
2313 diag(LFD->getLocation(), ConversionFunctionDiagText,
2314 DiagnosticIDs::Note)
2318 diag(RFD->getLocation(), ConversionFunctionDiagText,
2319 DiagnosticIDs::Note)
static constexpr bool DefaultModelImplicitConversions
The default value for the ModelImplicitConversions check option.
static constexpr std::size_t DefaultMinimumLength
The default value for the MinimumLength check option.
static constexpr bool DefaultQualifiersMix
The default value for the QualifiersMix check option.
static constexpr bool DefaultSuppressParametersUsedTogether
The default value for suppressing diagnostics about parameters that are used together.
static constexpr llvm::StringLiteral DefaultIgnoredParameterNames
The default value for ignored parameter names.
static constexpr llvm::StringLiteral DefaultIgnoredParameterTypeSuffixes
The default value for ignored parameter type suffixes.
static constexpr std::size_t DefaultNamePrefixSuffixSilenceDissimilarityTreshold
The default value for the NamePrefixSuffixSilenceDissimilarityThreshold check option.
Every ClangTidyCheck reports errors through a DiagnosticsEngine provided by this context.
Finds function definitions where parameters of convertible types follow each other directly,...
EasilySwappableParametersCheck(StringRef Name, ClangTidyContext *Context)
const std::size_t MinimumLength
The minimum length of an adjacent swappable parameter range required for a diagnostic.
const bool ModelImplicitConversions
Whether to model implicit conversions "in full" (conditions apply) during analysis and consider types...
const bool QualifiersMix
Whether to consider differently qualified versions of the same type mixable.
void storeOptions(ClangTidyOptions::OptionMap &Opts) override
const bool SuppressParametersUsedTogether
If enabled, diagnostics for parameters that are used together in a similar way are not emitted.
void check(const ast_matchers::MatchFinder::MatchResult &Result) override
void registerMatchers(ast_matchers::MatchFinder *Finder) override
const std::vector< StringRef > IgnoredParameterNames
The parameter names (as written in the source text) to be ignored.
const std::size_t NamePrefixSuffixSilenceDissimilarityThreshold
The number of characters two parameter names might be dissimilar at either end for the report about t...
const std::vector< StringRef > IgnoredParameterTypeSuffixes
The parameter typename suffixes (as written in the source code) to be ignored.
Helper class that is used to detect if two parameters of the same function are used in a similar fash...
SimilarlyUsedParameterPairSuppressor(const FunctionDecl *FD, bool Enable)
bool operator()(const ParmVarDecl *Param1, const ParmVarDecl *Param2) const
Returns whether the specified two parameters are deemed similarly used or related by the heuristics.
bool operator==(const Inclusion &LHS, const Inclusion &RHS)
bool operator<(const Ref &L, const Ref &R)
static void padStringAtBegin(SmallVectorImpl< char > &Str, std::size_t ToLen)
static bool prefixSuffixCoverUnderThreshold(std::size_t Threshold, StringRef Str1, StringRef Str2)
Returns whether the two strings are prefixes or suffixes of each other with at most Threshold charact...
static bool isCommonSuffixWithoutSomeCharacters(std::size_t N, StringRef S1, StringRef S2)
static bool isCommonPrefixWithoutSomeCharacters(std::size_t N, StringRef S1, StringRef S2)
static void padStringAtEnd(SmallVectorImpl< char > &Str, std::size_t ToLen)
static bool isSimilarlyUsedParameter(const SimilarlyUsedParameterPairSuppressor &Suppressor, const ParmVarDecl *Param1, const ParmVarDecl *Param2)
static bool isIgnoredParameter(const TheCheck &Check, const ParmVarDecl *Node)
Returns whether the parameter's name or the parameter's type's name is configured by the user to be i...
static std::optional< QualType > approximateStandardConversionSequence(const TheCheck &Check, QualType From, QualType To, const ASTContext &Ctx)
static MixData approximateImplicitConversion(const TheCheck &Check, QualType LType, QualType RType, const ASTContext &Ctx, ImplicitConversionModellingMode ImplicitMode)
Returns whether an expression of LType can be used in an RType context, as per the implicit conversio...
static MixableParameterRange modelMixingRange(const TheCheck &Check, const FunctionDecl *FD, std::size_t StartIndex, const filter::SimilarlyUsedParameterPairSuppressor &UsageBasedSuppressor)
static bool isUselessSugar(const Type *T)
static bool isDerivedToBase(const CXXRecordDecl *Derived, const CXXRecordDecl *Base)
static std::optional< ConversionSequence > tryConvertingConstructors(const TheCheck &Check, QualType FromType, const CXXRecordDecl *RD)
LLVM_ENABLE_BITMASK_ENUMS_IN_NAMESPACE()
MixFlags
The language features involved in allowing the mix between two parameters.
@ ImplicitConversion
The mixing of the parameters is possible through implicit conversions between the types.
@ TypeAlias
The path from one type to the other involves desugaring type aliases.
@ Invalid
Sentinel bit pattern. DO NOT USE!
@ WorkaroundDisableCanonicalEquivalence
Certain constructs (such as pointers to noexcept/non-noexcept functions) have the same CanonicalType,...
@ Qualifiers
The mix involves change in the qualifiers.
@ None
Mix between the two parameters is not possible.
@ Trivial
The two mix trivially, and are the exact same type.
@ Canonical
The two mix because the types refer to the same CanonicalType, but we do not elaborate as to how.
@ ReferenceBind
The mix involves the binding power of "const &".
ImplicitConversionModellingMode
Helper enum for the recursive calls in the modelling that toggle what kinds of implicit conversions a...
@ None
No implicit conversions are modelled.
@ OneWaySingleStandardOnly
@ All
Only model a unidirectional implicit conversion and within it only one standard conversion sequence.
static std::string formatMixFlags(MixFlags F)
Formats the MixFlags enum into a useful, user-readable representation.
static MixData calculateMixability(const TheCheck &Check, QualType LType, QualType RType, const ASTContext &Ctx, ImplicitConversionModellingMode ImplicitMode)
Approximate the way how LType and RType might refer to "essentially thesame" type,...
static std::optional< ConversionSequence > tryConversionOperators(const TheCheck &Check, const CXXRecordDecl *RD, QualType ToType)
static NonCVRQualifiersResult getNonCVRQualifiers(const ASTContext &Ctx, QualType LType, QualType RType)
Returns if the two types are qualified in a way that ever after equating or removing local CVR qualif...
static MixData isLRefEquallyBindingToType(const TheCheck &Check, const LValueReferenceType *LRef, QualType Ty, const ASTContext &Ctx, bool IsRefRHS, ImplicitConversionModellingMode ImplicitMode)
Calculates if the reference binds an expression of the given type.
static bool hasFlag(MixFlags Data, MixFlags SearchedFlag)
Returns whether the SearchedFlag is turned on in the Data.
static bool needsToElaborateImplicitConversion(const model::Mix &M)
Returns whether a particular Mix between the two parameters should have implicit conversions elaborat...
static SmallString< 64 > getNameOrUnnamed(const NamedDecl *ND)
Returns the diagnostic-friendly name of the node, or a constant value.
static SmallString< 64 > getName(const NamedDecl *ND)
Returns the diagnostic-friendly name of the node, or empty string.
static bool needsToPrintTypeInDiagnostic(const model::Mix &M)
Returns whether a particular Mix between two parameters should have the types involved diagnosed to t...
static unsigned clampMinimumLength(const unsigned Value)
Returns the DefaultMinimumLength if the Value of requested minimum length is less than 2.
EasilySwappableParametersCheck TheCheck
std::string serializeStringList(ArrayRef< StringRef > Strings)
Serialize a sequence of names that can be parsed by parseStringList.
llvm::StringMap< ClangTidyValue > OptionMap
QualType ConversionOperatorResultType
const CXXConversionDecl * Fun
const CXXConstructorDecl * Fun
QualType ConstructorParameterType
The results of the steps of an Implicit Conversion Sequence is saved in an instance of this record.
void setConversion(const UserDefinedConversionOperator &UDCO)
Sets the user-defined conversion to the given operator.
UserDefinedConversionOperator UDConvOp
ConversionSequence & update(const ConversionSequence &RHS)
Updates the steps of the conversion sequence with the steps from the other instance.
const CXXMethodDecl * getUserDefinedConversionFunction() const
SourceRange getUserDefinedConversionHighlight() const
Returns the SourceRange in the text that corresponds to the interesting part of the user-defined conv...
void setConversion(const UserDefinedConvertingConstructor &UDCC)
Sets the user-defined conversion to the given constructor.
SmallVector< QualType, 4 > getInvolvedTypesInSequence() const
Returns all the "steps" (non-unique and non-similar) types involved in the conversion sequence.
ConversionSequence(QualType From, QualType To)
QualType End
The result type the conversion targeted.
QualType getTypeAfterUserDefinedConversion() const
Returns the type in the conversion that's formally "in our hands" once the user-defined conversion is...
UserDefinedConversionKind
UserDefinedConvertingConstructor UDConvCtor
UserDefinedConversionKind UDConvKind
QualType Begin
The type the conversion stared from.
QualType AfterSecondStandard
The intermediate type after performing the second Standard Conversion Sequence.
QualType AfterFirstStandard
The intermediate type after the first Standard Conversion Sequence.
Contains the metadata for the mixability result between two types, independently of which parameters ...
QualType CommonType
A potentially calculated common underlying type after desugaring, that both sides of the mix can orig...
MixData(MixFlags Flags, ConversionSequence Conv)
ConversionSequence ConversionRTL
MixData(MixFlags Flags, QualType CommonType)
ConversionSequence Conversion
The steps an implicit conversion performs to get from one type to the other.
bool CreatedFromOneWayConversion
True if the MixData was specifically created with only a one-way conversion modelled.
MixData withCommonTypeTransformed(const F &Func) const
MixData(MixFlags Flags, ConversionSequence LTR, ConversionSequence RTL)
MixFlags Flags
The flag bits of the mix indicating what language features allow for it.
MixData(MixFlags Flags, QualType CommonType, ConversionSequence LTR, ConversionSequence RTL)
MixData operator|(MixFlags EnableFlags) const
Add the specified flag bits to the flags.
MixData & operator|=(MixFlags EnableFlags)
Add the specified flag bits to the flags.
bool indicatesMixability() const
A named tuple that contains the information for a mix between two concrete parameters.
const ConversionSequence & rightToLeftConversionSequence() const
Mix(const ParmVarDecl *F, const ParmVarDecl *S, MixData Data)
const ConversionSequence & leftToRightConversionSequence() const
const ParmVarDecl * Second
QualType commonUnderlyingType() const
const ParmVarDecl * First
const ParmVarDecl * getFirstParam() const
Gets the leftmost parameter of the range.
MixVector Mixes
The individual flags and supporting information for the mixes.
const ParmVarDecl * getLastParam() const
Gets the rightmost parameter of the range.
SmallVector< Mix, 8 > MixVector
A container for Mixes.
std::size_t NumParamsChecked
The number of parameters iterated to build the instance.