10#include "../utils/OptionsUtils.h"
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);
350 llvm_unreachable(
"Invalid UDConv kind.");
362 llvm_unreachable(
"Invalid UDConv kind.");
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;
490 M.CommonType = NewCommonType;
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, ElaboratedType, ParenType>(T);
585struct NonCVRQualifiersResult {
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';
730 if (!Check.QualifiersMix) {
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 QualifiersToApply = From.split().Quals.getAsOpaqueValue();
983 const auto *FromBuiltin = WorkType->getAs<BuiltinType>();
984 const auto *ToBuiltin = To->getAs<BuiltinType>();
985 bool FromNumeric = FromBuiltin && (FromBuiltin->isIntegerType() ||
986 FromBuiltin->isFloatingType());
988 ToBuiltin && (ToBuiltin->isIntegerType() || ToBuiltin->isFloatingType());
989 if (FromNumeric && ToNumeric) {
994 LLVM_DEBUG(llvm::dbgs()
995 <<
"--- approximateStdConv. Conversion between numerics.\n");
996 WorkType = QualType{ToBuiltin, QualifiersToApply};
999 const auto *FromEnum = WorkType->getAs<EnumType>();
1000 const auto *ToEnum = To->getAs<EnumType>();
1001 if (FromEnum && ToNumeric && FromEnum->isUnscopedEnumerationType()) {
1003 LLVM_DEBUG(llvm::dbgs()
1004 <<
"--- approximateStdConv. Unscoped enum to numeric.\n");
1005 WorkType = QualType{ToBuiltin, QualifiersToApply};
1006 }
else if (FromNumeric && ToEnum && ToEnum->isUnscopedEnumerationType()) {
1008 if (Ctx.getLangOpts().CPlusPlus) {
1009 LLVM_DEBUG(llvm::dbgs() <<
"<<< approximateStdConv. Numeric to unscoped "
1010 "enum, not possible in C++!\n");
1014 LLVM_DEBUG(llvm::dbgs()
1015 <<
"--- approximateStdConv. Numeric to unscoped enum.\n");
1016 WorkType = QualType{ToEnum, QualifiersToApply};
1020 const auto *FromPtr = WorkType->getAs<PointerType>();
1021 const auto *ToPtr = To->getAs<PointerType>();
1022 if (FromPtr && ToPtr) {
1023 if (ToPtr->isVoidPointerType()) {
1024 LLVM_DEBUG(llvm::dbgs() <<
"--- approximateStdConv. To void pointer.\n");
1025 WorkType = QualType{ToPtr, QualifiersToApply};
1028 const auto *FromRecordPtr = FromPtr->getPointeeCXXRecordDecl();
1029 const auto *ToRecordPtr = ToPtr->getPointeeCXXRecordDecl();
1031 LLVM_DEBUG(llvm::dbgs() <<
"--- approximateStdConv. Derived* to Base*\n");
1032 WorkType = QualType{ToPtr, QualifiersToApply};
1038 const auto *FromRecord = WorkType->getAsCXXRecordDecl();
1039 const auto *ToRecord = To->getAsCXXRecordDecl();
1041 LLVM_DEBUG(llvm::dbgs() <<
"--- approximateStdConv. Derived To Base.\n");
1042 WorkType = QualType{ToRecord->getTypeForDecl(), QualifiersToApply};
1045 if (Ctx.getLangOpts().CPlusPlus17 && FromPtr && ToPtr) {
1048 const auto *FromFunctionPtr =
1049 FromPtr->getPointeeType()->getAs<FunctionProtoType>();
1050 const auto *ToFunctionPtr =
1051 ToPtr->getPointeeType()->getAs<FunctionProtoType>();
1052 if (FromFunctionPtr && ToFunctionPtr &&
1053 FromFunctionPtr->hasNoexceptExceptionSpec() &&
1054 !ToFunctionPtr->hasNoexceptExceptionSpec()) {
1055 LLVM_DEBUG(llvm::dbgs() <<
"--- approximateStdConv. noexcept function "
1056 "pointer to non-noexcept.\n");
1057 WorkType = QualType{ToPtr, QualifiersToApply};
1063 LLVM_DEBUG(llvm::dbgs()
1064 <<
"--- approximateStdConv. Trying qualifier adjustment...\n");
1069 LLVM_DEBUG(llvm::dbgs()
1070 <<
"<<< approximateStdConv. Qualifiers adjusted.\n");
1074 if (WorkType == To) {
1075 LLVM_DEBUG(llvm::dbgs() <<
"<<< approximateStdConv. Reached 'To' type.\n");
1079 LLVM_DEBUG(llvm::dbgs() <<
"<<< approximateStdConv. Did not reach 'To'.\n");
1088class UserDefinedConversionSelector {
1094 struct PreparedConversion {
1099 PreparedConversion(
const CXXMethodDecl *CMD,
MixFlags F,
1100 ConversionSequence S)
1104 UserDefinedConversionSelector(
const TheCheck &Check) : Check(Check) {}
1111 void addConversion(
const CXXMethodDecl *ConvFun, QualType FromType,
1116 Check, FromType, ToType, ConvFun->getASTContext(),
1119 if (!Mix.indicatesMixability())
1122 LLVM_DEBUG(llvm::dbgs() <<
"--- tryConversion. Found viable with flags: "
1124 FlaggedConversions.emplace_back(ConvFun, Mix.Flags, Mix.Conversion);
1129 std::optional<PreparedConversion> operator()()
const {
1130 if (FlaggedConversions.empty()) {
1131 LLVM_DEBUG(llvm::dbgs() <<
"--- selectUserDefinedConv. Empty.\n");
1134 if (FlaggedConversions.size() == 1) {
1135 LLVM_DEBUG(llvm::dbgs() <<
"--- selectUserDefinedConv. Single.\n");
1136 return FlaggedConversions.front();
1139 std::optional<PreparedConversion> BestConversion;
1140 unsigned short HowManyGoodConversions = 0;
1141 for (
const auto &Prepared : FlaggedConversions) {
1142 LLVM_DEBUG(llvm::dbgs() <<
"--- selectUserDefinedConv. Candidate flags: "
1144 if (!BestConversion) {
1145 BestConversion = Prepared;
1146 ++HowManyGoodConversions;
1150 bool BestConversionHasImplicit =
1152 bool ThisConversionHasImplicit =
1154 if (!BestConversionHasImplicit && ThisConversionHasImplicit)
1158 if (BestConversionHasImplicit && !ThisConversionHasImplicit) {
1163 BestConversion = Prepared;
1164 HowManyGoodConversions = 1;
1168 if (BestConversionHasImplicit == ThisConversionHasImplicit)
1171 ++HowManyGoodConversions;
1174 if (HowManyGoodConversions == 1) {
1175 LLVM_DEBUG(llvm::dbgs()
1176 <<
"--- selectUserDefinedConv. Unique result. Flags: "
1178 return BestConversion;
1181 LLVM_DEBUG(llvm::dbgs()
1182 <<
"--- selectUserDefinedConv. No, or ambiguous.\n");
1187 llvm::SmallVector<PreparedConversion, 2> FlaggedConversions;
1193static std::optional<ConversionSequence>
1196 if (!RD || !RD->isCompleteDefinition())
1198 RD = RD->getDefinition();
1200 LLVM_DEBUG(llvm::dbgs() <<
">>> tryConversionOperators: " << RD->getName()
1202 ToType.dump(llvm::dbgs(), RD->getASTContext());
1203 llvm::dbgs() <<
'\n';);
1205 UserDefinedConversionSelector ConversionSet{Check};
1207 for (
const NamedDecl *Method : RD->getVisibleConversionFunctions()) {
1208 const auto *Con = dyn_cast<CXXConversionDecl>(Method);
1209 if (!Con || Con->isExplicit())
1211 LLVM_DEBUG(llvm::dbgs() <<
"--- tryConversionOperators. Trying:\n";
1212 Con->dump(llvm::dbgs()); llvm::dbgs() <<
'\n';);
1216 ConversionSet.addConversion(Con, Con->getConversionType(), ToType);
1219 if (std::optional<UserDefinedConversionSelector::PreparedConversion>
1220 SelectedConversion = ConversionSet()) {
1221 QualType RecordType{RD->getTypeForDecl(), 0};
1228 Result.AfterSecondStandard = SelectedConversion->Seq.AfterFirstStandard;
1231 ConvOp.
Fun = cast<CXXConversionDecl>(SelectedConversion->ConversionFun);
1234 Result.setConversion(ConvOp);
1236 LLVM_DEBUG(llvm::dbgs() <<
"<<< tryConversionOperators. Found result.\n");
1240 LLVM_DEBUG(llvm::dbgs() <<
"<<< tryConversionOperators. No conversion.\n");
1244static std::optional<ConversionSequence>
1246 const CXXRecordDecl *RD) {
1247 if (!RD || !RD->isCompleteDefinition())
1249 RD = RD->getDefinition();
1251 LLVM_DEBUG(llvm::dbgs() <<
">>> tryConveringConstructors: " << RD->getName()
1253 FromType.dump(llvm::dbgs(), RD->getASTContext());
1254 llvm::dbgs() <<
'\n';);
1256 UserDefinedConversionSelector ConversionSet{Check};
1258 for (
const CXXConstructorDecl *Con : RD->ctors()) {
1259 if (Con->isCopyOrMoveConstructor() ||
1260 !Con->isConvertingConstructor(
false))
1262 LLVM_DEBUG(llvm::dbgs() <<
"--- tryConvertingConstructors. Trying:\n";
1263 Con->dump(llvm::dbgs()); llvm::dbgs() <<
'\n';);
1267 ConversionSet.addConversion(Con, FromType, Con->getParamDecl(0)->getType());
1270 if (std::optional<UserDefinedConversionSelector::PreparedConversion>
1271 SelectedConversion = ConversionSet()) {
1272 QualType RecordType{RD->getTypeForDecl(), 0};
1275 Result.AfterFirstStandard = SelectedConversion->Seq.AfterFirstStandard;
1278 Ctor.
Fun = cast<CXXConstructorDecl>(SelectedConversion->ConversionFun);
1281 Result.setConversion(Ctor);
1283 LLVM_DEBUG(llvm::dbgs()
1284 <<
"<<< tryConvertingConstructors. Found result.\n");
1288 LLVM_DEBUG(llvm::dbgs() <<
"<<< tryConvertingConstructors. No conversion.\n");
1299 QualType RType,
const ASTContext &Ctx,
1301 LLVM_DEBUG(llvm::dbgs() <<
">>> approximateImplicitConversion for LType:\n";
1302 LType.dump(llvm::dbgs(), Ctx); llvm::dbgs() <<
"\nand RType:\n";
1303 RType.dump(llvm::dbgs(), Ctx);
1304 llvm::dbgs() <<
"\nimplicit mode: ";
switch (ImplicitMode) {
1306 llvm::dbgs() <<
"None";
1309 llvm::dbgs() <<
"All";
1312 llvm::dbgs() <<
"OneWay, Single, STD Only";
1314 } llvm::dbgs() <<
'\n';);
1323 QualType WorkType = LType;
1325 std::optional<QualType> AfterFirstStdConv =
1327 if (AfterFirstStdConv) {
1328 LLVM_DEBUG(llvm::dbgs() <<
"--- approximateImplicitConversion. Standard "
1329 "Pre-Conversion found!\n");
1330 ImplicitSeq.AfterFirstStandard = *AfterFirstStdConv;
1331 WorkType = ImplicitSeq.AfterFirstStandard;
1336 return {ImplicitSeq.AfterFirstStandard.isNull()
1341 if (Ctx.getLangOpts().CPlusPlus) {
1342 bool FoundConversionOperator =
false, FoundConvertingCtor =
false;
1344 if (
const auto *LRD = WorkType->getAsCXXRecordDecl()) {
1345 std::optional<ConversionSequence> ConversionOperatorResult =
1347 if (ConversionOperatorResult) {
1348 LLVM_DEBUG(llvm::dbgs() <<
"--- approximateImplicitConversion. Found "
1349 "conversion operator.\n");
1350 ImplicitSeq.update(*ConversionOperatorResult);
1351 WorkType = ImplicitSeq.getTypeAfterUserDefinedConversion();
1352 FoundConversionOperator =
true;
1356 if (
const auto *RRD = RType->getAsCXXRecordDecl()) {
1360 std::optional<ConversionSequence> ConvCtorResult =
1362 if (ConvCtorResult) {
1363 LLVM_DEBUG(llvm::dbgs() <<
"--- approximateImplicitConversion. Found "
1364 "converting constructor.\n");
1365 ImplicitSeq.update(*ConvCtorResult);
1366 WorkType = ImplicitSeq.getTypeAfterUserDefinedConversion();
1367 FoundConvertingCtor =
true;
1371 if (FoundConversionOperator && FoundConvertingCtor) {
1373 LLVM_DEBUG(llvm::dbgs()
1374 <<
"<<< approximateImplicitConversion. Found both "
1375 "user-defined conversion kinds in the same sequence!\n");
1384 <<
"--- approximateImplicitConversion. Try to find post-conversion.\n");
1386 Check, WorkType, RType, Ctx,
1389 LLVM_DEBUG(llvm::dbgs() <<
"--- approximateImplicitConversion. Standard "
1390 "Post-Conversion found!\n");
1395 ImplicitSeq.AfterSecondStandard =
1397 WorkType = ImplicitSeq.AfterSecondStandard;
1401 LLVM_DEBUG(llvm::dbgs()
1402 <<
"<<< approximateImplicitConversion. Found a conversion.\n");
1407 llvm::dbgs() <<
"<<< approximateImplicitConversion. No match found.\n");
1412 const TheCheck &Check,
const FunctionDecl *FD, std::size_t StartIndex,
1414 std::size_t NumParams = FD->getNumParams();
1415 assert(StartIndex < NumParams &&
"out of bounds for start");
1416 const ASTContext &Ctx = FD->getASTContext();
1422 for (std::size_t I = StartIndex + 1; I < NumParams; ++I) {
1423 const ParmVarDecl *Ith = FD->getParamDecl(I);
1424 StringRef ParamName = Ith->getName();
1425 LLVM_DEBUG(llvm::dbgs()
1426 <<
"Check param #" << I <<
" '" << ParamName <<
"'...\n");
1428 LLVM_DEBUG(llvm::dbgs() <<
"Param #" << I <<
" is ignored. Break!\n");
1432 StringRef PrevParamName = FD->getParamDecl(I - 1)->getName();
1433 if (!ParamName.empty() && !PrevParamName.empty() &&
1435 Check.NamePrefixSuffixSilenceDissimilarityTreshold, PrevParamName,
1437 LLVM_DEBUG(llvm::dbgs() <<
"Parameter '" << ParamName
1438 <<
"' follows a pattern with previous parameter '"
1439 << PrevParamName <<
"'. Break!\n");
1446 for (std::size_t J = StartIndex; J < I; ++J) {
1447 const ParmVarDecl *Jth = FD->getParamDecl(J);
1448 LLVM_DEBUG(llvm::dbgs()
1449 <<
"Check mix of #" << J <<
" against #" << I <<
"...\n");
1451 if (isSimilarlyUsedParameter(UsageBasedSuppressor, Ith, Jth)) {
1454 LLVM_DEBUG(llvm::dbgs() <<
"Parameters #" << I <<
" and #" << J
1455 <<
" deemed related, ignoring...\n");
1466 Check.ModelImplicitConversions
1469 LLVM_DEBUG(llvm::dbgs() <<
"Mix flags (raw) : "
1472 LLVM_DEBUG(llvm::dbgs() <<
"Mix flags (after sanitize): "
1475 assert(
M.flagsValid() &&
"All flags decayed!");
1478 MixesOfIth.emplace_back(std::move(
M));
1481 if (MixesOfIth.empty()) {
1484 LLVM_DEBUG(llvm::dbgs()
1486 <<
" does not mix with any in the current range. Break!\n");
1490 Ret.
Mixes.insert(Ret.
Mixes.end(), MixesOfIth.begin(), MixesOfIth.end());
1501 return expr(ignoringParenImpCasts(ignoringElidableConstructorCall(
1502 declRefExpr(to(parmVarDecl().bind(
"param"))))));
1510 LLVM_DEBUG(llvm::dbgs() <<
"Checking if '" <<
Node->getName()
1511 <<
"' is ignored.\n");
1513 if (!
Node->getIdentifier())
1514 return llvm::is_contained(Check.IgnoredParameterNames,
"\"\"");
1516 StringRef NodeName =
Node->getName();
1517 if (llvm::is_contained(Check.IgnoredParameterNames, NodeName)) {
1518 LLVM_DEBUG(llvm::dbgs() <<
"\tName ignored.\n");
1522 StringRef NodeTypeName = [
Node] {
1523 const ASTContext &Ctx =
Node->getASTContext();
1524 const SourceManager &SM = Ctx.getSourceManager();
1525 SourceLocation B =
Node->getTypeSpecStartLoc();
1526 SourceLocation
E =
Node->getTypeSpecEndLoc();
1529 LLVM_DEBUG(llvm::dbgs() <<
"\tType name code is '"
1530 << Lexer::getSourceText(
1531 CharSourceRange::getTokenRange(B,
E), SM, LO)
1533 if (B.isMacroID()) {
1534 LLVM_DEBUG(llvm::dbgs() <<
"\t\tBeginning is macro.\n");
1535 B = SM.getTopMacroCallerLoc(B);
1537 if (
E.isMacroID()) {
1538 LLVM_DEBUG(llvm::dbgs() <<
"\t\tEnding is macro.\n");
1539 E = Lexer::getLocForEndOfToken(SM.getTopMacroCallerLoc(
E), 0, SM, LO);
1541 LLVM_DEBUG(llvm::dbgs() <<
"\tType name code is '"
1542 << Lexer::getSourceText(
1543 CharSourceRange::getTokenRange(B,
E), SM, LO)
1546 return Lexer::getSourceText(CharSourceRange::getTokenRange(B,
E), SM, LO);
1549 LLVM_DEBUG(llvm::dbgs() <<
"\tType name is '" << NodeTypeName <<
"'\n");
1550 if (!NodeTypeName.empty()) {
1551 if (llvm::any_of(Check.IgnoredParameterTypeSuffixes,
1552 [NodeTypeName](StringRef
E) {
1553 return !E.empty() && NodeTypeName.endswith(E);
1555 LLVM_DEBUG(llvm::dbgs() <<
"\tType suffix ignored.\n");
1565namespace relatedness_heuristic {
1569template <
typename T, std::
size_t N = SmallDataStructureSize>
1571 llvm::DenseMap<const ParmVarDecl *, llvm::SmallSet<T, N>>;
1575template <
typename MapTy,
typename ElemTy>
1578 auto E1Iterator = Map.find(E1);
1579 auto E2Iterator = Map.find(E2);
1580 if (E1Iterator == Map.end() || E2Iterator == Map.end())
1583 for (
const auto &E1SetElem : E1Iterator->second)
1584 if (E2Iterator->second.contains(E1SetElem))
1595 using Base = RecursiveASTVisitor<AppearsInSameExpr>;
1597 const FunctionDecl *FD;
1598 const Expr *CurrentExprOnlyTreeRoot =
nullptr;
1599 llvm::DenseMap<
const ParmVarDecl *,
1600 llvm::SmallPtrSet<const Expr *, SmallDataStructureSize>>
1601 ParentExprsForParamRefs;
1606 TraverseFunctionDecl(
const_cast<FunctionDecl *
>(FD));
1609 bool operator()(
const ParmVarDecl *Param1,
const ParmVarDecl *Param2)
const {
1615 CurrentExprOnlyTreeRoot =
nullptr;
1616 return Base::TraverseDecl(D);
1620 if (
auto *
E = dyn_cast_or_null<Expr>(S)) {
1621 bool RootSetInCurrentStackFrame =
false;
1622 if (!CurrentExprOnlyTreeRoot) {
1623 CurrentExprOnlyTreeRoot =
E;
1624 RootSetInCurrentStackFrame =
true;
1627 bool Ret = Base::TraverseStmt(S);
1629 if (RootSetInCurrentStackFrame)
1630 CurrentExprOnlyTreeRoot =
nullptr;
1636 CurrentExprOnlyTreeRoot =
nullptr;
1637 return Base::TraverseStmt(S);
1641 if (!CurrentExprOnlyTreeRoot)
1644 if (
auto *PVD = dyn_cast<ParmVarDecl>(DRE->getDecl()))
1645 if (llvm::find(FD->parameters(), PVD))
1646 ParentExprsForParamRefs[PVD].insert(CurrentExprOnlyTreeRoot);
1661 auto ParamsAsArgsInFnCalls =
1662 match(functionDecl(forEachDescendant(
1663 callExpr(forEachArgumentWithParam(
1664 paramRefExpr(), parmVarDecl().bind(
"passed-to")))
1665 .bind(
"call-expr"))),
1666 *FD, FD->getASTContext());
1667 for (
const auto &Match : ParamsAsArgsInFnCalls) {
1668 const auto *PassedParamOfThisFn = Match.getNodeAs<ParmVarDecl>(
"param");
1669 const auto *
CE = Match.getNodeAs<CallExpr>(
"call-expr");
1670 const auto *PassedToParam = Match.getNodeAs<ParmVarDecl>(
"passed-to");
1671 assert(PassedParamOfThisFn &&
CE && PassedToParam);
1673 const FunctionDecl *CalledFn =
CE->getDirectCallee();
1677 std::optional<unsigned> TargetIdx;
1678 unsigned NumFnParams = CalledFn->getNumParams();
1679 for (
unsigned Idx = 0; Idx < NumFnParams; ++Idx)
1680 if (CalledFn->getParamDecl(Idx) == PassedToParam)
1681 TargetIdx.emplace(Idx);
1683 assert(TargetIdx &&
"Matched, but didn't find index?");
1684 TargetParams[PassedParamOfThisFn].insert(
1685 {CalledFn->getCanonicalDecl(), *TargetIdx});
1689 bool operator()(
const ParmVarDecl *Param1,
const ParmVarDecl *Param2)
const {
1701 auto MembersCalledOnParams = match(
1702 functionDecl(forEachDescendant(
1703 memberExpr(hasObjectExpression(paramRefExpr())).bind(
"mem-expr"))),
1704 *FD, FD->getASTContext());
1706 for (
const auto &Match : MembersCalledOnParams) {
1707 const auto *AccessedParam = Match.getNodeAs<ParmVarDecl>(
"param");
1708 const auto *ME = Match.getNodeAs<MemberExpr>(
"mem-expr");
1709 assert(AccessedParam && ME);
1710 AccessedMembers[AccessedParam].insert(
1711 ME->getMemberDecl()->getCanonicalDecl());
1715 bool operator()(
const ParmVarDecl *Param1,
const ParmVarDecl *Param2)
const {
1723 llvm::SmallVector<const ParmVarDecl *, SmallDataStructureSize> ReturnedParams;
1728 auto ParamReturns = match(functionDecl(forEachDescendant(
1729 returnStmt(hasReturnValue(paramRefExpr())))),
1730 *FD, FD->getASTContext());
1731 for (
const auto &Match : ParamReturns) {
1732 const auto *ReturnedParam = Match.getNodeAs<ParmVarDecl>(
"param");
1733 assert(ReturnedParam);
1735 if (find(FD->parameters(), ReturnedParam) == FD->param_end())
1741 ReturnedParams.emplace_back(ReturnedParam);
1745 bool operator()(
const ParmVarDecl *Param1,
const ParmVarDecl *Param2)
const {
1746 return llvm::is_contained(ReturnedParams, Param1) &&
1747 llvm::is_contained(ReturnedParams, Param2);
1769 PassToFun.
setup(FD);
1770 SameMember.
setup(FD);
1776 bool operator()(
const ParmVarDecl *Param1,
const ParmVarDecl *Param2)
const {
1780 LLVM_DEBUG(llvm::dbgs()
1781 <<
"::: Matching similar usage / relatedness heuristic...\n");
1783 if (SameExpr(Param1, Param2)) {
1784 LLVM_DEBUG(llvm::dbgs() <<
"::: Used in the same expression.\n");
1788 if (PassToFun(Param1, Param2)) {
1789 LLVM_DEBUG(llvm::dbgs()
1790 <<
"::: Passed to same function in different calls.\n");
1794 if (SameMember(Param1, Param2)) {
1795 LLVM_DEBUG(llvm::dbgs()
1796 <<
"::: Same member field access or method called.\n");
1800 if (Returns(Param1, Param2)) {
1801 LLVM_DEBUG(llvm::dbgs() <<
"::: Both parameter returned.\n");
1805 LLVM_DEBUG(llvm::dbgs() <<
"::: None.\n");
1814 const ParmVarDecl *Param1,
const ParmVarDecl *Param2) {
1815 return Suppressor(Param1, Param2);
1819 while (Str.size() < ToLen)
1820 Str.emplace_back(
'\0');
1824 while (Str.size() < ToLen)
1825 Str.insert(Str.begin(),
'\0');
1830 assert(S1.size() >= N && S2.size() >= N);
1831 StringRef S1Prefix = S1.take_front(S1.size() - N),
1832 S2Prefix = S2.take_front(S2.size() - N);
1833 return S1Prefix == S2Prefix && !S1Prefix.empty();
1838 assert(S1.size() >= N && S2.size() >= N);
1839 StringRef S1Suffix = S1.take_back(S1.size() - N),
1840 S2Suffix = S2.take_back(S2.size() - N);
1841 return S1Suffix == S2Suffix && !S1Suffix.empty();
1847 StringRef Str1, StringRef Str2) {
1852 std::size_t BiggerLength = std::max(Str1.size(), Str2.size());
1854 if (BiggerLength <= Threshold)
1861 SmallString<32> S1PadE{Str1}, S2PadE{Str2};
1866 Threshold, StringRef{S1PadE.begin(), BiggerLength},
1867 StringRef{S2PadE.begin(), BiggerLength}))
1870 SmallString<32> S1PadB{Str1}, S2PadB{Str2};
1875 Threshold, StringRef{S1PadB.begin(), BiggerLength},
1876 StringRef{S2PadB.begin(), BiggerLength}))
1886 return Node.getNumParams() >= N;
1891 switch (
Node.getOverloadedOperator()) {
1896 case OO_Array_Delete:
1897 case OO_Conditional:
1902 return Node.getNumParams() <= 2;
1915static SmallString<64>
getName(
const NamedDecl *ND) {
1916 SmallString<64>
Name;
1917 llvm::raw_svector_ostream
OS{
Name};
1918 ND->getNameForDiagnostic(
OS, ND->getASTContext().getPrintingPolicy(),
false);
1933 using namespace model;
1934 return static_cast<bool>(
1936 (MixFlags::TypeAlias | MixFlags::ReferenceBind | MixFlags::Qualifiers));
1949struct FormattedConversionSequence {
1957 FormattedConversionSequence(
const PrintingPolicy &
PP,
1958 StringRef StartTypeAsDiagnosed,
1959 const model::ConversionSequence &Conv,
1960 StringRef DestinationTypeAsDiagnosed) {
1964 OS <<
'\'' << StartTypeAsDiagnosed <<
'\'';
1965 std::string LastAddedType = StartTypeAsDiagnosed.str();
1966 std::size_t NumElementsAdded = 1;
1970 std::string SeqBeginTypeStr = Conv.Begin.getAsString(
PP);
1971 std::string SeqEndTypeStr = Conv.End.getAsString(
PP);
1972 if (StartTypeAsDiagnosed != SeqBeginTypeStr) {
1973 OS <<
" (as '" << SeqBeginTypeStr <<
"')";
1974 LastAddedType = SeqBeginTypeStr;
1978 auto AddType = [&](StringRef ToAdd) {
1979 if (LastAddedType != ToAdd && ToAdd != SeqEndTypeStr) {
1980 OS <<
" -> '" << ToAdd <<
"'";
1981 LastAddedType = ToAdd.str();
1985 for (QualType InvolvedType : Conv.getInvolvedTypesInSequence())
1987 AddType(InvolvedType.getAsString(
PP));
1989 if (LastAddedType != DestinationTypeAsDiagnosed) {
1990 OS <<
" -> '" << DestinationTypeAsDiagnosed <<
"'";
1991 LastAddedType = DestinationTypeAsDiagnosed.str();
1998 if (DestinationTypeAsDiagnosed != SeqEndTypeStr) {
1999 OS <<
" (as '" << SeqEndTypeStr <<
"')";
2000 LastAddedType = SeqEndTypeStr;
2004 if (
Trivial && NumElementsAdded > 2)
2014template <
typename E, std::
size_t N>
class InsertOnce {
2015 llvm::SmallSet<E, N> CalledWith;
2018 bool operator()(
E El) {
return CalledWith.insert(std::move(El)).second; }
2020 bool calledWith(
const E &El)
const {
return CalledWith.contains(El); }
2023struct SwappedEqualQualTypePair {
2026 bool operator==(
const SwappedEqualQualTypePair &Other)
const {
2031 bool operator<(
const SwappedEqualQualTypePair &Other)
const {
2036struct TypeAliasDiagnosticTuple {
2039 bool operator==(
const TypeAliasDiagnosticTuple &Other)
const {
2045 bool operator<(
const TypeAliasDiagnosticTuple &Other)
const {
2052class UniqueTypeAliasDiagnosticHelper
2053 :
public InsertOnce<TypeAliasDiagnosticTuple, 8> {
2054 using Base = InsertOnce<TypeAliasDiagnosticTuple, 8>;
2064 if (!Base::operator()(ThreeTuple))
2069 if (AlreadySaidLHSAndCommonIsSame && AlreadySaidRHSAndCommonIsSame) {
2087 IgnoredParameterNames(
optutils::parseStringList(
2089 IgnoredParameterTypeSuffixes(
optutils::parseStringList(
2090 Options.get(
"IgnoredParameterTypeSuffixes",
2093 ModelImplicitConversions(Options.get(
"ModelImplicitConversions",
2095 SuppressParametersUsedTogether(
2096 Options.get(
"SuppressParametersUsedTogether",
2098 NamePrefixSuffixSilenceDissimilarityTreshold(
2099 Options.get(
"NamePrefixSuffixSilenceDissimilarityTreshold",
2113 Options.
store(Opts,
"NamePrefixSuffixSilenceDissimilarityTreshold",
2118 const auto BaseConstraints = functionDecl(
2122 unless(isOverloadedUnaryOrBinaryOperator()));
2125 functionDecl(BaseConstraints,
2126 unless(ast_matchers::isTemplateInstantiation()))
2130 functionDecl(BaseConstraints, isExplicitTemplateSpecialization())
2136 const MatchFinder::MatchResult &Result) {
2137 using namespace model;
2138 using namespace filter;
2140 const auto *FD = Result.Nodes.getNodeAs<FunctionDecl>(
"func");
2143 const PrintingPolicy &
PP = FD->getASTContext().getPrintingPolicy();
2144 std::size_t NumParams = FD->getNumParams();
2145 std::size_t MixableRangeStartIndex = 0;
2152 LLVM_DEBUG(llvm::dbgs() <<
"Begin analysis of " <<
getName(FD) <<
" with "
2153 << NumParams <<
" parameters...\n");
2154 while (MixableRangeStartIndex < NumParams) {
2155 if (isIgnoredParameter(*
this, FD->getParamDecl(MixableRangeStartIndex))) {
2156 LLVM_DEBUG(llvm::dbgs()
2157 <<
"Parameter #" << MixableRangeStartIndex <<
" ignored.\n");
2158 ++MixableRangeStartIndex;
2162 MixableParameterRange R = modelMixingRange(
2163 *
this, FD, MixableRangeStartIndex, UsageBasedSuppressor);
2164 assert(R.NumParamsChecked > 0 &&
"Ensure forward progress!");
2165 MixableRangeStartIndex += R.NumParamsChecked;
2167 LLVM_DEBUG(llvm::dbgs() <<
"Ignoring range of " << R.NumParamsChecked
2168 <<
" lower than limit.\n");
2173 bool HasAnyImplicits =
2175 const ParmVarDecl *First = R.getFirstParam(), *Last = R.getLastParam();
2176 std::string FirstParamTypeAsWritten = First->getType().getAsString(
PP);
2180 if (HasAnyImplicits)
2181 DiagText =
"%0 adjacent parameters of %1 of convertible types are "
2182 "easily swapped by mistake";
2183 else if (NeedsAnyTypeNote)
2184 DiagText =
"%0 adjacent parameters of %1 of similar type are easily "
2185 "swapped by mistake";
2187 DiagText =
"%0 adjacent parameters of %1 of similar type ('%2') are "
2188 "easily swapped by mistake";
2190 auto Diag =
diag(First->getOuterLocStart(), DiagText)
2191 <<
static_cast<unsigned>(R.NumParamsChecked) << FD;
2192 if (!NeedsAnyTypeNote)
2193 Diag << FirstParamTypeAsWritten;
2195 CharSourceRange HighlightRange = CharSourceRange::getTokenRange(
2196 First->getBeginLoc(), Last->getEndLoc());
2197 Diag << HighlightRange;
2203 diag(First->getLocation(),
"the first parameter in the range is '%0'",
2204 DiagnosticIDs::Note)
2206 << CharSourceRange::getTokenRange(First->getLocation(),
2207 First->getLocation());
2208 diag(Last->getLocation(),
"the last parameter in the range is '%0'",
2209 DiagnosticIDs::Note)
2211 << CharSourceRange::getTokenRange(Last->getLocation(),
2212 Last->getLocation());
2216 UniqueTypeAliasDiagnosticHelper UniqueTypeAlias;
2217 InsertOnce<SwappedEqualQualTypePair, 8> UniqueBindPower;
2218 InsertOnce<SwappedEqualQualTypePair, 8> UniqueImplicitConversion;
2221 assert(
M.mixable() &&
"Sentinel or false mix in result.");
2228 const ParmVarDecl *LVar =
M.First;
2229 const ParmVarDecl *RVar =
M.Second;
2230 QualType LType = LVar->getType();
2231 QualType RType = RVar->getType();
2233 std::string LTypeStr = LType.getAsString(
PP);
2234 std::string RTypeStr = RType.getAsString(
PP);
2235 std::string CommonTypeStr =
CommonType.getAsString(
PP);
2237 if (hasFlag(
M.flags(), MixFlags::TypeAlias) &&
2240 bool ExplicitlyPrintCommonType =
false;
2241 if (LTypeStr == CommonTypeStr || RTypeStr == CommonTypeStr) {
2242 if (hasFlag(
M.flags(), MixFlags::Qualifiers))
2243 DiagText =
"after resolving type aliases, '%0' and '%1' share a "
2247 "after resolving type aliases, '%0' and '%1' are the same";
2249 DiagText =
"after resolving type aliases, the common type of '%0' "
2251 ExplicitlyPrintCommonType =
true;
2255 diag(LVar->getOuterLocStart(), DiagText, DiagnosticIDs::Note)
2256 << LTypeStr << RTypeStr;
2257 if (ExplicitlyPrintCommonType)
2258 Diag << CommonTypeStr;
2261 if ((hasFlag(
M.flags(), MixFlags::ReferenceBind) ||
2262 hasFlag(
M.flags(), MixFlags::Qualifiers)) &&
2263 UniqueBindPower({LType, RType})) {
2264 StringRef DiagText =
"'%0' and '%1' parameters accept and bind the "
2265 "same kind of values";
2266 diag(RVar->getOuterLocStart(), DiagText, DiagnosticIDs::Note)
2267 << LTypeStr << RTypeStr;
2271 UniqueImplicitConversion({LType, RType})) {
2273 M.leftToRightConversionSequence();
2275 M.rightToLeftConversionSequence();
2276 FormattedConversionSequence LTRFmt{
PP, LTypeStr, LTR, RTypeStr};
2277 FormattedConversionSequence RTLFmt{
PP, RTypeStr, RTL, LTypeStr};
2279 StringRef DiagText =
"'%0' and '%1' may be implicitly converted";
2280 if (!LTRFmt.Trivial || !RTLFmt.Trivial)
2281 DiagText =
"'%0' and '%1' may be implicitly converted: %2, %3";
2285 diag(RVar->getOuterLocStart(), DiagText, DiagnosticIDs::Note)
2286 << LTypeStr << RTypeStr;
2288 if (!LTRFmt.Trivial || !RTLFmt.Trivial)
2289 Diag << LTRFmt.DiagnosticText << RTLFmt.DiagnosticText;
2292 StringRef ConversionFunctionDiagText =
2293 "the implicit conversion involves the "
2294 "%select{|converting constructor|conversion operator}0 "
2297 diag(LFD->getLocation(), ConversionFunctionDiagText,
2298 DiagnosticIDs::Note)
2302 diag(RFD->getLocation(), ConversionFunctionDiagText,
2303 DiagnosticIDs::Note)
const FunctionDecl * Decl
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.
const CXXMethodDecl * ConversionFun
static constexpr bool DefaultQualifiersMix
The default value for the QualifiersMix check option.
bool HasMixabilityBreakingQualifiers
True if the types are qualified in a way that even after equating or removing local CVR qualification...
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.
Qualifiers CommonQualifiers
The set of equal qualifiers between the two types.
bool Trivial
The formatted sequence is trivial if it is "Ty1 -> Ty2", but Ty1 and Ty2 are the types that are shown...
std::string DiagnosticText
static constexpr llvm::StringLiteral DefaultIgnoredParameterTypeSuffixes
The default value for ignored parameter type suffixes.
static constexpr std::size_t DefaultNamePrefixSuffixSilenceDissimilarityTreshold
The default value for the NamePrefixSuffixSilenceDissimilarityTreshold check option.
::clang::DynTypedNode Node
const google::protobuf::Message & M
const bool Enabled
Whether the heuristic is to be enabled by default.
llvm::raw_string_ostream OS
void store(ClangTidyOptions::OptionMap &Options, StringRef LocalName, StringRef Value) const
Stores an option with the check-local name LocalName with string value Value to Options.
Base class for all clang-tidy checks.
DiagnosticBuilder diag(SourceLocation Loc, StringRef Description, DiagnosticIDs::Level Level=DiagnosticIDs::Warning)
Add a diagnostic with the check's name.
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
Should store all options supported by this check with their current values or default values for opti...
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
ClangTidyChecks that register ASTMatchers should do the actual work in here.
void registerMatchers(ast_matchers::MatchFinder *Finder) override
Override this to register AST matchers with Finder.
const std::vector< StringRef > IgnoredParameterNames
The parameter names (as written in the source text) to be ignored.
const std::size_t NamePrefixSuffixSilenceDissimilarityTreshold
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 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 the same" 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.
AST_MATCHER_P(FunctionDecl, parameterCountGE, unsigned, N)
Matches functions that have at least the specified amount of parameters.
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.
AST_MATCHER(clang::VarDecl, hasConstantDeclaration)
EasilySwappableParametersCheck TheCheck
AST_MATCHER_FUNCTION(ast_matchers::internal::Matcher< Stmt >, paramRefExpr)
Matches DeclRefExprs and their ignorable wrappers to ParmVarDecls.
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.