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 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->getAs<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{ToRecord->getTypeForDecl(), FastQualifiersToApply};
1046 if (Ctx.getLangOpts().CPlusPlus17 && FromPtr && ToPtr) {
1049 const auto *FromFunctionPtr =
1050 FromPtr->getPointeeType()->getAs<FunctionProtoType>();
1051 const auto *ToFunctionPtr =
1052 ToPtr->getPointeeType()->getAs<FunctionProtoType>();
1053 if (FromFunctionPtr && ToFunctionPtr &&
1054 FromFunctionPtr->hasNoexceptExceptionSpec() &&
1055 !ToFunctionPtr->hasNoexceptExceptionSpec()) {
1056 LLVM_DEBUG(llvm::dbgs() <<
"--- approximateStdConv. noexcept function "
1057 "pointer to non-noexcept.\n");
1058 WorkType = QualType{ToPtr, FastQualifiersToApply};
1064 LLVM_DEBUG(llvm::dbgs()
1065 <<
"--- approximateStdConv. Trying qualifier adjustment...\n");
1070 LLVM_DEBUG(llvm::dbgs()
1071 <<
"<<< approximateStdConv. Qualifiers adjusted.\n");
1075 if (WorkType == To) {
1076 LLVM_DEBUG(llvm::dbgs() <<
"<<< approximateStdConv. Reached 'To' type.\n");
1080 LLVM_DEBUG(llvm::dbgs() <<
"<<< approximateStdConv. Did not reach 'To'.\n");
1089class UserDefinedConversionSelector {
1095 struct PreparedConversion {
1100 PreparedConversion(
const CXXMethodDecl *CMD,
MixFlags F,
1101 ConversionSequence S)
1105 UserDefinedConversionSelector(
const TheCheck &Check) : Check(Check) {}
1112 void addConversion(
const CXXMethodDecl *ConvFun, QualType FromType,
1117 Check, FromType, ToType, ConvFun->getASTContext(),
1120 if (!Mix.indicatesMixability())
1123 LLVM_DEBUG(llvm::dbgs() <<
"--- tryConversion. Found viable with flags: "
1125 FlaggedConversions.emplace_back(ConvFun, Mix.Flags, Mix.Conversion);
1130 std::optional<PreparedConversion> operator()()
const {
1131 if (FlaggedConversions.empty()) {
1132 LLVM_DEBUG(llvm::dbgs() <<
"--- selectUserDefinedConv. Empty.\n");
1135 if (FlaggedConversions.size() == 1) {
1136 LLVM_DEBUG(llvm::dbgs() <<
"--- selectUserDefinedConv. Single.\n");
1137 return FlaggedConversions.front();
1140 std::optional<PreparedConversion> BestConversion;
1141 unsigned short HowManyGoodConversions = 0;
1142 for (
const auto &Prepared : FlaggedConversions) {
1143 LLVM_DEBUG(llvm::dbgs() <<
"--- selectUserDefinedConv. Candidate flags: "
1145 if (!BestConversion) {
1146 BestConversion = Prepared;
1147 ++HowManyGoodConversions;
1151 bool BestConversionHasImplicit =
1153 bool ThisConversionHasImplicit =
1155 if (!BestConversionHasImplicit && ThisConversionHasImplicit)
1159 if (BestConversionHasImplicit && !ThisConversionHasImplicit) {
1164 BestConversion = Prepared;
1165 HowManyGoodConversions = 1;
1169 if (BestConversionHasImplicit == ThisConversionHasImplicit)
1172 ++HowManyGoodConversions;
1175 if (HowManyGoodConversions == 1) {
1176 LLVM_DEBUG(llvm::dbgs()
1177 <<
"--- selectUserDefinedConv. Unique result. Flags: "
1179 return BestConversion;
1182 LLVM_DEBUG(llvm::dbgs()
1183 <<
"--- selectUserDefinedConv. No, or ambiguous.\n");
1188 llvm::SmallVector<PreparedConversion, 2> FlaggedConversions;
1194static std::optional<ConversionSequence>
1197 if (!RD || !RD->isCompleteDefinition())
1199 RD = RD->getDefinition();
1201 LLVM_DEBUG(llvm::dbgs() <<
">>> tryConversionOperators: " << RD->getName()
1203 ToType.dump(llvm::dbgs(), RD->getASTContext());
1204 llvm::dbgs() <<
'\n';);
1206 UserDefinedConversionSelector ConversionSet{Check};
1208 for (
const NamedDecl *Method : RD->getVisibleConversionFunctions()) {
1209 const auto *Con = dyn_cast<CXXConversionDecl>(Method);
1210 if (!Con || Con->isExplicit())
1212 LLVM_DEBUG(llvm::dbgs() <<
"--- tryConversionOperators. Trying:\n";
1213 Con->dump(llvm::dbgs()); llvm::dbgs() <<
'\n';);
1217 ConversionSet.addConversion(Con, Con->getConversionType(), ToType);
1220 if (std::optional<UserDefinedConversionSelector::PreparedConversion>
1221 SelectedConversion = ConversionSet()) {
1222 QualType RecordType{RD->getTypeForDecl(), 0};
1229 Result.AfterSecondStandard = SelectedConversion->Seq.AfterFirstStandard;
1232 ConvOp.
Fun = cast<CXXConversionDecl>(SelectedConversion->ConversionFun);
1235 Result.setConversion(ConvOp);
1237 LLVM_DEBUG(llvm::dbgs() <<
"<<< tryConversionOperators. Found result.\n");
1241 LLVM_DEBUG(llvm::dbgs() <<
"<<< tryConversionOperators. No conversion.\n");
1245static std::optional<ConversionSequence>
1247 const CXXRecordDecl *RD) {
1248 if (!RD || !RD->isCompleteDefinition())
1250 RD = RD->getDefinition();
1252 LLVM_DEBUG(llvm::dbgs() <<
">>> tryConveringConstructors: " << RD->getName()
1254 FromType.dump(llvm::dbgs(), RD->getASTContext());
1255 llvm::dbgs() <<
'\n';);
1257 UserDefinedConversionSelector ConversionSet{Check};
1259 for (
const CXXConstructorDecl *Con : RD->ctors()) {
1260 if (Con->isCopyOrMoveConstructor() ||
1261 !Con->isConvertingConstructor(
false))
1263 LLVM_DEBUG(llvm::dbgs() <<
"--- tryConvertingConstructors. Trying:\n";
1264 Con->dump(llvm::dbgs()); llvm::dbgs() <<
'\n';);
1268 ConversionSet.addConversion(Con, FromType, Con->getParamDecl(0)->getType());
1271 if (std::optional<UserDefinedConversionSelector::PreparedConversion>
1272 SelectedConversion = ConversionSet()) {
1273 QualType RecordType{RD->getTypeForDecl(), 0};
1276 Result.AfterFirstStandard = SelectedConversion->Seq.AfterFirstStandard;
1279 Ctor.
Fun = cast<CXXConstructorDecl>(SelectedConversion->ConversionFun);
1282 Result.setConversion(Ctor);
1284 LLVM_DEBUG(llvm::dbgs()
1285 <<
"<<< tryConvertingConstructors. Found result.\n");
1289 LLVM_DEBUG(llvm::dbgs() <<
"<<< tryConvertingConstructors. No conversion.\n");
1300 QualType RType,
const ASTContext &Ctx,
1302 LLVM_DEBUG(llvm::dbgs() <<
">>> approximateImplicitConversion for LType:\n";
1303 LType.dump(llvm::dbgs(), Ctx); llvm::dbgs() <<
"\nand RType:\n";
1304 RType.dump(llvm::dbgs(), Ctx);
1305 llvm::dbgs() <<
"\nimplicit mode: ";
switch (ImplicitMode) {
1307 llvm::dbgs() <<
"None";
1310 llvm::dbgs() <<
"All";
1313 llvm::dbgs() <<
"OneWay, Single, STD Only";
1315 } llvm::dbgs() <<
'\n';);
1324 QualType WorkType = LType;
1326 std::optional<QualType> AfterFirstStdConv =
1328 if (AfterFirstStdConv) {
1329 LLVM_DEBUG(llvm::dbgs() <<
"--- approximateImplicitConversion. Standard "
1330 "Pre-Conversion found!\n");
1331 ImplicitSeq.AfterFirstStandard = *AfterFirstStdConv;
1332 WorkType = ImplicitSeq.AfterFirstStandard;
1337 return {ImplicitSeq.AfterFirstStandard.isNull()
1342 if (Ctx.getLangOpts().CPlusPlus) {
1343 bool FoundConversionOperator =
false, FoundConvertingCtor =
false;
1345 if (
const auto *LRD = WorkType->getAsCXXRecordDecl()) {
1346 std::optional<ConversionSequence> ConversionOperatorResult =
1348 if (ConversionOperatorResult) {
1349 LLVM_DEBUG(llvm::dbgs() <<
"--- approximateImplicitConversion. Found "
1350 "conversion operator.\n");
1351 ImplicitSeq.update(*ConversionOperatorResult);
1352 WorkType = ImplicitSeq.getTypeAfterUserDefinedConversion();
1353 FoundConversionOperator =
true;
1357 if (
const auto *RRD = RType->getAsCXXRecordDecl()) {
1361 std::optional<ConversionSequence> ConvCtorResult =
1363 if (ConvCtorResult) {
1364 LLVM_DEBUG(llvm::dbgs() <<
"--- approximateImplicitConversion. Found "
1365 "converting constructor.\n");
1366 ImplicitSeq.update(*ConvCtorResult);
1367 WorkType = ImplicitSeq.getTypeAfterUserDefinedConversion();
1368 FoundConvertingCtor =
true;
1372 if (FoundConversionOperator && FoundConvertingCtor) {
1374 LLVM_DEBUG(llvm::dbgs()
1375 <<
"<<< approximateImplicitConversion. Found both "
1376 "user-defined conversion kinds in the same sequence!\n");
1385 <<
"--- approximateImplicitConversion. Try to find post-conversion.\n");
1387 Check, WorkType, RType, Ctx,
1390 LLVM_DEBUG(llvm::dbgs() <<
"--- approximateImplicitConversion. Standard "
1391 "Post-Conversion found!\n");
1396 ImplicitSeq.AfterSecondStandard =
1398 WorkType = ImplicitSeq.AfterSecondStandard;
1402 LLVM_DEBUG(llvm::dbgs()
1403 <<
"<<< approximateImplicitConversion. Found a conversion.\n");
1408 llvm::dbgs() <<
"<<< approximateImplicitConversion. No match found.\n");
1413 const TheCheck &Check,
const FunctionDecl *FD, std::size_t StartIndex,
1415 std::size_t NumParams = FD->getNumParams();
1416 assert(StartIndex < NumParams &&
"out of bounds for start");
1417 const ASTContext &Ctx = FD->getASTContext();
1423 for (std::size_t I = StartIndex + 1; I < NumParams; ++I) {
1424 const ParmVarDecl *Ith = FD->getParamDecl(I);
1425 StringRef ParamName = Ith->getName();
1426 LLVM_DEBUG(llvm::dbgs()
1427 <<
"Check param #" << I <<
" '" << ParamName <<
"'...\n");
1429 LLVM_DEBUG(llvm::dbgs() <<
"Param #" << I <<
" is ignored. Break!\n");
1433 StringRef PrevParamName = FD->getParamDecl(I - 1)->getName();
1434 if (!ParamName.empty() && !PrevParamName.empty() &&
1436 Check.NamePrefixSuffixSilenceDissimilarityTreshold, PrevParamName,
1438 LLVM_DEBUG(llvm::dbgs() <<
"Parameter '" << ParamName
1439 <<
"' follows a pattern with previous parameter '"
1440 << PrevParamName <<
"'. Break!\n");
1447 for (std::size_t J = StartIndex; J < I; ++J) {
1448 const ParmVarDecl *Jth = FD->getParamDecl(J);
1449 LLVM_DEBUG(llvm::dbgs()
1450 <<
"Check mix of #" << J <<
" against #" << I <<
"...\n");
1452 if (isSimilarlyUsedParameter(UsageBasedSuppressor, Ith, Jth)) {
1455 LLVM_DEBUG(llvm::dbgs() <<
"Parameters #" << I <<
" and #" << J
1456 <<
" deemed related, ignoring...\n");
1467 Check.ModelImplicitConversions
1470 LLVM_DEBUG(llvm::dbgs() <<
"Mix flags (raw) : "
1473 LLVM_DEBUG(llvm::dbgs() <<
"Mix flags (after sanitize): "
1476 assert(
M.flagsValid() &&
"All flags decayed!");
1479 MixesOfIth.emplace_back(std::move(
M));
1482 if (MixesOfIth.empty()) {
1485 LLVM_DEBUG(llvm::dbgs()
1487 <<
" does not mix with any in the current range. Break!\n");
1491 Ret.
Mixes.insert(Ret.
Mixes.end(), MixesOfIth.begin(), MixesOfIth.end());
1502 return expr(ignoringParenImpCasts(ignoringElidableConstructorCall(
1503 declRefExpr(to(parmVarDecl().bind(
"param"))))));
1511 LLVM_DEBUG(llvm::dbgs() <<
"Checking if '" <<
Node->getName()
1512 <<
"' is ignored.\n");
1514 if (!
Node->getIdentifier())
1515 return llvm::is_contained(Check.IgnoredParameterNames,
"\"\"");
1517 StringRef NodeName =
Node->getName();
1518 if (llvm::is_contained(Check.IgnoredParameterNames, NodeName)) {
1519 LLVM_DEBUG(llvm::dbgs() <<
"\tName ignored.\n");
1523 StringRef NodeTypeName = [
Node] {
1524 const ASTContext &Ctx =
Node->getASTContext();
1525 const SourceManager &SM = Ctx.getSourceManager();
1526 SourceLocation B =
Node->getTypeSpecStartLoc();
1527 SourceLocation
E =
Node->getTypeSpecEndLoc();
1530 LLVM_DEBUG(llvm::dbgs() <<
"\tType name code is '"
1531 << Lexer::getSourceText(
1532 CharSourceRange::getTokenRange(B,
E), SM, LO)
1534 if (B.isMacroID()) {
1535 LLVM_DEBUG(llvm::dbgs() <<
"\t\tBeginning is macro.\n");
1536 B = SM.getTopMacroCallerLoc(B);
1538 if (
E.isMacroID()) {
1539 LLVM_DEBUG(llvm::dbgs() <<
"\t\tEnding is macro.\n");
1540 E = Lexer::getLocForEndOfToken(SM.getTopMacroCallerLoc(
E), 0, SM, LO);
1542 LLVM_DEBUG(llvm::dbgs() <<
"\tType name code is '"
1543 << Lexer::getSourceText(
1544 CharSourceRange::getTokenRange(B,
E), SM, LO)
1547 return Lexer::getSourceText(CharSourceRange::getTokenRange(B,
E), SM, LO);
1550 LLVM_DEBUG(llvm::dbgs() <<
"\tType name is '" << NodeTypeName <<
"'\n");
1551 if (!NodeTypeName.empty()) {
1552 if (llvm::any_of(Check.IgnoredParameterTypeSuffixes,
1553 [NodeTypeName](StringRef
E) {
1554 return !E.empty() && NodeTypeName.ends_with(E);
1556 LLVM_DEBUG(llvm::dbgs() <<
"\tType suffix ignored.\n");
1566namespace relatedness_heuristic {
1570template <
typename T, std::
size_t N = SmallDataStructureSize>
1572 llvm::DenseMap<const ParmVarDecl *, llvm::SmallSet<T, N>>;
1576template <
typename MapTy,
typename ElemTy>
1579 auto E1Iterator = Map.find(E1);
1580 auto E2Iterator = Map.find(E2);
1581 if (E1Iterator == Map.end() || E2Iterator == Map.end())
1584 for (
const auto &E1SetElem : E1Iterator->second)
1585 if (E2Iterator->second.contains(E1SetElem))
1596 using Base = RecursiveASTVisitor<AppearsInSameExpr>;
1598 const FunctionDecl *FD;
1599 const Expr *CurrentExprOnlyTreeRoot =
nullptr;
1600 llvm::DenseMap<
const ParmVarDecl *,
1601 llvm::SmallPtrSet<const Expr *, SmallDataStructureSize>>
1602 ParentExprsForParamRefs;
1607 TraverseFunctionDecl(
const_cast<FunctionDecl *
>(FD));
1610 bool operator()(
const ParmVarDecl *Param1,
const ParmVarDecl *Param2)
const {
1616 CurrentExprOnlyTreeRoot =
nullptr;
1617 return Base::TraverseDecl(D);
1621 if (
auto *
E = dyn_cast_or_null<Expr>(S)) {
1622 bool RootSetInCurrentStackFrame =
false;
1623 if (!CurrentExprOnlyTreeRoot) {
1624 CurrentExprOnlyTreeRoot =
E;
1625 RootSetInCurrentStackFrame =
true;
1628 bool Ret = Base::TraverseStmt(S);
1630 if (RootSetInCurrentStackFrame)
1631 CurrentExprOnlyTreeRoot =
nullptr;
1637 CurrentExprOnlyTreeRoot =
nullptr;
1638 return Base::TraverseStmt(S);
1642 if (!CurrentExprOnlyTreeRoot)
1645 if (
auto *PVD = dyn_cast<ParmVarDecl>(DRE->getDecl()))
1646 if (llvm::find(FD->parameters(), PVD))
1647 ParentExprsForParamRefs[PVD].insert(CurrentExprOnlyTreeRoot);
1662 auto ParamsAsArgsInFnCalls =
1663 match(functionDecl(forEachDescendant(
1664 callExpr(forEachArgumentWithParam(
1665 paramRefExpr(), parmVarDecl().bind(
"passed-to")))
1666 .bind(
"call-expr"))),
1667 *FD, FD->getASTContext());
1668 for (
const auto &Match : ParamsAsArgsInFnCalls) {
1669 const auto *PassedParamOfThisFn = Match.getNodeAs<ParmVarDecl>(
"param");
1670 const auto *
CE = Match.getNodeAs<CallExpr>(
"call-expr");
1671 const auto *PassedToParam = Match.getNodeAs<ParmVarDecl>(
"passed-to");
1672 assert(PassedParamOfThisFn &&
CE && PassedToParam);
1674 const FunctionDecl *CalledFn =
CE->getDirectCallee();
1678 std::optional<unsigned> TargetIdx;
1679 unsigned NumFnParams = CalledFn->getNumParams();
1680 for (
unsigned Idx = 0; Idx < NumFnParams; ++Idx)
1681 if (CalledFn->getParamDecl(Idx) == PassedToParam)
1682 TargetIdx.emplace(Idx);
1684 assert(TargetIdx &&
"Matched, but didn't find index?");
1685 TargetParams[PassedParamOfThisFn].insert(
1686 {CalledFn->getCanonicalDecl(), *TargetIdx});
1690 bool operator()(
const ParmVarDecl *Param1,
const ParmVarDecl *Param2)
const {
1702 auto MembersCalledOnParams = match(
1703 functionDecl(forEachDescendant(
1704 memberExpr(hasObjectExpression(paramRefExpr())).bind(
"mem-expr"))),
1705 *FD, FD->getASTContext());
1707 for (
const auto &Match : MembersCalledOnParams) {
1708 const auto *AccessedParam = Match.getNodeAs<ParmVarDecl>(
"param");
1709 const auto *ME = Match.getNodeAs<MemberExpr>(
"mem-expr");
1710 assert(AccessedParam && ME);
1711 AccessedMembers[AccessedParam].insert(
1712 ME->getMemberDecl()->getCanonicalDecl());
1716 bool operator()(
const ParmVarDecl *Param1,
const ParmVarDecl *Param2)
const {
1724 llvm::SmallVector<const ParmVarDecl *, SmallDataStructureSize> ReturnedParams;
1729 auto ParamReturns = match(functionDecl(forEachDescendant(
1730 returnStmt(hasReturnValue(paramRefExpr())))),
1731 *FD, FD->getASTContext());
1732 for (
const auto &Match : ParamReturns) {
1733 const auto *ReturnedParam = Match.getNodeAs<ParmVarDecl>(
"param");
1734 assert(ReturnedParam);
1736 if (find(FD->parameters(), ReturnedParam) == FD->param_end())
1742 ReturnedParams.emplace_back(ReturnedParam);
1746 bool operator()(
const ParmVarDecl *Param1,
const ParmVarDecl *Param2)
const {
1747 return llvm::is_contained(ReturnedParams, Param1) &&
1748 llvm::is_contained(ReturnedParams, Param2);
1770 PassToFun.
setup(FD);
1771 SameMember.
setup(FD);
1777 bool operator()(
const ParmVarDecl *Param1,
const ParmVarDecl *Param2)
const {
1781 LLVM_DEBUG(llvm::dbgs()
1782 <<
"::: Matching similar usage / relatedness heuristic...\n");
1784 if (SameExpr(Param1, Param2)) {
1785 LLVM_DEBUG(llvm::dbgs() <<
"::: Used in the same expression.\n");
1789 if (PassToFun(Param1, Param2)) {
1790 LLVM_DEBUG(llvm::dbgs()
1791 <<
"::: Passed to same function in different calls.\n");
1795 if (SameMember(Param1, Param2)) {
1796 LLVM_DEBUG(llvm::dbgs()
1797 <<
"::: Same member field access or method called.\n");
1801 if (Returns(Param1, Param2)) {
1802 LLVM_DEBUG(llvm::dbgs() <<
"::: Both parameter returned.\n");
1806 LLVM_DEBUG(llvm::dbgs() <<
"::: None.\n");
1815 const ParmVarDecl *Param1,
const ParmVarDecl *Param2) {
1816 return Suppressor(Param1, Param2);
1820 while (Str.size() < ToLen)
1821 Str.emplace_back(
'\0');
1825 while (Str.size() < ToLen)
1826 Str.insert(Str.begin(),
'\0');
1831 assert(S1.size() >= N && S2.size() >= N);
1832 StringRef S1Prefix = S1.take_front(S1.size() - N),
1833 S2Prefix = S2.take_front(S2.size() - N);
1834 return S1Prefix == S2Prefix && !S1Prefix.empty();
1839 assert(S1.size() >= N && S2.size() >= N);
1840 StringRef S1Suffix = S1.take_back(S1.size() - N),
1841 S2Suffix = S2.take_back(S2.size() - N);
1842 return S1Suffix == S2Suffix && !S1Suffix.empty();
1848 StringRef Str1, StringRef Str2) {
1853 std::size_t BiggerLength = std::max(Str1.size(), Str2.size());
1855 if (BiggerLength <= Threshold)
1862 SmallString<32> S1PadE{Str1}, S2PadE{Str2};
1867 Threshold, StringRef{S1PadE.begin(), BiggerLength},
1868 StringRef{S2PadE.begin(), BiggerLength}))
1871 SmallString<32> S1PadB{Str1}, S2PadB{Str2};
1876 Threshold, StringRef{S1PadB.begin(), BiggerLength},
1877 StringRef{S2PadB.begin(), BiggerLength}))
1887 return Node.getNumParams() >= N;
1892 switch (
Node.getOverloadedOperator()) {
1897 case OO_Array_Delete:
1898 case OO_Conditional:
1903 return Node.getNumParams() <= 2;
1916static SmallString<64>
getName(
const NamedDecl *ND) {
1917 SmallString<64>
Name;
1918 llvm::raw_svector_ostream
OS{
Name};
1919 ND->getNameForDiagnostic(
OS, ND->getASTContext().getPrintingPolicy(),
false);
1934 using namespace model;
1935 return static_cast<bool>(
1937 (MixFlags::TypeAlias | MixFlags::ReferenceBind | MixFlags::Qualifiers));
1950struct FormattedConversionSequence {
1958 FormattedConversionSequence(
const PrintingPolicy &
PP,
1959 StringRef StartTypeAsDiagnosed,
1960 const model::ConversionSequence &Conv,
1961 StringRef DestinationTypeAsDiagnosed) {
1965 OS <<
'\'' << StartTypeAsDiagnosed <<
'\'';
1966 std::string LastAddedType = StartTypeAsDiagnosed.str();
1967 std::size_t NumElementsAdded = 1;
1971 std::string SeqBeginTypeStr = Conv.Begin.getAsString(
PP);
1972 std::string SeqEndTypeStr = Conv.End.getAsString(
PP);
1973 if (StartTypeAsDiagnosed != SeqBeginTypeStr) {
1974 OS <<
" (as '" << SeqBeginTypeStr <<
"')";
1975 LastAddedType = SeqBeginTypeStr;
1979 auto AddType = [&](StringRef ToAdd) {
1980 if (LastAddedType != ToAdd && ToAdd != SeqEndTypeStr) {
1981 OS <<
" -> '" << ToAdd <<
"'";
1982 LastAddedType = ToAdd.str();
1986 for (QualType InvolvedType : Conv.getInvolvedTypesInSequence())
1988 AddType(InvolvedType.getAsString(
PP));
1990 if (LastAddedType != DestinationTypeAsDiagnosed) {
1991 OS <<
" -> '" << DestinationTypeAsDiagnosed <<
"'";
1992 LastAddedType = DestinationTypeAsDiagnosed.str();
1999 if (DestinationTypeAsDiagnosed != SeqEndTypeStr) {
2000 OS <<
" (as '" << SeqEndTypeStr <<
"')";
2001 LastAddedType = SeqEndTypeStr;
2005 if (
Trivial && NumElementsAdded > 2)
2015template <
typename E, std::
size_t N>
class InsertOnce {
2016 llvm::SmallSet<E, N> CalledWith;
2019 bool operator()(
E El) {
return CalledWith.insert(std::move(El)).second; }
2021 bool calledWith(
const E &El)
const {
return CalledWith.contains(El); }
2024struct SwappedEqualQualTypePair {
2027 bool operator==(
const SwappedEqualQualTypePair &Other)
const {
2032 bool operator<(
const SwappedEqualQualTypePair &Other)
const {
2037struct TypeAliasDiagnosticTuple {
2040 bool operator==(
const TypeAliasDiagnosticTuple &Other)
const {
2046 bool operator<(
const TypeAliasDiagnosticTuple &Other)
const {
2053class UniqueTypeAliasDiagnosticHelper
2054 :
public InsertOnce<TypeAliasDiagnosticTuple, 8> {
2055 using Base = InsertOnce<TypeAliasDiagnosticTuple, 8>;
2065 if (!Base::operator()(ThreeTuple))
2070 if (AlreadySaidLHSAndCommonIsSame && AlreadySaidRHSAndCommonIsSame) {
2088 IgnoredParameterNames(
optutils::parseStringList(
2090 IgnoredParameterTypeSuffixes(
optutils::parseStringList(
2091 Options.get(
"IgnoredParameterTypeSuffixes",
2094 ModelImplicitConversions(Options.get(
"ModelImplicitConversions",
2096 SuppressParametersUsedTogether(
2097 Options.get(
"SuppressParametersUsedTogether",
2099 NamePrefixSuffixSilenceDissimilarityTreshold(
2100 Options.get(
"NamePrefixSuffixSilenceDissimilarityTreshold",
2114 Options.
store(Opts,
"NamePrefixSuffixSilenceDissimilarityTreshold",
2119 const auto BaseConstraints = functionDecl(
2123 unless(isOverloadedUnaryOrBinaryOperator()));
2126 functionDecl(BaseConstraints,
2127 unless(ast_matchers::isTemplateInstantiation()))
2131 functionDecl(BaseConstraints, isExplicitTemplateSpecialization())
2137 const MatchFinder::MatchResult &Result) {
2138 using namespace model;
2139 using namespace filter;
2141 const auto *FD = Result.Nodes.getNodeAs<FunctionDecl>(
"func");
2144 const PrintingPolicy &
PP = FD->getASTContext().getPrintingPolicy();
2145 std::size_t NumParams = FD->getNumParams();
2146 std::size_t MixableRangeStartIndex = 0;
2153 LLVM_DEBUG(llvm::dbgs() <<
"Begin analysis of " <<
getName(FD) <<
" with "
2154 << NumParams <<
" parameters...\n");
2155 while (MixableRangeStartIndex < NumParams) {
2156 if (isIgnoredParameter(*
this, FD->getParamDecl(MixableRangeStartIndex))) {
2157 LLVM_DEBUG(llvm::dbgs()
2158 <<
"Parameter #" << MixableRangeStartIndex <<
" ignored.\n");
2159 ++MixableRangeStartIndex;
2163 MixableParameterRange R = modelMixingRange(
2164 *
this, FD, MixableRangeStartIndex, UsageBasedSuppressor);
2165 assert(R.NumParamsChecked > 0 &&
"Ensure forward progress!");
2166 MixableRangeStartIndex += R.NumParamsChecked;
2168 LLVM_DEBUG(llvm::dbgs() <<
"Ignoring range of " << R.NumParamsChecked
2169 <<
" lower than limit.\n");
2174 bool HasAnyImplicits =
2176 const ParmVarDecl *First = R.getFirstParam(), *Last = R.getLastParam();
2177 std::string FirstParamTypeAsWritten = First->getType().getAsString(
PP);
2181 if (HasAnyImplicits)
2182 DiagText =
"%0 adjacent parameters of %1 of convertible types are "
2183 "easily swapped by mistake";
2184 else if (NeedsAnyTypeNote)
2185 DiagText =
"%0 adjacent parameters of %1 of similar type are easily "
2186 "swapped by mistake";
2188 DiagText =
"%0 adjacent parameters of %1 of similar type ('%2') are "
2189 "easily swapped by mistake";
2191 auto Diag =
diag(First->getOuterLocStart(), DiagText)
2192 <<
static_cast<unsigned>(R.NumParamsChecked) << FD;
2193 if (!NeedsAnyTypeNote)
2194 Diag << FirstParamTypeAsWritten;
2196 CharSourceRange HighlightRange = CharSourceRange::getTokenRange(
2197 First->getBeginLoc(), Last->getEndLoc());
2198 Diag << HighlightRange;
2204 diag(First->getLocation(),
"the first parameter in the range is '%0'",
2205 DiagnosticIDs::Note)
2207 << CharSourceRange::getTokenRange(First->getLocation(),
2208 First->getLocation());
2209 diag(Last->getLocation(),
"the last parameter in the range is '%0'",
2210 DiagnosticIDs::Note)
2212 << CharSourceRange::getTokenRange(Last->getLocation(),
2213 Last->getLocation());
2217 UniqueTypeAliasDiagnosticHelper UniqueTypeAlias;
2218 InsertOnce<SwappedEqualQualTypePair, 8> UniqueBindPower;
2219 InsertOnce<SwappedEqualQualTypePair, 8> UniqueImplicitConversion;
2222 assert(
M.mixable() &&
"Sentinel or false mix in result.");
2229 const ParmVarDecl *LVar =
M.First;
2230 const ParmVarDecl *RVar =
M.Second;
2231 QualType LType = LVar->getType();
2232 QualType RType = RVar->getType();
2234 std::string LTypeStr = LType.getAsString(
PP);
2235 std::string RTypeStr = RType.getAsString(
PP);
2236 std::string CommonTypeStr =
CommonType.getAsString(
PP);
2238 if (hasFlag(
M.flags(), MixFlags::TypeAlias) &&
2241 bool ExplicitlyPrintCommonType =
false;
2242 if (LTypeStr == CommonTypeStr || RTypeStr == CommonTypeStr) {
2243 if (hasFlag(
M.flags(), MixFlags::Qualifiers))
2244 DiagText =
"after resolving type aliases, '%0' and '%1' share a "
2248 "after resolving type aliases, '%0' and '%1' are the same";
2250 DiagText =
"after resolving type aliases, the common type of '%0' "
2252 ExplicitlyPrintCommonType =
true;
2256 diag(LVar->getOuterLocStart(), DiagText, DiagnosticIDs::Note)
2257 << LTypeStr << RTypeStr;
2258 if (ExplicitlyPrintCommonType)
2259 Diag << CommonTypeStr;
2262 if ((hasFlag(
M.flags(), MixFlags::ReferenceBind) ||
2263 hasFlag(
M.flags(), MixFlags::Qualifiers)) &&
2264 UniqueBindPower({LType, RType})) {
2265 StringRef DiagText =
"'%0' and '%1' parameters accept and bind the "
2266 "same kind of values";
2267 diag(RVar->getOuterLocStart(), DiagText, DiagnosticIDs::Note)
2268 << LTypeStr << RTypeStr;
2272 UniqueImplicitConversion({LType, RType})) {
2274 M.leftToRightConversionSequence();
2276 M.rightToLeftConversionSequence();
2277 FormattedConversionSequence LTRFmt{
PP, LTypeStr, LTR, RTypeStr};
2278 FormattedConversionSequence RTLFmt{
PP, RTypeStr, RTL, LTypeStr};
2280 StringRef DiagText =
"'%0' and '%1' may be implicitly converted";
2281 if (!LTRFmt.Trivial || !RTLFmt.Trivial)
2282 DiagText =
"'%0' and '%1' may be implicitly converted: %2, %3";
2286 diag(RVar->getOuterLocStart(), DiagText, DiagnosticIDs::Note)
2287 << LTypeStr << RTypeStr;
2289 if (!LTRFmt.Trivial || !RTLFmt.Trivial)
2290 Diag << LTRFmt.DiagnosticText << RTLFmt.DiagnosticText;
2293 StringRef ConversionFunctionDiagText =
2294 "the implicit conversion involves the "
2295 "%select{|converting constructor|conversion operator}0 "
2298 diag(LFD->getLocation(), ConversionFunctionDiagText,
2299 DiagnosticIDs::Note)
2303 diag(RFD->getLocation(), ConversionFunctionDiagText,
2304 DiagnosticIDs::Note)
const FunctionDecl * Decl
llvm::SmallString< 256U > Name
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.
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.