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.");
422 if ((
Flags & CanonicalAndWorkaround) == CanonicalAndWorkaround) {
447 bool ShouldHaveImplicitConvFlag =
false;
449 ShouldHaveImplicitConvFlag =
true;
454 ShouldHaveImplicitConvFlag =
true;
456 if (ShouldHaveImplicitConvFlag)
478 Flags |= EnableFlags;
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 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 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 bool FromNumeric = FromBuiltin && (FromBuiltin->isIntegerType() ||
987 FromBuiltin->isFloatingType());
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 (Ctx.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 bool BestConversionHasImplicit =
1155 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 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 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 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 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 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 StringRef NodeName = Node->getName();
1523 LLVM_DEBUG(llvm::dbgs() <<
"\tName ignored.\n");
1527 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();
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 for (
const auto &E1SetElem : E1Iterator->second)
1593 if (E2Iterator->second.contains(E1SetElem))
1604 using Base = RecursiveASTVisitor<AppearsInSameExpr>;
1606 const FunctionDecl *FD;
1607 const Expr *CurrentExprOnlyTreeRoot =
nullptr;
1608 llvm::DenseMap<
const ParmVarDecl *,
1609 llvm::SmallPtrSet<const Expr *, SmallDataStructureSize>>
1610 ParentExprsForParamRefs;
1615 TraverseFunctionDecl(
const_cast<FunctionDecl *
>(FD));
1618 bool operator()(
const ParmVarDecl *Param1,
const ParmVarDecl *Param2)
const {
1624 CurrentExprOnlyTreeRoot =
nullptr;
1625 return Base::TraverseDecl(D);
1629 if (
auto *E = dyn_cast_or_null<Expr>(S)) {
1630 bool RootSetInCurrentStackFrame =
false;
1631 if (!CurrentExprOnlyTreeRoot) {
1632 CurrentExprOnlyTreeRoot = E;
1633 RootSetInCurrentStackFrame =
true;
1636 bool Ret = Base::TraverseStmt(S);
1638 if (RootSetInCurrentStackFrame)
1639 CurrentExprOnlyTreeRoot =
nullptr;
1645 CurrentExprOnlyTreeRoot =
nullptr;
1646 return Base::TraverseStmt(S);
1650 if (!CurrentExprOnlyTreeRoot)
1653 if (
auto *PVD = dyn_cast<ParmVarDecl>(DRE->getDecl()))
1654 if (llvm::find(FD->parameters(), PVD))
1655 ParentExprsForParamRefs[PVD].insert(CurrentExprOnlyTreeRoot);
1670 auto ParamsAsArgsInFnCalls =
1671 match(functionDecl(forEachDescendant(
1672 callExpr(forEachArgumentWithParam(
1673 paramRefExpr(), parmVarDecl().bind(
"passed-to")))
1674 .bind(
"call-expr"))),
1675 *FD, FD->getASTContext());
1676 for (
const auto &Match : ParamsAsArgsInFnCalls) {
1677 const auto *PassedParamOfThisFn = Match.getNodeAs<ParmVarDecl>(
"param");
1678 const auto *CE = Match.getNodeAs<CallExpr>(
"call-expr");
1679 const auto *PassedToParam = Match.getNodeAs<ParmVarDecl>(
"passed-to");
1680 assert(PassedParamOfThisFn && CE && PassedToParam);
1682 const FunctionDecl *CalledFn = CE->getDirectCallee();
1686 std::optional<unsigned> TargetIdx;
1687 unsigned NumFnParams = CalledFn->getNumParams();
1688 for (
unsigned Idx = 0; Idx < NumFnParams; ++Idx)
1689 if (CalledFn->getParamDecl(Idx) == PassedToParam)
1690 TargetIdx.emplace(Idx);
1692 assert(TargetIdx &&
"Matched, but didn't find index?");
1693 TargetParams[PassedParamOfThisFn].insert(
1694 {CalledFn->getCanonicalDecl(), *TargetIdx});
1698 bool operator()(
const ParmVarDecl *Param1,
const ParmVarDecl *Param2)
const {
1710 auto MembersCalledOnParams = match(
1711 functionDecl(forEachDescendant(
1712 memberExpr(hasObjectExpression(paramRefExpr())).bind(
"mem-expr"))),
1713 *FD, FD->getASTContext());
1715 for (
const auto &Match : MembersCalledOnParams) {
1716 const auto *AccessedParam = Match.getNodeAs<ParmVarDecl>(
"param");
1717 const auto *ME = Match.getNodeAs<MemberExpr>(
"mem-expr");
1718 assert(AccessedParam && ME);
1719 AccessedMembers[AccessedParam].insert(
1720 ME->getMemberDecl()->getCanonicalDecl());
1724 bool operator()(
const ParmVarDecl *Param1,
const ParmVarDecl *Param2)
const {
1732 llvm::SmallVector<const ParmVarDecl *, SmallDataStructureSize> ReturnedParams;
1737 auto ParamReturns = match(functionDecl(forEachDescendant(
1738 returnStmt(hasReturnValue(paramRefExpr())))),
1739 *FD, FD->getASTContext());
1740 for (
const auto &Match : ParamReturns) {
1741 const auto *ReturnedParam = Match.getNodeAs<ParmVarDecl>(
"param");
1742 assert(ReturnedParam);
1744 if (find(FD->parameters(), ReturnedParam) == FD->param_end())
1750 ReturnedParams.emplace_back(ReturnedParam);
1754 bool operator()(
const ParmVarDecl *Param1,
const ParmVarDecl *Param2)
const {
1755 return llvm::is_contained(ReturnedParams, Param1) &&
1756 llvm::is_contained(ReturnedParams, Param2);
1778 PassToFun.setup(FD);
1779 SameMember.setup(FD);
1785 bool operator()(
const ParmVarDecl *Param1,
const ParmVarDecl *Param2)
const {
1789 LLVM_DEBUG(llvm::dbgs()
1790 <<
"::: Matching similar usage / relatedness heuristic...\n");
1792 if (SameExpr(Param1, Param2)) {
1793 LLVM_DEBUG(llvm::dbgs() <<
"::: Used in the same expression.\n");
1797 if (PassToFun(Param1, Param2)) {
1798 LLVM_DEBUG(llvm::dbgs()
1799 <<
"::: Passed to same function in different calls.\n");
1803 if (SameMember(Param1, Param2)) {
1804 LLVM_DEBUG(llvm::dbgs()
1805 <<
"::: Same member field access or method called.\n");
1809 if (Returns(Param1, Param2)) {
1810 LLVM_DEBUG(llvm::dbgs() <<
"::: Both parameter returned.\n");
1814 LLVM_DEBUG(llvm::dbgs() <<
"::: None.\n");
1823 const ParmVarDecl *Param1,
const ParmVarDecl *Param2) {
1824 return Suppressor(Param1, Param2);
1828 while (Str.size() < ToLen)
1829 Str.emplace_back(
'\0');
1833 while (Str.size() < ToLen)
1834 Str.insert(Str.begin(),
'\0');
1839 assert(S1.size() >= N && S2.size() >= N);
1840 StringRef S1Prefix = S1.take_front(S1.size() - N),
1841 S2Prefix = S2.take_front(S2.size() - N);
1842 return S1Prefix == S2Prefix && !S1Prefix.empty();
1847 assert(S1.size() >= N && S2.size() >= N);
1848 StringRef S1Suffix = S1.take_back(S1.size() - N),
1849 S2Suffix = S2.take_back(S2.size() - N);
1850 return S1Suffix == S2Suffix && !S1Suffix.empty();
1856 StringRef Str1, StringRef Str2) {
1861 std::size_t BiggerLength = std::max(Str1.size(), Str2.size());
1863 if (BiggerLength <= Threshold)
1870 SmallString<32> S1PadE{Str1}, S2PadE{Str2};
1875 Threshold, StringRef{S1PadE.begin(), BiggerLength},
1876 StringRef{S2PadE.begin(), BiggerLength}))
1879 SmallString<32> S1PadB{Str1}, S2PadB{Str2};
1884 Threshold, StringRef{S1PadB.begin(), BiggerLength},
1885 StringRef{S2PadB.begin(), BiggerLength}))
1896AST_MATCHER_P(FunctionDecl, parameterCountGE,
unsigned, N) {
1897 return Node.getNumParams() >= N;
1901AST_MATCHER(FunctionDecl, isOverloadedUnaryOrBinaryOperator) {
1902 switch (Node.getOverloadedOperator()) {
1907 case OO_Array_Delete:
1908 case OO_Conditional:
1913 return Node.getNumParams() <= 2;
1928static SmallString<64>
getName(
const NamedDecl *ND) {
1929 SmallString<64> Name;
1930 llvm::raw_svector_ostream OS{Name};
1931 ND->getNameForDiagnostic(OS, ND->getASTContext().getPrintingPolicy(),
false);
1946 using namespace model;
1947 return static_cast<bool>(
1949 (MixFlags::TypeAlias | MixFlags::ReferenceBind | MixFlags::Qualifiers));
1962struct FormattedConversionSequence {
1963 std::string DiagnosticText;
1968 bool Trivial =
true;
1970 FormattedConversionSequence(
const PrintingPolicy &
PP,
1971 StringRef StartTypeAsDiagnosed,
1972 const model::ConversionSequence &Conv,
1973 StringRef DestinationTypeAsDiagnosed) {
1974 llvm::raw_string_ostream OS{DiagnosticText};
1977 OS <<
'\'' << StartTypeAsDiagnosed <<
'\'';
1978 std::string LastAddedType = StartTypeAsDiagnosed.str();
1979 std::size_t NumElementsAdded = 1;
1983 std::string SeqBeginTypeStr = Conv.Begin.getAsString(
PP);
1984 std::string SeqEndTypeStr = Conv.End.getAsString(
PP);
1985 if (StartTypeAsDiagnosed != SeqBeginTypeStr) {
1986 OS <<
" (as '" << SeqBeginTypeStr <<
"')";
1987 LastAddedType = SeqBeginTypeStr;
1991 auto AddType = [&](StringRef ToAdd) {
1992 if (LastAddedType != ToAdd && ToAdd != SeqEndTypeStr) {
1993 OS <<
" -> '" << ToAdd <<
"'";
1994 LastAddedType = ToAdd.str();
1998 for (QualType InvolvedType : Conv.getInvolvedTypesInSequence())
2000 AddType(InvolvedType.getAsString(
PP));
2002 if (LastAddedType != DestinationTypeAsDiagnosed) {
2003 OS <<
" -> '" << DestinationTypeAsDiagnosed <<
"'";
2004 LastAddedType = DestinationTypeAsDiagnosed.str();
2011 if (DestinationTypeAsDiagnosed != SeqEndTypeStr) {
2012 OS <<
" (as '" << SeqEndTypeStr <<
"')";
2013 LastAddedType = SeqEndTypeStr;
2017 if (Trivial && NumElementsAdded > 2)
2027template <
typename E, std::
size_t N>
class InsertOnce {
2028 llvm::SmallSet<E, N> CalledWith;
2031 bool operator()(E El) {
return CalledWith.insert(std::move(El)).second; }
2033 bool calledWith(
const E &El)
const {
return CalledWith.contains(El); }
2036struct SwappedEqualQualTypePair {
2037 QualType LHSType, RHSType;
2039 bool operator==(
const SwappedEqualQualTypePair &Other)
const {
2040 return (LHSType == Other.LHSType && RHSType == Other.RHSType) ||
2041 (LHSType == Other.RHSType && RHSType == Other.LHSType);
2044 bool operator<(
const SwappedEqualQualTypePair &Other)
const {
2045 return LHSType < Other.LHSType && RHSType < Other.RHSType;
2049struct TypeAliasDiagnosticTuple {
2050 QualType LHSType, RHSType, CommonType;
2052 bool operator==(
const TypeAliasDiagnosticTuple &Other)
const {
2053 return CommonType == Other.CommonType &&
2054 ((LHSType == Other.LHSType && RHSType == Other.RHSType) ||
2055 (LHSType == Other.RHSType && RHSType == Other.LHSType));
2058 bool operator<(
const TypeAliasDiagnosticTuple &Other)
const {
2059 return CommonType < Other.CommonType && LHSType < Other.LHSType &&
2060 RHSType < Other.RHSType;
2065class UniqueTypeAliasDiagnosticHelper
2066 :
public InsertOnce<TypeAliasDiagnosticTuple, 8> {
2067 using Base = InsertOnce<TypeAliasDiagnosticTuple, 8>;
2072 bool operator()(QualType LHSType, QualType RHSType, QualType CommonType) {
2073 if (CommonType.isNull() || CommonType == LHSType || CommonType == RHSType)
2074 return Base::operator()({LHSType, RHSType, {}});
2076 TypeAliasDiagnosticTuple ThreeTuple{LHSType, RHSType, CommonType};
2077 if (!Base::operator()(ThreeTuple))
2080 bool AlreadySaidLHSAndCommonIsSame = calledWith({LHSType, CommonType, {}});
2081 bool AlreadySaidRHSAndCommonIsSame = 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 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");
2186 bool HasAnyImplicits =
2188 const ParmVarDecl *First = R.getFirstParam(), *Last = R.getLastParam();
2189 std::string FirstParamTypeAsWritten = First->getType().getAsString(
PP);
2193 if (HasAnyImplicits)
2194 DiagText =
"%0 adjacent parameters of %1 of convertible types are "
2195 "easily swapped by mistake";
2196 else if (NeedsAnyTypeNote)
2197 DiagText =
"%0 adjacent parameters of %1 of similar type are easily "
2198 "swapped by mistake";
2200 DiagText =
"%0 adjacent parameters of %1 of similar type ('%2') are "
2201 "easily swapped by mistake";
2203 auto Diag = diag(First->getOuterLocStart(), DiagText)
2204 <<
static_cast<unsigned>(R.NumParamsChecked) << FD;
2205 if (!NeedsAnyTypeNote)
2206 Diag << FirstParamTypeAsWritten;
2208 CharSourceRange HighlightRange = CharSourceRange::getTokenRange(
2209 First->getBeginLoc(), Last->getEndLoc());
2210 Diag << HighlightRange;
2216 diag(First->getLocation(),
"the first parameter in the range is '%0'",
2217 DiagnosticIDs::Note)
2219 << CharSourceRange::getTokenRange(First->getLocation(),
2220 First->getLocation());
2221 diag(Last->getLocation(),
"the last parameter in the range is '%0'",
2222 DiagnosticIDs::Note)
2224 << CharSourceRange::getTokenRange(Last->getLocation(),
2225 Last->getLocation());
2229 UniqueTypeAliasDiagnosticHelper UniqueTypeAlias;
2230 InsertOnce<SwappedEqualQualTypePair, 8> UniqueBindPower;
2231 InsertOnce<SwappedEqualQualTypePair, 8> UniqueImplicitConversion;
2234 assert(M.
mixable() &&
"Sentinel or false mix in result.");
2241 const ParmVarDecl *LVar = M.
First;
2242 const ParmVarDecl *RVar = M.
Second;
2243 QualType LType = LVar->getType();
2244 QualType RType = RVar->getType();
2246 std::string LTypeStr = LType.getAsString(
PP);
2247 std::string RTypeStr = RType.getAsString(
PP);
2248 std::string CommonTypeStr = CommonType.getAsString(
PP);
2250 if (hasFlag(M.
flags(), MixFlags::TypeAlias) &&
2251 UniqueTypeAlias(LType, RType, CommonType)) {
2253 bool ExplicitlyPrintCommonType =
false;
2254 if (LTypeStr == CommonTypeStr || RTypeStr == CommonTypeStr) {
2255 if (hasFlag(M.
flags(), MixFlags::Qualifiers))
2256 DiagText =
"after resolving type aliases, '%0' and '%1' share a "
2260 "after resolving type aliases, '%0' and '%1' are the same";
2261 }
else if (!CommonType.isNull()) {
2262 DiagText =
"after resolving type aliases, the common type of '%0' "
2264 ExplicitlyPrintCommonType =
true;
2268 diag(LVar->getOuterLocStart(), DiagText, DiagnosticIDs::Note)
2269 << LTypeStr << RTypeStr;
2270 if (ExplicitlyPrintCommonType)
2271 Diag << CommonTypeStr;
2274 if ((hasFlag(M.
flags(), MixFlags::ReferenceBind) ||
2275 hasFlag(M.
flags(), MixFlags::Qualifiers)) &&
2276 UniqueBindPower({LType, RType})) {
2277 StringRef DiagText =
"'%0' and '%1' parameters accept and bind the "
2278 "same kind of values";
2279 diag(RVar->getOuterLocStart(), DiagText, DiagnosticIDs::Note)
2280 << LTypeStr << RTypeStr;
2284 UniqueImplicitConversion({LType, RType})) {
2289 FormattedConversionSequence LTRFmt{
PP, LTypeStr, LTR, RTypeStr};
2290 FormattedConversionSequence RTLFmt{
PP, RTypeStr, RTL, LTypeStr};
2292 StringRef DiagText =
"'%0' and '%1' may be implicitly converted";
2293 if (!LTRFmt.Trivial || !RTLFmt.Trivial)
2294 DiagText =
"'%0' and '%1' may be implicitly converted: %2, %3";
2298 diag(RVar->getOuterLocStart(), DiagText, DiagnosticIDs::Note)
2299 << LTypeStr << RTypeStr;
2301 if (!LTRFmt.Trivial || !RTLFmt.Trivial)
2302 Diag << LTRFmt.DiagnosticText << RTLFmt.DiagnosticText;
2305 StringRef ConversionFunctionDiagText =
2306 "the implicit conversion involves the "
2307 "%select{|converting constructor|conversion operator}0 "
2310 diag(LFD->getLocation(), ConversionFunctionDiagText,
2311 DiagnosticIDs::Note)
2315 diag(RFD->getLocation(), ConversionFunctionDiagText,
2316 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.