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"
72 "reverse_const_iterator;"
73 "ConstReverseIterator;"
74 "Const_Reverse_Iterator;"
75 "const_reverse_iterator;"
76 "Constreverseiterator;"
77 "constreverseiterator";
91static constexpr std::size_t
102 const ParmVarDecl *Param1,
const ParmVarDecl *Param2);
104 StringRef Str1, StringRef Str2);
112enum class MixFlags :
unsigned char {
119 WorkaroundDisableCanonicalEquivalence = 1,
129 ImplicitConversion = 128,
132 LLVM_MARK_AS_BITMASK_ENUM(ImplicitConversion)
140static inline bool hasFlag(MixFlags Data, MixFlags SearchedFlag) {
141 assert(SearchedFlag != MixFlags::Invalid &&
142 "can't be used to detect lack of all bits!");
145 return (Data & SearchedFlag) == SearchedFlag;
157 if (F == MixFlags::Invalid)
160 SmallString<8> Str{
"-------"};
162 if (
hasFlag(F, MixFlags::None))
166 if (
hasFlag(F, MixFlags::Trivial))
168 if (
hasFlag(F, MixFlags::Canonical))
170 if (
hasFlag(F, MixFlags::TypeAlias))
172 if (
hasFlag(F, MixFlags::ReferenceBind))
174 if (
hasFlag(F, MixFlags::Qualifiers))
176 if (
hasFlag(F, MixFlags::ImplicitConversion))
179 if (
hasFlag(F, MixFlags::WorkaroundDisableCanonicalEquivalence))
182 return Str.str().str();
232struct ConversionSequence {
233 enum UserDefinedConversionKind { UDCK_None, UDCK_Ctor, UDCK_Oper };
235 struct UserDefinedConvertingConstructor {
236 const CXXConstructorDecl *Fun;
237 QualType ConstructorParameterType;
238 QualType UserDefinedType;
241 struct UserDefinedConversionOperator {
242 const CXXConversionDecl *Fun;
243 QualType UserDefinedType;
244 QualType ConversionOperatorResultType;
251 QualType AfterFirstStandard;
256 UserDefinedConvertingConstructor UDConvCtor;
257 UserDefinedConversionOperator UDConvOp;
259 UserDefinedConversionKind UDConvKind;
263 QualType AfterSecondStandard;
268 ConversionSequence() : None(0), UDConvKind(UDCK_None) {}
269 ConversionSequence(QualType From, QualType To)
270 : Begin(From), None(0), UDConvKind(UDCK_None), End(To) {}
272 explicit operator bool()
const {
273 return !AfterFirstStandard.isNull() || UDConvKind != UDCK_None ||
274 !AfterSecondStandard.isNull();
279 SmallVector<QualType, 4> getInvolvedTypesInSequence()
const {
280 SmallVector<QualType, 4> Ret;
281 auto EmplaceIfDifferent = [&Ret](QualType QT) {
285 Ret.emplace_back(QT);
286 else if (Ret.back() != QT)
287 Ret.emplace_back(QT);
290 EmplaceIfDifferent(AfterFirstStandard);
291 switch (UDConvKind) {
293 EmplaceIfDifferent(UDConvCtor.ConstructorParameterType);
294 EmplaceIfDifferent(UDConvCtor.UserDefinedType);
297 EmplaceIfDifferent(UDConvOp.UserDefinedType);
298 EmplaceIfDifferent(UDConvOp.ConversionOperatorResultType);
303 EmplaceIfDifferent(AfterSecondStandard);
313 ConversionSequence &update(
const ConversionSequence &RHS) {
314 if (!RHS.AfterFirstStandard.isNull())
315 AfterFirstStandard = RHS.AfterFirstStandard;
316 switch (RHS.UDConvKind) {
318 UDConvKind = UDCK_Ctor;
319 UDConvCtor = RHS.UDConvCtor;
322 UDConvKind = UDCK_Oper;
323 UDConvOp = RHS.UDConvOp;
328 if (!RHS.AfterSecondStandard.isNull())
329 AfterSecondStandard = RHS.AfterSecondStandard;
335 void setConversion(
const UserDefinedConvertingConstructor &UDCC) {
336 UDConvKind = UDCK_Ctor;
341 void setConversion(
const UserDefinedConversionOperator &UDCO) {
342 UDConvKind = UDCK_Oper;
348 QualType getTypeAfterUserDefinedConversion()
const {
349 switch (UDConvKind) {
351 return UDConvCtor.UserDefinedType;
353 return UDConvOp.ConversionOperatorResultType;
357 llvm_unreachable(
"Invalid UDConv kind.");
360 const CXXMethodDecl *getUserDefinedConversionFunction()
const {
361 switch (UDConvKind) {
363 return UDConvCtor.Fun;
369 llvm_unreachable(
"Invalid UDConv kind.");
376 SourceRange getUserDefinedConversionHighlight()
const {
377 switch (UDConvKind) {
379 return UDConvCtor.Fun->getParamDecl(0)->getSourceRange();
383 if (
const FunctionTypeLoc FTL = UDConvOp.Fun->getFunctionTypeLoc())
384 if (
const TypeLoc RetLoc = FTL.getReturnLoc())
385 return RetLoc.getSourceRange();
390 llvm_unreachable(
"Invalid UDConv kind.");
398 MixFlags Flags = MixFlags::Invalid;
406 ConversionSequence Conversion, ConversionRTL;
410 bool CreatedFromOneWayConversion =
false;
412 MixData(MixFlags Flags) : Flags(Flags) {}
413 MixData(MixFlags Flags, QualType CommonType)
414 : Flags(Flags), CommonType(CommonType) {}
415 MixData(MixFlags Flags, ConversionSequence Conv)
416 : Flags(Flags), Conversion(Conv), CreatedFromOneWayConversion(true) {}
417 MixData(MixFlags Flags, ConversionSequence LTR, ConversionSequence RTL)
418 : Flags(Flags), Conversion(LTR), ConversionRTL(RTL) {}
419 MixData(MixFlags Flags, QualType CommonType, ConversionSequence LTR,
420 ConversionSequence RTL)
421 : Flags(Flags), CommonType(CommonType), Conversion(LTR),
422 ConversionRTL(RTL) {}
425 assert(Flags != MixFlags::Invalid &&
"sanitize() called on invalid bitvec");
427 const MixFlags CanonicalAndWorkaround =
428 MixFlags::Canonical | MixFlags::WorkaroundDisableCanonicalEquivalence;
429 if ((Flags & CanonicalAndWorkaround) == CanonicalAndWorkaround) {
433 Flags = MixFlags::None;
437 if (
hasFlag(Flags, MixFlags::None)) {
441 Flags = MixFlags::None;
445 if (Flags == MixFlags::Trivial)
448 if (
static_cast<bool>(Flags ^ MixFlags::Trivial))
452 Flags &= ~MixFlags::Trivial;
454 bool ShouldHaveImplicitConvFlag =
false;
455 if (CreatedFromOneWayConversion && Conversion)
456 ShouldHaveImplicitConvFlag =
true;
457 else if (!CreatedFromOneWayConversion && Conversion && ConversionRTL)
461 ShouldHaveImplicitConvFlag =
true;
463 if (ShouldHaveImplicitConvFlag)
464 Flags |= MixFlags::ImplicitConversion;
466 Flags &= ~MixFlags::ImplicitConversion;
469 bool isValid()
const {
return Flags >= MixFlags::None; }
471 bool indicatesMixability()
const {
return Flags > MixFlags::None; }
474 MixData
operator|(MixFlags EnableFlags)
const {
475 if (CreatedFromOneWayConversion) {
476 MixData M{Flags | EnableFlags, Conversion};
477 M.CommonType = CommonType;
480 return {Flags | EnableFlags, CommonType, Conversion, ConversionRTL};
485 Flags |= EnableFlags;
489 template <
typename F> MixData withCommonTypeTransformed(
const F &Func)
const {
490 if (CommonType.isNull())
493 const QualType NewCommonType = Func(CommonType);
495 if (CreatedFromOneWayConversion) {
496 MixData M{Flags, Conversion};
497 M.CommonType = NewCommonType;
501 return {Flags, NewCommonType, Conversion, ConversionRTL};
508 const ParmVarDecl *First, *Second;
511 Mix(
const ParmVarDecl *F,
const ParmVarDecl *S, MixData Data)
512 : First(F), Second(S), Data(std::move(Data)) {}
514 void sanitize() { Data.sanitize(); }
515 MixFlags flags()
const {
return Data.Flags; }
516 bool flagsValid()
const {
return Data.isValid(); }
517 bool mixable()
const {
return Data.indicatesMixability(); }
518 QualType commonUnderlyingType()
const {
return Data.CommonType; }
519 const ConversionSequence &leftToRightConversionSequence()
const {
520 return Data.Conversion;
522 const ConversionSequence &rightToLeftConversionSequence()
const {
523 return Data.ConversionRTL;
528static_assert(std::is_trivially_copyable_v<Mix> &&
529 std::is_trivially_move_constructible_v<Mix> &&
530 std::is_trivially_move_assignable_v<Mix>,
531 "Keep frequently used data simple!");
533struct MixableParameterRange {
535 using MixVector = SmallVector<Mix, 8>;
538 std::size_t NumParamsChecked = 0;
544 const ParmVarDecl *getFirstParam()
const {
546 assert(!Mixes.empty());
547 return Mixes.front().First;
551 const ParmVarDecl *getLastParam()
const {
556 assert(!Mixes.empty());
557 return Mixes.back().Second;
563enum class ImplicitConversionModellingMode :
unsigned char {
572 OneWaySingleStandardOnly
579 const LValueReferenceType *LRef, QualType Ty,
580 const ASTContext &Ctx,
bool IsRefRHS,
581 ImplicitConversionModellingMode ImplicitMode);
585 QualType RType,
const ASTContext &Ctx,
586 ImplicitConversionModellingMode ImplicitMode);
589 return isa<AttributedType, DecayedType, ParenType>(T);
594struct NonCVRQualifiersResult {
599 bool HasMixabilityBreakingQualifiers;
602 Qualifiers CommonQualifiers;
611static NonCVRQualifiersResult
613 LLVM_DEBUG(llvm::dbgs() <<
">>> getNonCVRQualifiers for LType:\n";
614 LType.dump(llvm::dbgs(), Ctx); llvm::dbgs() <<
"\nand RType:\n";
615 RType.dump(llvm::dbgs(), Ctx); llvm::dbgs() <<
'\n';);
616 Qualifiers LQual = LType.getLocalQualifiers(),
617 RQual = RType.getLocalQualifiers();
620 LQual.removeCVRQualifiers();
621 RQual.removeCVRQualifiers();
623 NonCVRQualifiersResult Ret;
624 Ret.CommonQualifiers = Qualifiers::removeCommonQualifiers(LQual, RQual);
626 LLVM_DEBUG(llvm::dbgs() <<
"--- hasNonCVRMixabilityBreakingQualifiers. "
627 "Removed common qualifiers: ";
628 Ret.CommonQualifiers.print(llvm::dbgs(), Ctx.getPrintingPolicy());
629 llvm::dbgs() <<
"\n\tremaining on LType: ";
630 LQual.print(llvm::dbgs(), Ctx.getPrintingPolicy());
631 llvm::dbgs() <<
"\n\tremaining on RType: ";
632 RQual.print(llvm::dbgs(), Ctx.getPrintingPolicy());
633 llvm::dbgs() <<
'\n';);
637 Ret.HasMixabilityBreakingQualifiers =
638 LQual.hasQualifiers() || RQual.hasQualifiers();
654 const ASTContext &Ctx,
655 ImplicitConversionModellingMode ImplicitMode) {
656 LLVM_DEBUG(llvm::dbgs() <<
">>> calculateMixability for LType:\n";
657 LType.dump(llvm::dbgs(), Ctx); llvm::dbgs() <<
"\nand RType:\n";
658 RType.dump(llvm::dbgs(), Ctx); llvm::dbgs() <<
'\n';);
659 if (LType == RType) {
660 LLVM_DEBUG(llvm::dbgs() <<
"<<< calculateMixability. Trivial equality.\n");
661 return {MixFlags::Trivial, LType};
668 LLVM_DEBUG(llvm::dbgs()
669 <<
"--- calculateMixability. LHS is useless sugar.\n");
671 RType, Ctx, ImplicitMode);
674 LLVM_DEBUG(llvm::dbgs()
675 <<
"--- calculateMixability. RHS is useless sugar.\n");
677 Check, LType, RType.getSingleStepDesugaredType(Ctx), Ctx, ImplicitMode);
680 const auto *LLRef = LType->getAs<LValueReferenceType>();
681 const auto *RLRef = RType->getAs<LValueReferenceType>();
682 if (LLRef && RLRef) {
683 LLVM_DEBUG(llvm::dbgs() <<
"--- calculateMixability. LHS and RHS are &.\n");
686 RLRef->getPointeeType(), Ctx, ImplicitMode)
687 .withCommonTypeTransformed(
688 [&Ctx](QualType QT) {
return Ctx.getLValueReferenceType(QT); });
694 LLVM_DEBUG(llvm::dbgs() <<
"--- calculateMixability. LHS is &.\n");
697 MixFlags::ReferenceBind;
700 LLVM_DEBUG(llvm::dbgs() <<
"--- calculateMixability. RHS is &.\n");
703 MixFlags::ReferenceBind;
706 if (LType->getAs<TypedefType>()) {
707 LLVM_DEBUG(llvm::dbgs() <<
"--- calculateMixability. LHS is typedef.\n");
709 RType, Ctx, ImplicitMode) |
712 if (RType->getAs<TypedefType>()) {
713 LLVM_DEBUG(llvm::dbgs() <<
"--- calculateMixability. RHS is typedef.\n");
715 RType.getSingleStepDesugaredType(Ctx), Ctx,
724 bool CompareUnqualifiedTypes =
false;
725 if (LType.getLocalCVRQualifiers() != RType.getLocalCVRQualifiers()) {
726 LLVM_DEBUG(
if (LType.getLocalCVRQualifiers()) {
727 llvm::dbgs() <<
"--- calculateMixability. LHS has CVR-Qualifiers: ";
728 Qualifiers::fromCVRMask(LType.getLocalCVRQualifiers())
729 .print(llvm::dbgs(), Ctx.getPrintingPolicy());
730 llvm::dbgs() <<
'\n';
732 LLVM_DEBUG(
if (RType.getLocalCVRQualifiers()) {
733 llvm::dbgs() <<
"--- calculateMixability. RHS has CVR-Qualifiers: ";
734 Qualifiers::fromCVRMask(RType.getLocalCVRQualifiers())
735 .print(llvm::dbgs(), Ctx.getPrintingPolicy());
736 llvm::dbgs() <<
'\n';
740 LLVM_DEBUG(llvm::dbgs()
741 <<
"<<< calculateMixability. QualifiersMix turned off - not "
743 return {MixFlags::None};
746 CompareUnqualifiedTypes =
true;
749 bool OriginallySameQualifiers =
false;
750 if (LType.getLocalCVRQualifiers() == RType.getLocalCVRQualifiers() &&
751 LType.getLocalCVRQualifiers() != 0) {
752 LLVM_DEBUG(
if (LType.getLocalCVRQualifiers()) {
754 <<
"--- calculateMixability. LHS and RHS have same CVR-Qualifiers: ";
755 Qualifiers::fromCVRMask(LType.getLocalCVRQualifiers())
756 .print(llvm::dbgs(), Ctx.getPrintingPolicy());
757 llvm::dbgs() <<
'\n';
760 CompareUnqualifiedTypes =
true;
761 OriginallySameQualifiers =
true;
764 if (CompareUnqualifiedTypes) {
765 NonCVRQualifiersResult AdditionalQuals =
767 if (AdditionalQuals.HasMixabilityBreakingQualifiers) {
768 LLVM_DEBUG(llvm::dbgs() <<
"<<< calculateMixability. Additional "
769 "non-equal incompatible qualifiers.\n");
770 return {MixFlags::None};
773 const MixData UnqualifiedMixability =
775 RType.getLocalUnqualifiedType(), Ctx, ImplicitMode)
776 .withCommonTypeTransformed([&AdditionalQuals, &Ctx](QualType QT) {
779 return Ctx.getQualifiedType(QT, AdditionalQuals.CommonQualifiers);
782 if (!OriginallySameQualifiers)
784 return UnqualifiedMixability | MixFlags::Qualifiers;
788 return UnqualifiedMixability.withCommonTypeTransformed(
789 [&Ctx, LType](QualType QT) {
790 return Ctx.getQualifiedType(QT, LType.getLocalQualifiers());
797 bool RecursiveReturnDiscardingCanonicalType =
false;
799 if (LType->isPointerType() && RType->isPointerType()) {
803 LLVM_DEBUG(llvm::dbgs()
804 <<
"--- calculateMixability. LHS and RHS are Ptrs.\n");
805 MixData MixOfPointee =
807 RType->getPointeeType(), Ctx,
808 ImplicitConversionModellingMode::None)
809 .withCommonTypeTransformed(
810 [&Ctx](QualType QT) {
return Ctx.getPointerType(QT); });
811 if (
hasFlag(MixOfPointee.Flags,
812 MixFlags::WorkaroundDisableCanonicalEquivalence))
813 RecursiveReturnDiscardingCanonicalType =
true;
815 MixOfPointee.sanitize();
816 if (MixOfPointee.indicatesMixability()) {
817 LLVM_DEBUG(llvm::dbgs()
818 <<
"<<< calculateMixability. Pointees are mixable.\n");
823 if (ImplicitMode > ImplicitConversionModellingMode::None) {
824 LLVM_DEBUG(llvm::dbgs() <<
"--- calculateMixability. Start implicit...\n");
825 const MixData MixLTR =
828 if (
hasFlag(MixLTR.Flags, MixFlags::ImplicitConversion)) llvm::dbgs()
829 <<
"--- calculateMixability. Implicit Left -> Right found.\n";);
832 ImplicitConversionModellingMode::OneWaySingleStandardOnly &&
833 MixLTR.Conversion && !MixLTR.Conversion.AfterFirstStandard.isNull() &&
834 MixLTR.Conversion.UDConvKind == ConversionSequence::UDCK_None &&
835 MixLTR.Conversion.AfterSecondStandard.isNull()) {
838 LLVM_DEBUG(llvm::dbgs() <<
"<<< calculateMixability. Implicit "
839 "conversion, one-way, standard-only.\n");
840 return {MixFlags::ImplicitConversion, MixLTR.Conversion};
845 const MixData MixRTL =
848 if (
hasFlag(MixRTL.Flags, MixFlags::ImplicitConversion)) llvm::dbgs()
849 <<
"--- calculateMixability. Implicit Right -> Left found.\n";);
851 if (MixLTR.Conversion && MixRTL.Conversion) {
854 <<
"<<< calculateMixability. Implicit conversion, bidirectional.\n");
855 return {MixFlags::ImplicitConversion, MixLTR.Conversion,
860 if (RecursiveReturnDiscardingCanonicalType)
861 LLVM_DEBUG(llvm::dbgs() <<
"--- calculateMixability. Before CanonicalType, "
862 "Discard was enabled.\n");
866 if (LType->getAs<FunctionProtoType>() || RType->getAs<FunctionProtoType>()) {
870 LLVM_DEBUG(llvm::dbgs()
871 <<
"--- calculateMixability. Discarding potential canonical "
872 "equivalence on FunctionProtoTypes.\n");
873 RecursiveReturnDiscardingCanonicalType =
true;
876 MixData MixToReturn{MixFlags::None};
880 const QualType LCanonical = LType.getCanonicalType();
881 if (LCanonical == RType.getCanonicalType()) {
882 LLVM_DEBUG(llvm::dbgs()
883 <<
"<<< calculateMixability. Same CanonicalType.\n");
884 MixToReturn = {MixFlags::Canonical, LCanonical};
887 if (RecursiveReturnDiscardingCanonicalType)
888 MixToReturn |= MixFlags::WorkaroundDisableCanonicalEquivalence;
890 LLVM_DEBUG(
if (MixToReturn.Flags == MixFlags::None) llvm::dbgs()
891 <<
"<<< calculateMixability. No match found.\n");
902 const LValueReferenceType *LRef, QualType Ty,
903 const ASTContext &Ctx,
bool IsRefRHS,
904 ImplicitConversionModellingMode ImplicitMode) {
905 LLVM_DEBUG(llvm::dbgs() <<
">>> isLRefEquallyBindingToType for LRef:\n";
906 LRef->dump(llvm::dbgs(), Ctx); llvm::dbgs() <<
"\nand Type:\n";
907 Ty.dump(llvm::dbgs(), Ctx); llvm::dbgs() <<
'\n';);
909 QualType ReferredType = LRef->getPointeeType();
910 if (!ReferredType.isLocalConstQualified() &&
911 ReferredType->getAs<TypedefType>()) {
914 <<
"--- isLRefEquallyBindingToType. Non-const LRef to Typedef.\n");
915 ReferredType = ReferredType.getDesugaredType(Ctx);
916 if (!ReferredType.isLocalConstQualified()) {
917 LLVM_DEBUG(llvm::dbgs()
918 <<
"<<< isLRefEquallyBindingToType. Typedef is not const.\n");
919 return {MixFlags::None};
922 LLVM_DEBUG(llvm::dbgs() <<
"--- isLRefEquallyBindingToType. Typedef is "
923 "const, considering as const LRef.\n");
924 }
else if (!ReferredType.isLocalConstQualified()) {
925 LLVM_DEBUG(llvm::dbgs()
926 <<
"<<< isLRefEquallyBindingToType. Not const LRef.\n");
927 return {MixFlags::None};
930 assert(ReferredType.isLocalConstQualified() &&
931 "Reaching this point means we are sure LRef is effectively a const&.");
933 if (ReferredType == Ty) {
936 <<
"<<< isLRefEquallyBindingToType. Type of referred matches.\n");
937 return {MixFlags::Trivial, ReferredType};
940 QualType NonConstReferredType = ReferredType;
941 NonConstReferredType.removeLocalConst();
942 if (NonConstReferredType == Ty) {
943 LLVM_DEBUG(llvm::dbgs() <<
"<<< isLRefEquallyBindingToType. Type of "
944 "referred matches to non-const qualified.\n");
945 return {MixFlags::Trivial, NonConstReferredType};
950 <<
"--- isLRefEquallyBindingToType. Checking mix for underlying type.\n");
958 const CXXRecordDecl *Base) {
959 return Derived && Base && Derived->isCompleteDefinition() &&
960 Base->isCompleteDefinition() && Derived->isDerivedFrom(Base);
963static std::optional<QualType>
965 QualType To,
const ASTContext &Ctx) {
966 LLVM_DEBUG(llvm::dbgs() <<
">>> approximateStdConv for LType:\n";
967 From.dump(llvm::dbgs(), Ctx); llvm::dbgs() <<
"\nand RType:\n";
968 To.dump(llvm::dbgs(), Ctx); llvm::dbgs() <<
'\n';);
975 QualType WorkType = From;
979 auto FastQualifiersToApply =
static_cast<unsigned>(
980 From.split().Quals.getAsOpaqueValue() & Qualifiers::FastMask);
993 const auto *FromBuiltin = WorkType->getAs<BuiltinType>();
994 const auto *ToBuiltin = To->getAs<BuiltinType>();
995 const bool FromNumeric = FromBuiltin && (FromBuiltin->isIntegerType() ||
996 FromBuiltin->isFloatingType());
997 const bool ToNumeric =
998 ToBuiltin && (ToBuiltin->isIntegerType() || ToBuiltin->isFloatingType());
999 if (FromNumeric && ToNumeric) {
1004 LLVM_DEBUG(llvm::dbgs()
1005 <<
"--- approximateStdConv. Conversion between numerics.\n");
1006 WorkType = QualType{ToBuiltin, FastQualifiersToApply};
1009 const auto *FromEnum = WorkType->getAsCanonical<EnumType>();
1010 const auto *ToEnum = To->getAs<EnumType>();
1011 if (FromEnum && ToNumeric && FromEnum->isUnscopedEnumerationType()) {
1013 LLVM_DEBUG(llvm::dbgs()
1014 <<
"--- approximateStdConv. Unscoped enum to numeric.\n");
1015 WorkType = QualType{ToBuiltin, FastQualifiersToApply};
1016 }
else if (FromNumeric && ToEnum && ToEnum->isUnscopedEnumerationType()) {
1018 if (Ctx.getLangOpts().CPlusPlus) {
1019 LLVM_DEBUG(llvm::dbgs() <<
"<<< approximateStdConv. Numeric to unscoped "
1020 "enum, not possible in C++!\n");
1024 LLVM_DEBUG(llvm::dbgs()
1025 <<
"--- approximateStdConv. Numeric to unscoped enum.\n");
1026 WorkType = QualType{ToEnum, FastQualifiersToApply};
1030 const auto *FromPtr = WorkType->getAs<PointerType>();
1031 const auto *ToPtr = To->getAs<PointerType>();
1032 if (FromPtr && ToPtr) {
1033 if (ToPtr->isVoidPointerType()) {
1034 LLVM_DEBUG(llvm::dbgs() <<
"--- approximateStdConv. To void pointer.\n");
1035 WorkType = QualType{ToPtr, FastQualifiersToApply};
1038 const auto *FromRecordPtr = FromPtr->getPointeeCXXRecordDecl();
1039 const auto *ToRecordPtr = ToPtr->getPointeeCXXRecordDecl();
1041 LLVM_DEBUG(llvm::dbgs() <<
"--- approximateStdConv. Derived* to Base*\n");
1042 WorkType = QualType{ToPtr, FastQualifiersToApply};
1048 const auto *FromRecord = WorkType->getAsCXXRecordDecl();
1049 const auto *ToRecord = To->getAsCXXRecordDecl();
1051 LLVM_DEBUG(llvm::dbgs() <<
"--- approximateStdConv. Derived To Base.\n");
1052 WorkType = QualType{
1053 ToRecord->getASTContext().getCanonicalTagType(ToRecord)->getTypePtr(),
1054 FastQualifiersToApply};
1057 if (Ctx.getLangOpts().CPlusPlus17 && FromPtr && ToPtr) {
1060 const auto *FromFunctionPtr =
1061 FromPtr->getPointeeType()->getAs<FunctionProtoType>();
1062 const auto *ToFunctionPtr =
1063 ToPtr->getPointeeType()->getAs<FunctionProtoType>();
1064 if (FromFunctionPtr && ToFunctionPtr &&
1065 FromFunctionPtr->hasNoexceptExceptionSpec() &&
1066 !ToFunctionPtr->hasNoexceptExceptionSpec()) {
1067 LLVM_DEBUG(llvm::dbgs() <<
"--- approximateStdConv. noexcept function "
1068 "pointer to non-noexcept.\n");
1069 WorkType = QualType{ToPtr, FastQualifiersToApply};
1075 LLVM_DEBUG(llvm::dbgs()
1076 <<
"--- approximateStdConv. Trying qualifier adjustment...\n");
1078 ImplicitConversionModellingMode::None);
1079 QualConv.sanitize();
1080 if (
hasFlag(QualConv.Flags, MixFlags::Qualifiers)) {
1081 LLVM_DEBUG(llvm::dbgs()
1082 <<
"<<< approximateStdConv. Qualifiers adjusted.\n");
1086 if (ASTContext::hasSameType(WorkType, To)) {
1087 LLVM_DEBUG(llvm::dbgs() <<
"<<< approximateStdConv. Reached 'To' type.\n");
1088 return {Ctx.getCommonSugaredType(WorkType, To)};
1091 LLVM_DEBUG(llvm::dbgs() <<
"<<< approximateStdConv. Did not reach 'To'.\n");
1100class UserDefinedConversionSelector {
1106 struct PreparedConversion {
1107 const CXXMethodDecl *ConversionFun;
1109 ConversionSequence Seq;
1111 PreparedConversion(
const CXXMethodDecl *CMD, MixFlags F,
1112 ConversionSequence S)
1113 : ConversionFun(CMD), Flags(F), Seq(S) {}
1116 UserDefinedConversionSelector(
const TheCheck &Check) : Check(Check) {}
1123 void addConversion(
const CXXMethodDecl *ConvFun, QualType FromType,
1128 Check, FromType, ToType, ConvFun->getASTContext(),
1129 ImplicitConversionModellingMode::OneWaySingleStandardOnly);
1131 if (!Mix.indicatesMixability())
1134 LLVM_DEBUG(llvm::dbgs() <<
"--- tryConversion. Found viable with flags: "
1136 FlaggedConversions.emplace_back(ConvFun, Mix.Flags, Mix.Conversion);
1141 std::optional<PreparedConversion> operator()()
const {
1142 if (FlaggedConversions.empty()) {
1143 LLVM_DEBUG(llvm::dbgs() <<
"--- selectUserDefinedConv. Empty.\n");
1146 if (FlaggedConversions.size() == 1) {
1147 LLVM_DEBUG(llvm::dbgs() <<
"--- selectUserDefinedConv. Single.\n");
1148 return FlaggedConversions.front();
1151 std::optional<PreparedConversion> BestConversion;
1152 unsigned short HowManyGoodConversions = 0;
1153 for (
const auto &Prepared : FlaggedConversions) {
1154 LLVM_DEBUG(llvm::dbgs() <<
"--- selectUserDefinedConv. Candidate flags: "
1156 if (!BestConversion) {
1157 BestConversion = Prepared;
1158 ++HowManyGoodConversions;
1162 const bool BestConversionHasImplicit =
1163 hasFlag(BestConversion->Flags, MixFlags::ImplicitConversion);
1164 const bool ThisConversionHasImplicit =
1165 hasFlag(Prepared.Flags, MixFlags::ImplicitConversion);
1166 if (!BestConversionHasImplicit && ThisConversionHasImplicit)
1170 if (BestConversionHasImplicit && !ThisConversionHasImplicit) {
1175 BestConversion = Prepared;
1176 HowManyGoodConversions = 1;
1180 if (BestConversionHasImplicit == ThisConversionHasImplicit)
1183 ++HowManyGoodConversions;
1186 if (HowManyGoodConversions == 1) {
1187 LLVM_DEBUG(llvm::dbgs()
1188 <<
"--- selectUserDefinedConv. Unique result. Flags: "
1190 return BestConversion;
1193 LLVM_DEBUG(llvm::dbgs()
1194 <<
"--- selectUserDefinedConv. No, or ambiguous.\n");
1199 llvm::SmallVector<PreparedConversion, 2> FlaggedConversions;
1205static std::optional<ConversionSequence>
1208 if (!RD || !RD->isCompleteDefinition())
1210 RD = RD->getDefinition();
1212 LLVM_DEBUG(llvm::dbgs() <<
">>> tryConversionOperators: " << RD->getName()
1214 ToType.dump(llvm::dbgs(), RD->getASTContext());
1215 llvm::dbgs() <<
'\n';);
1217 UserDefinedConversionSelector ConversionSet{Check};
1219 for (
const NamedDecl *Method : RD->getVisibleConversionFunctions()) {
1220 const auto *Con = dyn_cast<CXXConversionDecl>(Method);
1221 if (!Con || Con->isExplicit())
1223 LLVM_DEBUG(llvm::dbgs() <<
"--- tryConversionOperators. Trying:\n";
1224 Con->dump(llvm::dbgs()); llvm::dbgs() <<
'\n';);
1228 ConversionSet.addConversion(Con, Con->getConversionType(), ToType);
1231 if (std::optional<UserDefinedConversionSelector::PreparedConversion>
1232 SelectedConversion = ConversionSet()) {
1233 const CanQualType RecordType = RD->getASTContext().getCanonicalTagType(RD);
1235 ConversionSequence Result{RecordType, ToType};
1240 Result.AfterSecondStandard = SelectedConversion->Seq.AfterFirstStandard;
1242 ConversionSequence::UserDefinedConversionOperator ConvOp;
1243 ConvOp.Fun = cast<CXXConversionDecl>(SelectedConversion->ConversionFun);
1244 ConvOp.UserDefinedType = RecordType;
1245 ConvOp.ConversionOperatorResultType = ConvOp.Fun->getConversionType();
1246 Result.setConversion(ConvOp);
1248 LLVM_DEBUG(llvm::dbgs() <<
"<<< tryConversionOperators. Found result.\n");
1252 LLVM_DEBUG(llvm::dbgs() <<
"<<< tryConversionOperators. No conversion.\n");
1256static std::optional<ConversionSequence>
1258 const CXXRecordDecl *RD) {
1259 if (!RD || !RD->isCompleteDefinition())
1261 RD = RD->getDefinition();
1263 LLVM_DEBUG(llvm::dbgs() <<
">>> tryConveringConstructors: " << RD->getName()
1265 FromType.dump(llvm::dbgs(), RD->getASTContext());
1266 llvm::dbgs() <<
'\n';);
1268 UserDefinedConversionSelector ConversionSet{Check};
1270 for (
const CXXConstructorDecl *Con : RD->ctors()) {
1271 if (Con->isCopyOrMoveConstructor() ||
1272 !Con->isConvertingConstructor(
false))
1274 LLVM_DEBUG(llvm::dbgs() <<
"--- tryConvertingConstructors. Trying:\n";
1275 Con->dump(llvm::dbgs()); llvm::dbgs() <<
'\n';);
1279 ConversionSet.addConversion(Con, FromType, Con->getParamDecl(0)->getType());
1282 if (std::optional<UserDefinedConversionSelector::PreparedConversion>
1283 SelectedConversion = ConversionSet()) {
1284 const CanQualType RecordType = RD->getASTContext().getCanonicalTagType(RD);
1286 ConversionSequence Result{FromType, RecordType};
1287 Result.AfterFirstStandard = SelectedConversion->Seq.AfterFirstStandard;
1289 ConversionSequence::UserDefinedConvertingConstructor Ctor;
1290 Ctor.Fun = cast<CXXConstructorDecl>(SelectedConversion->ConversionFun);
1291 Ctor.ConstructorParameterType = Ctor.Fun->getParamDecl(0)->getType();
1292 Ctor.UserDefinedType = RecordType;
1293 Result.setConversion(Ctor);
1295 LLVM_DEBUG(llvm::dbgs()
1296 <<
"<<< tryConvertingConstructors. Found result.\n");
1300 LLVM_DEBUG(llvm::dbgs() <<
"<<< tryConvertingConstructors. No conversion.\n");
1311 QualType RType,
const ASTContext &Ctx,
1312 ImplicitConversionModellingMode ImplicitMode) {
1313 LLVM_DEBUG(llvm::dbgs() <<
">>> approximateImplicitConversion for LType:\n";
1314 LType.dump(llvm::dbgs(), Ctx); llvm::dbgs() <<
"\nand RType:\n";
1315 RType.dump(llvm::dbgs(), Ctx);
1316 llvm::dbgs() <<
"\nimplicit mode: ";
switch (ImplicitMode) {
1317 case ImplicitConversionModellingMode::None:
1318 llvm::dbgs() <<
"None";
1320 case ImplicitConversionModellingMode::All:
1321 llvm::dbgs() <<
"All";
1323 case ImplicitConversionModellingMode::OneWaySingleStandardOnly:
1324 llvm::dbgs() <<
"OneWay, Single, STD Only";
1326 } llvm::dbgs() <<
'\n';);
1328 return {MixFlags::Trivial, LType};
1334 ConversionSequence ImplicitSeq{LType, RType};
1335 QualType WorkType = LType;
1337 std::optional<QualType> AfterFirstStdConv =
1339 if (AfterFirstStdConv) {
1340 LLVM_DEBUG(llvm::dbgs() <<
"--- approximateImplicitConversion. Standard "
1341 "Pre-Conversion found!\n");
1342 ImplicitSeq.AfterFirstStandard = *AfterFirstStdConv;
1343 WorkType = ImplicitSeq.AfterFirstStandard;
1346 if (ImplicitMode == ImplicitConversionModellingMode::OneWaySingleStandardOnly)
1348 return {ImplicitSeq.AfterFirstStandard.isNull()
1350 : MixFlags::ImplicitConversion,
1353 if (Ctx.getLangOpts().CPlusPlus) {
1354 bool FoundConversionOperator =
false, FoundConvertingCtor =
false;
1356 if (
const auto *LRD = WorkType->getAsCXXRecordDecl()) {
1357 std::optional<ConversionSequence> ConversionOperatorResult =
1359 if (ConversionOperatorResult) {
1360 LLVM_DEBUG(llvm::dbgs() <<
"--- approximateImplicitConversion. Found "
1361 "conversion operator.\n");
1362 ImplicitSeq.update(*ConversionOperatorResult);
1363 WorkType = ImplicitSeq.getTypeAfterUserDefinedConversion();
1364 FoundConversionOperator =
true;
1368 if (
const auto *RRD = RType->getAsCXXRecordDecl()) {
1372 std::optional<ConversionSequence> ConvCtorResult =
1374 if (ConvCtorResult) {
1375 LLVM_DEBUG(llvm::dbgs() <<
"--- approximateImplicitConversion. Found "
1376 "converting constructor.\n");
1377 ImplicitSeq.update(*ConvCtorResult);
1378 WorkType = ImplicitSeq.getTypeAfterUserDefinedConversion();
1379 FoundConvertingCtor =
true;
1383 if (FoundConversionOperator && FoundConvertingCtor) {
1385 LLVM_DEBUG(llvm::dbgs()
1386 <<
"<<< approximateImplicitConversion. Found both "
1387 "user-defined conversion kinds in the same sequence!\n");
1388 return {MixFlags::None};
1396 <<
"--- approximateImplicitConversion. Try to find post-conversion.\n");
1398 Check, WorkType, RType, Ctx,
1399 ImplicitConversionModellingMode::OneWaySingleStandardOnly);
1400 if (SecondStdConv.indicatesMixability()) {
1401 LLVM_DEBUG(llvm::dbgs() <<
"--- approximateImplicitConversion. Standard "
1402 "Post-Conversion found!\n");
1407 ImplicitSeq.AfterSecondStandard =
1408 SecondStdConv.Conversion.AfterFirstStandard;
1409 WorkType = ImplicitSeq.AfterSecondStandard;
1413 LLVM_DEBUG(llvm::dbgs()
1414 <<
"<<< approximateImplicitConversion. Found a conversion.\n");
1415 return {MixFlags::ImplicitConversion, ImplicitSeq};
1419 llvm::dbgs() <<
"<<< approximateImplicitConversion. No match found.\n");
1420 return {MixFlags::None};
1424 const TheCheck &Check,
const FunctionDecl *FD, std::size_t StartIndex,
1426 const std::size_t NumParams = FD->getNumParams();
1427 assert(StartIndex < NumParams &&
"out of bounds for start");
1428 const ASTContext &Ctx = FD->getASTContext();
1430 MixableParameterRange Ret;
1432 Ret.NumParamsChecked = 1;
1434 for (std::size_t I = StartIndex + 1; I < NumParams; ++I) {
1435 const ParmVarDecl *Ith = FD->getParamDecl(I);
1436 const StringRef ParamName = Ith->getName();
1437 LLVM_DEBUG(llvm::dbgs()
1438 <<
"Check param #" << I <<
" '" << ParamName <<
"'...\n");
1440 LLVM_DEBUG(llvm::dbgs() <<
"Param #" << I <<
" is ignored. Break!\n");
1444 const StringRef PrevParamName = FD->getParamDecl(I - 1)->getName();
1445 if (!ParamName.empty() && !PrevParamName.empty() &&
1449 LLVM_DEBUG(llvm::dbgs() <<
"Parameter '" << ParamName
1450 <<
"' follows a pattern with previous parameter '"
1451 << PrevParamName <<
"'. Break!\n");
1457 MixableParameterRange::MixVector MixesOfIth;
1458 for (std::size_t J = StartIndex; J < I; ++J) {
1459 const ParmVarDecl *Jth = FD->getParamDecl(J);
1460 LLVM_DEBUG(llvm::dbgs()
1461 <<
"Check mix of #" << J <<
" against #" << I <<
"...\n");
1463 if (isSimilarlyUsedParameter(UsageBasedSuppressor, Ith, Jth)) {
1466 LLVM_DEBUG(llvm::dbgs() <<
"Parameters #" << I <<
" and #" << J
1467 <<
" deemed related, ignoring...\n");
1479 ? ImplicitConversionModellingMode::All
1480 : ImplicitConversionModellingMode::None)};
1481 LLVM_DEBUG(llvm::dbgs() <<
"Mix flags (raw) : "
1484 LLVM_DEBUG(llvm::dbgs() <<
"Mix flags (after sanitize): "
1487 assert(M.flagsValid() &&
"All flags decayed!");
1490 MixesOfIth.emplace_back(std::move(M));
1493 if (MixesOfIth.empty()) {
1496 LLVM_DEBUG(llvm::dbgs()
1498 <<
" does not mix with any in the current range. Break!\n");
1502 Ret.Mixes.insert(Ret.Mixes.end(), MixesOfIth.begin(), MixesOfIth.end());
1503 ++Ret.NumParamsChecked;
1513AST_MATCHER_FUNCTION(ast_matchers::internal::Matcher<Stmt>, paramRefExpr) {
1514 return expr(ignoringParenImpCasts(ignoringElidableConstructorCall(
1515 declRefExpr(to(parmVarDecl().bind(
"param"))))));
1524 LLVM_DEBUG(llvm::dbgs() <<
"Checking if '" << Node->getName()
1525 <<
"' is ignored.\n");
1527 if (!Node->getIdentifier())
1530 const StringRef NodeName = Node->getName();
1532 LLVM_DEBUG(llvm::dbgs() <<
"\tName ignored.\n");
1536 const StringRef NodeTypeName = [Node] {
1537 const ASTContext &Ctx = Node->getASTContext();
1538 const SourceManager &SM = Ctx.getSourceManager();
1539 SourceLocation B = Node->getTypeSpecStartLoc();
1540 SourceLocation E = Node->getTypeSpecEndLoc();
1541 const LangOptions LO;
1543 LLVM_DEBUG(llvm::dbgs() <<
"\tType name code is '"
1544 << Lexer::getSourceText(
1545 CharSourceRange::getTokenRange(B, E), SM, LO)
1547 if (B.isMacroID()) {
1548 LLVM_DEBUG(llvm::dbgs() <<
"\t\tBeginning is macro.\n");
1549 B = SM.getTopMacroCallerLoc(B);
1551 if (E.isMacroID()) {
1552 LLVM_DEBUG(llvm::dbgs() <<
"\t\tEnding is macro.\n");
1553 E = Lexer::getLocForEndOfToken(SM.getTopMacroCallerLoc(E), 0, SM, LO);
1555 LLVM_DEBUG(llvm::dbgs() <<
"\tType name code is '"
1556 << Lexer::getSourceText(
1557 CharSourceRange::getTokenRange(B, E), SM, LO)
1560 return Lexer::getSourceText(CharSourceRange::getTokenRange(B, E), SM, LO);
1563 LLVM_DEBUG(llvm::dbgs() <<
"\tType name is '" << NodeTypeName <<
"'\n");
1564 if (!NodeTypeName.empty()) {
1566 [NodeTypeName](StringRef E) {
1567 return !E.empty() && NodeTypeName.ends_with(E);
1569 LLVM_DEBUG(llvm::dbgs() <<
"\tType suffix ignored.\n");
1583template <
typename T, std::
size_t N = SmallDataStructureSize>
1585 llvm::DenseMap<const ParmVarDecl *, llvm::SmallSet<T, N>>;
1587template <
typename T, std::
size_t N = SmallDataStructureSize>
1589 llvm::DenseMap<const ParmVarDecl *, llvm::SmallPtrSet<T, N>>;
1593template <
typename MapTy,
typename ElemTy>
1596 auto E1Iterator = Map.find(E1);
1597 auto E2Iterator = Map.find(E2);
1598 if (E1Iterator == Map.end() || E2Iterator == Map.end())
1601 return llvm::any_of(E1Iterator->second, [&E2Iterator](
const auto &E1SetElem) {
1602 return E2Iterator->second.contains(E1SetElem);
1612class AppearsInSameExpr :
public RecursiveASTVisitor<AppearsInSameExpr> {
1613 using Base = RecursiveASTVisitor<AppearsInSameExpr>;
1615 const FunctionDecl *FD;
1616 const Expr *CurrentExprOnlyTreeRoot =
nullptr;
1617 llvm::DenseMap<
const ParmVarDecl *,
1618 llvm::SmallPtrSet<const Expr *, SmallDataStructureSize>>
1619 ParentExprsForParamRefs;
1622 void setup(
const FunctionDecl *FD) {
1624 TraverseFunctionDecl(
const_cast<FunctionDecl *
>(FD));
1627 bool operator()(
const ParmVarDecl *Param1,
const ParmVarDecl *Param2)
const {
1632 bool TraverseDecl(Decl *D) {
1633 CurrentExprOnlyTreeRoot =
nullptr;
1634 return Base::TraverseDecl(D);
1637 bool TraverseStmt(Stmt *S, DataRecursionQueue *Queue =
nullptr) {
1638 if (
auto *E = dyn_cast_or_null<Expr>(S)) {
1639 bool RootSetInCurrentStackFrame =
false;
1640 if (!CurrentExprOnlyTreeRoot) {
1641 CurrentExprOnlyTreeRoot = E;
1642 RootSetInCurrentStackFrame =
true;
1645 const bool Ret = Base::TraverseStmt(S);
1647 if (RootSetInCurrentStackFrame)
1648 CurrentExprOnlyTreeRoot =
nullptr;
1654 CurrentExprOnlyTreeRoot =
nullptr;
1655 return Base::TraverseStmt(S);
1658 bool VisitDeclRefExpr(DeclRefExpr *DRE) {
1659 if (!CurrentExprOnlyTreeRoot)
1662 if (
auto *PVD = dyn_cast<ParmVarDecl>(DRE->getDecl()))
1663 if (llvm::find(FD->parameters(), PVD))
1664 ParentExprsForParamRefs[PVD].insert(CurrentExprOnlyTreeRoot);
1674class PassedToSameFunction {
1678 void setup(
const FunctionDecl *FD) {
1679 auto ParamsAsArgsInFnCalls =
1680 match(functionDecl(forEachDescendant(
1681 callExpr(forEachArgumentWithParam(
1682 paramRefExpr(), parmVarDecl().bind(
"passed-to")))
1683 .bind(
"call-expr"))),
1684 *FD, FD->getASTContext());
1685 for (
const auto &Match : ParamsAsArgsInFnCalls) {
1686 const auto *PassedParamOfThisFn = Match.getNodeAs<ParmVarDecl>(
"param");
1687 const auto *CE = Match.getNodeAs<CallExpr>(
"call-expr");
1688 const auto *PassedToParam = Match.getNodeAs<ParmVarDecl>(
"passed-to");
1689 assert(PassedParamOfThisFn && CE && PassedToParam);
1691 const FunctionDecl *CalledFn = CE->getDirectCallee();
1695 std::optional<unsigned> TargetIdx;
1696 const unsigned NumFnParams = CalledFn->getNumParams();
1697 for (
unsigned Idx = 0; Idx < NumFnParams; ++Idx)
1698 if (CalledFn->getParamDecl(Idx) == PassedToParam)
1699 TargetIdx.emplace(Idx);
1701 assert(TargetIdx &&
"Matched, but didn't find index?");
1702 TargetParams[PassedParamOfThisFn].insert(
1703 {CalledFn->getCanonicalDecl(), *TargetIdx});
1707 bool operator()(
const ParmVarDecl *Param1,
const ParmVarDecl *Param2)
const {
1714class AccessedSameMemberOf {
1718 void setup(
const FunctionDecl *FD) {
1719 auto MembersCalledOnParams =
match(
1720 functionDecl(forEachDescendant(
1721 memberExpr(hasObjectExpression(paramRefExpr())).bind(
"mem-expr"))),
1722 *FD, FD->getASTContext());
1724 for (
const auto &Match : MembersCalledOnParams) {
1725 const auto *AccessedParam = Match.getNodeAs<ParmVarDecl>(
"param");
1726 const auto *ME = Match.getNodeAs<MemberExpr>(
"mem-expr");
1727 assert(AccessedParam && ME);
1728 AccessedMembers[AccessedParam].insert(
1729 ME->getMemberDecl()->getCanonicalDecl());
1733 bool operator()(
const ParmVarDecl *Param1,
const ParmVarDecl *Param2)
const {
1741 llvm::SmallVector<const ParmVarDecl *, SmallDataStructureSize> ReturnedParams;
1744 void setup(
const FunctionDecl *FD) {
1746 auto ParamReturns =
match(functionDecl(forEachDescendant(
1747 returnStmt(hasReturnValue(paramRefExpr())))),
1748 *FD, FD->getASTContext());
1749 for (
const auto &Match : ParamReturns) {
1750 const auto *ReturnedParam = Match.getNodeAs<ParmVarDecl>(
"param");
1751 assert(ReturnedParam);
1753 if (find(FD->parameters(), ReturnedParam) == FD->param_end())
1759 ReturnedParams.emplace_back(ReturnedParam);
1763 bool operator()(
const ParmVarDecl *Param1,
const ParmVarDecl *Param2)
const {
1764 return llvm::is_contained(ReturnedParams, Param1) &&
1765 llvm::is_contained(ReturnedParams, Param2);
1777 relatedness_heuristic::AppearsInSameExpr SameExpr;
1778 relatedness_heuristic::PassedToSameFunction PassToFun;
1779 relatedness_heuristic::AccessedSameMemberOf SameMember;
1780 relatedness_heuristic::Returned Returns;
1789 PassToFun.setup(FD);
1790 SameMember.setup(FD);
1796 bool operator()(
const ParmVarDecl *Param1,
const ParmVarDecl *Param2)
const {
1800 LLVM_DEBUG(llvm::dbgs()
1801 <<
"::: Matching similar usage / relatedness heuristic...\n");
1803 if (SameExpr(Param1, Param2)) {
1804 LLVM_DEBUG(llvm::dbgs() <<
"::: Used in the same expression.\n");
1808 if (PassToFun(Param1, Param2)) {
1809 LLVM_DEBUG(llvm::dbgs()
1810 <<
"::: Passed to same function in different calls.\n");
1814 if (SameMember(Param1, Param2)) {
1815 LLVM_DEBUG(llvm::dbgs()
1816 <<
"::: Same member field access or method called.\n");
1820 if (Returns(Param1, Param2)) {
1821 LLVM_DEBUG(llvm::dbgs() <<
"::: Both parameter returned.\n");
1825 LLVM_DEBUG(llvm::dbgs() <<
"::: None.\n");
1834 const ParmVarDecl *Param1,
const ParmVarDecl *Param2) {
1835 return Suppressor(Param1, Param2);
1839 while (Str.size() < ToLen)
1840 Str.emplace_back(
'\0');
1844 while (Str.size() < ToLen)
1845 Str.insert(Str.begin(),
'\0');
1850 assert(S1.size() >= N && S2.size() >= N);
1851 const StringRef S1Prefix = S1.take_front(S1.size() - N),
1852 S2Prefix = S2.take_front(S2.size() - N);
1853 return S1Prefix == S2Prefix && !S1Prefix.empty();
1858 assert(S1.size() >= N && S2.size() >= N);
1859 const StringRef S1Suffix = S1.take_back(S1.size() - N),
1860 S2Suffix = S2.take_back(S2.size() - N);
1861 return S1Suffix == S2Suffix && !S1Suffix.empty();
1867 StringRef Str1, StringRef Str2) {
1872 const std::size_t BiggerLength = std::max(Str1.size(), Str2.size());
1874 if (BiggerLength <= Threshold)
1881 SmallString<32> S1PadE{Str1}, S2PadE{Str2};
1886 Threshold, StringRef{S1PadE.begin(), BiggerLength},
1887 StringRef{S2PadE.begin(), BiggerLength}))
1890 SmallString<32> S1PadB{Str1}, S2PadB{Str2};
1895 Threshold, StringRef{S1PadB.begin(), BiggerLength},
1896 StringRef{S2PadB.begin(), BiggerLength}))
1907AST_MATCHER_P(FunctionDecl, parameterCountGE,
unsigned, N) {
1908 return Node.getNumParams() >= N;
1912AST_MATCHER(FunctionDecl, isOverloadedUnaryOrBinaryOperator) {
1913 switch (Node.getOverloadedOperator()) {
1918 case OO_Array_Delete:
1919 case OO_Conditional:
1924 return Node.getNumParams() <= 2;
1939static SmallString<64>
getName(
const NamedDecl *ND) {
1940 SmallString<64> Name;
1941 llvm::raw_svector_ostream OS{Name};
1942 ND->getNameForDiagnostic(OS, ND->getASTContext().getPrintingPolicy(),
false);
1957 using namespace model;
1958 return static_cast<bool>(
1960 (MixFlags::TypeAlias | MixFlags::ReferenceBind | MixFlags::Qualifiers));
1966 return model::hasFlag(M.flags(), model::MixFlags::ImplicitConversion);
1973struct FormattedConversionSequence {
1974 std::string DiagnosticText;
1979 bool Trivial =
true;
1981 FormattedConversionSequence(
const PrintingPolicy &
PP,
1982 StringRef StartTypeAsDiagnosed,
1983 const model::ConversionSequence &Conv,
1984 StringRef DestinationTypeAsDiagnosed) {
1985 llvm::raw_string_ostream OS{DiagnosticText};
1988 OS <<
'\'' << StartTypeAsDiagnosed <<
'\'';
1989 std::string LastAddedType = StartTypeAsDiagnosed.str();
1990 std::size_t NumElementsAdded = 1;
1994 const std::string SeqBeginTypeStr = Conv.Begin.getAsString(
PP);
1995 std::string SeqEndTypeStr = Conv.End.getAsString(
PP);
1996 if (StartTypeAsDiagnosed != SeqBeginTypeStr) {
1997 OS <<
" (as '" << SeqBeginTypeStr <<
"')";
1998 LastAddedType = SeqBeginTypeStr;
2002 auto AddType = [&](StringRef ToAdd) {
2003 if (LastAddedType != ToAdd && ToAdd != SeqEndTypeStr) {
2004 OS <<
" -> '" << ToAdd <<
"'";
2005 LastAddedType = ToAdd.str();
2009 for (
const QualType InvolvedType : Conv.getInvolvedTypesInSequence())
2011 AddType(InvolvedType.getAsString(
PP));
2013 if (LastAddedType != DestinationTypeAsDiagnosed) {
2014 OS <<
" -> '" << DestinationTypeAsDiagnosed <<
"'";
2015 LastAddedType = DestinationTypeAsDiagnosed.str();
2022 if (DestinationTypeAsDiagnosed != SeqEndTypeStr) {
2023 OS <<
" (as '" << SeqEndTypeStr <<
"')";
2024 LastAddedType = SeqEndTypeStr;
2028 if (Trivial && NumElementsAdded > 2)
2038template <
typename E, std::
size_t N>
class InsertOnce {
2039 llvm::SmallSet<E, N> CalledWith;
2042 bool operator()(E El) {
return CalledWith.insert(std::move(El)).second; }
2044 bool calledWith(
const E &El)
const {
return CalledWith.contains(El); }
2047struct SwappedEqualQualTypePair {
2048 QualType LHSType, RHSType;
2050 bool operator==(
const SwappedEqualQualTypePair &Other)
const {
2051 return (LHSType == Other.LHSType && RHSType == Other.RHSType) ||
2052 (LHSType == Other.RHSType && RHSType == Other.LHSType);
2055 bool operator<(
const SwappedEqualQualTypePair &Other)
const {
2056 return LHSType < Other.LHSType && RHSType < Other.RHSType;
2060struct TypeAliasDiagnosticTuple {
2061 QualType LHSType, RHSType, CommonType;
2063 bool operator==(
const TypeAliasDiagnosticTuple &Other)
const {
2064 return CommonType == Other.CommonType &&
2065 ((LHSType == Other.LHSType && RHSType == Other.RHSType) ||
2066 (LHSType == Other.RHSType && RHSType == Other.LHSType));
2069 bool operator<(
const TypeAliasDiagnosticTuple &Other)
const {
2070 return CommonType < Other.CommonType && LHSType < Other.LHSType &&
2071 RHSType < Other.RHSType;
2076class UniqueTypeAliasDiagnosticHelper
2077 :
public InsertOnce<TypeAliasDiagnosticTuple, 8> {
2078 using Base = InsertOnce<TypeAliasDiagnosticTuple, 8>;
2083 bool operator()(QualType LHSType, QualType RHSType, QualType CommonType) {
2084 if (CommonType.isNull() || CommonType == LHSType || CommonType == RHSType)
2085 return Base::operator()({LHSType, RHSType, {}});
2087 const TypeAliasDiagnosticTuple ThreeTuple{LHSType, RHSType, CommonType};
2088 if (!Base::operator()(ThreeTuple))
2091 const bool AlreadySaidLHSAndCommonIsSame =
2092 calledWith({LHSType, CommonType, {}});
2093 const bool AlreadySaidRHSAndCommonIsSame =
2094 calledWith({RHSType, CommonType, {}});
2095 if (AlreadySaidLHSAndCommonIsSame && AlreadySaidRHSAndCommonIsSame) {
2116 Options.get(
"IgnoredParameterTypeSuffixes",
2122 Options.get(
"SuppressParametersUsedTogether",
2125 Options.get(
"NamePrefixSuffixSilenceDissimilarityThreshold",
2131 Options.store(Opts,
"IgnoredParameterNames",
2133 Options.store(Opts,
"IgnoredParameterTypeSuffixes",
2137 Options.store(Opts,
"SuppressParametersUsedTogether",
2139 Options.store(Opts,
"NamePrefixSuffixSilenceDissimilarityThreshold",
2144 const auto BaseConstraints = functionDecl(
2148 unless(isOverloadedUnaryOrBinaryOperator()));
2151 functionDecl(BaseConstraints,
2152 unless(ast_matchers::isTemplateInstantiation()))
2156 functionDecl(BaseConstraints, isExplicitTemplateSpecialization())
2162 const MatchFinder::MatchResult &Result) {
2163 using namespace model;
2166 const auto *FD = Result.Nodes.getNodeAs<FunctionDecl>(
"func");
2169 const PrintingPolicy &
PP = FD->getASTContext().getPrintingPolicy();
2170 const std::size_t NumParams = FD->getNumParams();
2171 std::size_t MixableRangeStartIndex = 0;
2178 LLVM_DEBUG(llvm::dbgs() <<
"Begin analysis of " <<
getName(FD) <<
" with "
2179 << NumParams <<
" parameters...\n");
2180 while (MixableRangeStartIndex < NumParams) {
2181 if (isIgnoredParameter(*
this, FD->getParamDecl(MixableRangeStartIndex))) {
2182 LLVM_DEBUG(llvm::dbgs()
2183 <<
"Parameter #" << MixableRangeStartIndex <<
" ignored.\n");
2184 ++MixableRangeStartIndex;
2188 MixableParameterRange R = modelMixingRange(
2189 *
this, FD, MixableRangeStartIndex, UsageBasedSuppressor);
2190 assert(R.NumParamsChecked > 0 &&
"Ensure forward progress!");
2191 MixableRangeStartIndex += R.NumParamsChecked;
2193 LLVM_DEBUG(llvm::dbgs() <<
"Ignoring range of " << R.NumParamsChecked
2194 <<
" lower than limit.\n");
2198 const bool NeedsAnyTypeNote =
2200 const bool HasAnyImplicits =
2202 const ParmVarDecl *First = R.getFirstParam(), *Last = R.getLastParam();
2203 const std::string FirstParamTypeAsWritten =
2204 First->getType().getAsString(
PP);
2208 if (HasAnyImplicits)
2209 DiagText =
"%0 adjacent parameters of %1 of convertible types are "
2210 "easily swapped by mistake";
2211 else if (NeedsAnyTypeNote)
2212 DiagText =
"%0 adjacent parameters of %1 of similar type are easily "
2213 "swapped by mistake";
2215 DiagText =
"%0 adjacent parameters of %1 of similar type ('%2') are "
2216 "easily swapped by mistake";
2218 auto Diag = diag(First->getOuterLocStart(), DiagText)
2219 <<
static_cast<unsigned>(R.NumParamsChecked) << FD;
2220 if (!NeedsAnyTypeNote)
2221 Diag << FirstParamTypeAsWritten;
2223 const CharSourceRange HighlightRange = CharSourceRange::getTokenRange(
2224 First->getBeginLoc(), Last->getEndLoc());
2225 Diag << HighlightRange;
2231 diag(First->getLocation(),
"the first parameter in the range is '%0'",
2232 DiagnosticIDs::Note)
2234 << CharSourceRange::getTokenRange(First->getLocation(),
2235 First->getLocation());
2236 diag(Last->getLocation(),
"the last parameter in the range is '%0'",
2237 DiagnosticIDs::Note)
2239 << CharSourceRange::getTokenRange(Last->getLocation(),
2240 Last->getLocation());
2244 UniqueTypeAliasDiagnosticHelper UniqueTypeAlias;
2245 InsertOnce<SwappedEqualQualTypePair, 8> UniqueBindPower;
2246 InsertOnce<SwappedEqualQualTypePair, 8> UniqueImplicitConversion;
2248 for (
const model::Mix &M : R.Mixes) {
2249 assert(M.mixable() &&
"Sentinel or false mix in result.");
2256 const ParmVarDecl *LVar = M.First;
2257 const ParmVarDecl *RVar = M.Second;
2258 const QualType LType = LVar->getType();
2259 const QualType RType = RVar->getType();
2260 const QualType CommonType = M.commonUnderlyingType();
2261 const std::string LTypeStr = LType.getAsString(
PP);
2262 const std::string RTypeStr = RType.getAsString(
PP);
2263 const std::string CommonTypeStr = CommonType.getAsString(
PP);
2265 if (hasFlag(M.flags(), MixFlags::TypeAlias) &&
2266 UniqueTypeAlias(LType, RType, CommonType)) {
2268 bool ExplicitlyPrintCommonType =
false;
2269 if (LTypeStr == CommonTypeStr || RTypeStr == CommonTypeStr) {
2270 if (hasFlag(M.flags(), MixFlags::Qualifiers))
2271 DiagText =
"after resolving type aliases, '%0' and '%1' share a "
2275 "after resolving type aliases, '%0' and '%1' are the same";
2276 }
else if (!CommonType.isNull()) {
2277 DiagText =
"after resolving type aliases, the common type of '%0' "
2279 ExplicitlyPrintCommonType =
true;
2283 diag(LVar->getOuterLocStart(), DiagText, DiagnosticIDs::Note)
2284 << LTypeStr << RTypeStr;
2285 if (ExplicitlyPrintCommonType)
2286 Diag << CommonTypeStr;
2289 if ((hasFlag(M.flags(), MixFlags::ReferenceBind) ||
2290 hasFlag(M.flags(), MixFlags::Qualifiers)) &&
2291 UniqueBindPower({LType, RType})) {
2292 const StringRef DiagText =
2293 "'%0' and '%1' parameters accept and bind the "
2294 "same kind of values";
2295 diag(RVar->getOuterLocStart(), DiagText, DiagnosticIDs::Note)
2296 << LTypeStr << RTypeStr;
2300 UniqueImplicitConversion({LType, RType})) {
2301 const model::ConversionSequence <R =
2302 M.leftToRightConversionSequence();
2303 const model::ConversionSequence &RTL =
2304 M.rightToLeftConversionSequence();
2305 const FormattedConversionSequence LTRFmt{
PP, LTypeStr, LTR, RTypeStr};
2306 const FormattedConversionSequence RTLFmt{
PP, RTypeStr, RTL, LTypeStr};
2308 StringRef DiagText =
"'%0' and '%1' may be implicitly converted";
2309 if (!LTRFmt.Trivial || !RTLFmt.Trivial)
2310 DiagText =
"'%0' and '%1' may be implicitly converted: %2, %3";
2314 diag(RVar->getOuterLocStart(), DiagText, DiagnosticIDs::Note)
2315 << LTypeStr << RTypeStr;
2317 if (!LTRFmt.Trivial || !RTLFmt.Trivial)
2318 Diag << LTRFmt.DiagnosticText << RTLFmt.DiagnosticText;
2321 const StringRef ConversionFunctionDiagText =
2322 "the implicit conversion involves the "
2323 "%select{|converting constructor|conversion operator}0 "
2325 if (
const FunctionDecl *LFD = LTR.getUserDefinedConversionFunction())
2326 diag(LFD->getLocation(), ConversionFunctionDiagText,
2327 DiagnosticIDs::Note)
2328 <<
static_cast<unsigned>(LTR.UDConvKind)
2329 << LTR.getUserDefinedConversionHighlight();
2330 if (
const FunctionDecl *RFD = RTL.getUserDefinedConversionFunction())
2331 diag(RFD->getLocation(), ConversionFunctionDiagText,
2332 DiagnosticIDs::Note)
2333 <<
static_cast<unsigned>(RTL.UDConvKind)
2334 << RTL.getUserDefinedConversionHighlight();
Every ClangTidyCheck reports errors through a DiagnosticsEngine provided by this context.
Finds function definitions where parameters of convertible types follow each other directly,...
EasilySwappableParametersCheck(StringRef Name, ClangTidyContext *Context)
const std::size_t MinimumLength
The minimum length of an adjacent swappable parameter range required for a diagnostic.
const bool ModelImplicitConversions
Whether to model implicit conversions "in full" (conditions apply) during analysis and consider types...
const bool QualifiersMix
Whether to consider differently qualified versions of the same type mixable.
void storeOptions(ClangTidyOptions::OptionMap &Opts) override
const bool SuppressParametersUsedTogether
If enabled, diagnostics for parameters that are used together in a similar way are not emitted.
void check(const ast_matchers::MatchFinder::MatchResult &Result) override
void registerMatchers(ast_matchers::MatchFinder *Finder) override
const std::vector< StringRef > IgnoredParameterNames
The parameter names (as written in the source text) to be ignored.
const std::size_t NamePrefixSuffixSilenceDissimilarityThreshold
The number of characters two parameter names might be dissimilar at either end for the report about t...
const std::vector< StringRef > IgnoredParameterTypeSuffixes
The parameter typename suffixes (as written in the source code) to be ignored.
Helper class that is used to detect if two parameters of the same function are used in a similar fash...
SimilarlyUsedParameterPairSuppressor(const FunctionDecl *FD, bool Enable)
bool operator()(const ParmVarDecl *Param1, const ParmVarDecl *Param2) const
Returns whether the specified two parameters are deemed similarly used or related by the heuristics.
bool operator==(const Inclusion &LHS, const Inclusion &RHS)
DeclRelationSet operator|(DeclRelation L, DeclRelation R)
std::vector< std::string > match(const SymbolIndex &I, const FuzzyFindRequest &Req, bool *Incomplete)
bool operator<(const Ref &L, const Ref &R)
IncludeGraphNode::SourceFlag & operator|=(IncludeGraphNode::SourceFlag &A, IncludeGraphNode::SourceFlag B)
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()
static std::string formatMixFlags(MixFlags F)
Formats the MixFlags enum into a useful, user-readable representation.
static MixData calculateMixability(const TheCheck &Check, QualType LType, QualType RType, const ASTContext &Ctx, ImplicitConversionModellingMode ImplicitMode)
Approximate the way how LType and RType might refer to "essentially thesame" type,...
static std::optional< ConversionSequence > tryConversionOperators(const TheCheck &Check, const CXXRecordDecl *RD, QualType ToType)
static NonCVRQualifiersResult getNonCVRQualifiers(const ASTContext &Ctx, QualType LType, QualType RType)
Returns if the two types are qualified in a way that ever after equating or removing local CVR qualif...
static MixData isLRefEquallyBindingToType(const TheCheck &Check, const LValueReferenceType *LRef, QualType Ty, const ASTContext &Ctx, bool IsRefRHS, ImplicitConversionModellingMode ImplicitMode)
Calculates if the reference binds an expression of the given type.
static bool hasFlag(MixFlags Data, MixFlags SearchedFlag)
Returns whether the SearchedFlag is turned on in the Data.
static bool needsToElaborateImplicitConversion(const model::Mix &M)
Returns whether a particular Mix between the two parameters should have implicit conversions elaborat...
static SmallString< 64 > getNameOrUnnamed(const NamedDecl *ND)
Returns the diagnostic-friendly name of the node, or a constant value.
static constexpr StringRef DefaultIgnoredParameterTypeSuffixes
The default value for ignored parameter type suffixes.
static constexpr bool DefaultQualifiersMix
The default value for the QualifiersMix check option.
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.
static constexpr StringRef DefaultIgnoredParameterNames
The default value for ignored parameter names.
EasilySwappableParametersCheck TheCheck
static constexpr bool DefaultSuppressParametersUsedTogether
The default value for suppressing diagnostics about parameters that are used together.
static constexpr std::size_t DefaultMinimumLength
The default value for the MinimumLength check option.
static constexpr std::size_t DefaultNamePrefixSuffixSilenceDissimilarityTreshold
The default value for the NamePrefixSuffixSilenceDissimilarityThreshold check option.
static constexpr bool DefaultModelImplicitConversions
The default value for the ModelImplicitConversions check option.
std::string serializeStringList(ArrayRef< StringRef > Strings)
Serialize a sequence of names that can be parsed by parseStringList.
llvm::StringMap< ClangTidyValue > OptionMap