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 const 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 const 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 assert(BestConversion &&
1188 "BestConversion must be set if HowManyGoodConversions is 1");
1189 LLVM_DEBUG(llvm::dbgs()
1190 <<
"--- selectUserDefinedConv. Unique result. Flags: "
1192 return BestConversion;
1195 LLVM_DEBUG(llvm::dbgs()
1196 <<
"--- selectUserDefinedConv. No, or ambiguous.\n");
1201 SmallVector<PreparedConversion, 2> FlaggedConversions;
1207static std::optional<ConversionSequence>
1210 if (!RD || !RD->isCompleteDefinition())
1212 RD = RD->getDefinition();
1214 LLVM_DEBUG(llvm::dbgs() <<
">>> tryConversionOperators: " << RD->getName()
1216 ToType.dump(llvm::dbgs(), RD->getASTContext());
1217 llvm::dbgs() <<
'\n';);
1219 UserDefinedConversionSelector ConversionSet{Check};
1221 for (
const NamedDecl *Method : RD->getVisibleConversionFunctions()) {
1222 const auto *Con = dyn_cast<CXXConversionDecl>(Method);
1223 if (!Con || Con->isExplicit())
1225 LLVM_DEBUG(llvm::dbgs() <<
"--- tryConversionOperators. Trying:\n";
1226 Con->dump(llvm::dbgs()); llvm::dbgs() <<
'\n';);
1230 ConversionSet.addConversion(Con, Con->getConversionType(), ToType);
1233 if (std::optional<UserDefinedConversionSelector::PreparedConversion>
1234 SelectedConversion = ConversionSet()) {
1235 const CanQualType RecordType = RD->getASTContext().getCanonicalTagType(RD);
1237 ConversionSequence Result{RecordType, ToType};
1242 Result.AfterSecondStandard = SelectedConversion->Seq.AfterFirstStandard;
1244 ConversionSequence::UserDefinedConversionOperator ConvOp;
1245 ConvOp.Fun = cast<CXXConversionDecl>(SelectedConversion->ConversionFun);
1246 ConvOp.UserDefinedType = RecordType;
1247 ConvOp.ConversionOperatorResultType = ConvOp.Fun->getConversionType();
1248 Result.setConversion(ConvOp);
1250 LLVM_DEBUG(llvm::dbgs() <<
"<<< tryConversionOperators. Found result.\n");
1254 LLVM_DEBUG(llvm::dbgs() <<
"<<< tryConversionOperators. No conversion.\n");
1258static std::optional<ConversionSequence>
1260 const CXXRecordDecl *RD) {
1261 if (!RD || !RD->isCompleteDefinition())
1263 RD = RD->getDefinition();
1265 LLVM_DEBUG(llvm::dbgs() <<
">>> tryConveringConstructors: " << RD->getName()
1267 FromType.dump(llvm::dbgs(), RD->getASTContext());
1268 llvm::dbgs() <<
'\n';);
1270 UserDefinedConversionSelector ConversionSet{Check};
1272 for (
const CXXConstructorDecl *Con : RD->ctors()) {
1273 if (Con->isCopyOrMoveConstructor() ||
1274 !Con->isConvertingConstructor(
false))
1276 LLVM_DEBUG(llvm::dbgs() <<
"--- tryConvertingConstructors. Trying:\n";
1277 Con->dump(llvm::dbgs()); llvm::dbgs() <<
'\n';);
1281 ConversionSet.addConversion(Con, FromType, Con->getParamDecl(0)->getType());
1284 if (std::optional<UserDefinedConversionSelector::PreparedConversion>
1285 SelectedConversion = ConversionSet()) {
1286 const CanQualType RecordType = RD->getASTContext().getCanonicalTagType(RD);
1288 ConversionSequence Result{FromType, RecordType};
1289 Result.AfterFirstStandard = SelectedConversion->Seq.AfterFirstStandard;
1291 ConversionSequence::UserDefinedConvertingConstructor Ctor;
1292 Ctor.Fun = cast<CXXConstructorDecl>(SelectedConversion->ConversionFun);
1293 Ctor.ConstructorParameterType = Ctor.Fun->getParamDecl(0)->getType();
1294 Ctor.UserDefinedType = RecordType;
1295 Result.setConversion(Ctor);
1297 LLVM_DEBUG(llvm::dbgs()
1298 <<
"<<< tryConvertingConstructors. Found result.\n");
1302 LLVM_DEBUG(llvm::dbgs() <<
"<<< tryConvertingConstructors. No conversion.\n");
1313 QualType RType,
const ASTContext &Ctx,
1314 ImplicitConversionModellingMode ImplicitMode) {
1315 LLVM_DEBUG(llvm::dbgs() <<
">>> approximateImplicitConversion for LType:\n";
1316 LType.dump(llvm::dbgs(), Ctx); llvm::dbgs() <<
"\nand RType:\n";
1317 RType.dump(llvm::dbgs(), Ctx);
1318 llvm::dbgs() <<
"\nimplicit mode: ";
switch (ImplicitMode) {
1319 case ImplicitConversionModellingMode::None:
1320 llvm::dbgs() <<
"None";
1322 case ImplicitConversionModellingMode::All:
1323 llvm::dbgs() <<
"All";
1325 case ImplicitConversionModellingMode::OneWaySingleStandardOnly:
1326 llvm::dbgs() <<
"OneWay, Single, STD Only";
1328 } llvm::dbgs() <<
'\n';);
1330 return {MixFlags::Trivial, LType};
1336 ConversionSequence ImplicitSeq{LType, RType};
1337 QualType WorkType = LType;
1339 std::optional<QualType> AfterFirstStdConv =
1341 if (AfterFirstStdConv) {
1342 LLVM_DEBUG(llvm::dbgs() <<
"--- approximateImplicitConversion. Standard "
1343 "Pre-Conversion found!\n");
1344 ImplicitSeq.AfterFirstStandard = *AfterFirstStdConv;
1345 WorkType = ImplicitSeq.AfterFirstStandard;
1348 if (ImplicitMode == ImplicitConversionModellingMode::OneWaySingleStandardOnly)
1350 return {ImplicitSeq.AfterFirstStandard.isNull()
1352 : MixFlags::ImplicitConversion,
1355 if (Ctx.getLangOpts().CPlusPlus) {
1356 bool FoundConversionOperator =
false, FoundConvertingCtor =
false;
1358 if (
const auto *LRD = WorkType->getAsCXXRecordDecl()) {
1359 std::optional<ConversionSequence> ConversionOperatorResult =
1361 if (ConversionOperatorResult) {
1362 LLVM_DEBUG(llvm::dbgs() <<
"--- approximateImplicitConversion. Found "
1363 "conversion operator.\n");
1364 ImplicitSeq.update(*ConversionOperatorResult);
1365 WorkType = ImplicitSeq.getTypeAfterUserDefinedConversion();
1366 FoundConversionOperator =
true;
1370 if (
const auto *RRD = RType->getAsCXXRecordDecl()) {
1374 std::optional<ConversionSequence> ConvCtorResult =
1376 if (ConvCtorResult) {
1377 LLVM_DEBUG(llvm::dbgs() <<
"--- approximateImplicitConversion. Found "
1378 "converting constructor.\n");
1379 ImplicitSeq.update(*ConvCtorResult);
1380 WorkType = ImplicitSeq.getTypeAfterUserDefinedConversion();
1381 FoundConvertingCtor =
true;
1385 if (FoundConversionOperator && FoundConvertingCtor) {
1387 LLVM_DEBUG(llvm::dbgs()
1388 <<
"<<< approximateImplicitConversion. Found both "
1389 "user-defined conversion kinds in the same sequence!\n");
1390 return {MixFlags::None};
1398 <<
"--- approximateImplicitConversion. Try to find post-conversion.\n");
1400 Check, WorkType, RType, Ctx,
1401 ImplicitConversionModellingMode::OneWaySingleStandardOnly);
1402 if (SecondStdConv.indicatesMixability()) {
1403 LLVM_DEBUG(llvm::dbgs() <<
"--- approximateImplicitConversion. Standard "
1404 "Post-Conversion found!\n");
1409 ImplicitSeq.AfterSecondStandard =
1410 SecondStdConv.Conversion.AfterFirstStandard;
1411 WorkType = ImplicitSeq.AfterSecondStandard;
1415 LLVM_DEBUG(llvm::dbgs()
1416 <<
"<<< approximateImplicitConversion. Found a conversion.\n");
1417 return {MixFlags::ImplicitConversion, ImplicitSeq};
1421 llvm::dbgs() <<
"<<< approximateImplicitConversion. No match found.\n");
1422 return {MixFlags::None};
1426 const TheCheck &Check,
const FunctionDecl *FD, std::size_t StartIndex,
1428 const std::size_t NumParams = FD->getNumParams();
1429 assert(StartIndex < NumParams &&
"out of bounds for start");
1430 const ASTContext &Ctx = FD->getASTContext();
1432 MixableParameterRange Ret;
1434 Ret.NumParamsChecked = 1;
1436 for (std::size_t I = StartIndex + 1; I < NumParams; ++I) {
1437 const ParmVarDecl *Ith = FD->getParamDecl(I);
1438 const StringRef ParamName = Ith->getName();
1439 LLVM_DEBUG(llvm::dbgs()
1440 <<
"Check param #" << I <<
" '" << ParamName <<
"'...\n");
1442 LLVM_DEBUG(llvm::dbgs() <<
"Param #" << I <<
" is ignored. Break!\n");
1446 const StringRef PrevParamName = FD->getParamDecl(I - 1)->getName();
1447 if (!ParamName.empty() && !PrevParamName.empty() &&
1451 LLVM_DEBUG(llvm::dbgs() <<
"Parameter '" << ParamName
1452 <<
"' follows a pattern with previous parameter '"
1453 << PrevParamName <<
"'. Break!\n");
1459 MixableParameterRange::MixVector MixesOfIth;
1460 for (std::size_t J = StartIndex; J < I; ++J) {
1461 const ParmVarDecl *Jth = FD->getParamDecl(J);
1462 LLVM_DEBUG(llvm::dbgs()
1463 <<
"Check mix of #" << J <<
" against #" << I <<
"...\n");
1465 if (isSimilarlyUsedParameter(UsageBasedSuppressor, Ith, Jth)) {
1468 LLVM_DEBUG(llvm::dbgs() <<
"Parameters #" << I <<
" and #" << J
1469 <<
" deemed related, ignoring...\n");
1481 ? ImplicitConversionModellingMode::All
1482 : ImplicitConversionModellingMode::None)};
1483 LLVM_DEBUG(llvm::dbgs() <<
"Mix flags (raw) : "
1486 LLVM_DEBUG(llvm::dbgs() <<
"Mix flags (after sanitize): "
1489 assert(M.flagsValid() &&
"All flags decayed!");
1492 MixesOfIth.emplace_back(std::move(M));
1495 if (MixesOfIth.empty()) {
1498 LLVM_DEBUG(llvm::dbgs()
1500 <<
" does not mix with any in the current range. Break!\n");
1504 Ret.Mixes.insert(Ret.Mixes.end(), MixesOfIth.begin(), MixesOfIth.end());
1505 ++Ret.NumParamsChecked;
1515AST_MATCHER_FUNCTION(ast_matchers::internal::Matcher<Stmt>, paramRefExpr) {
1516 return expr(ignoringParenImpCasts(ignoringElidableConstructorCall(
1517 declRefExpr(to(parmVarDecl().bind(
"param"))))));
1526 LLVM_DEBUG(llvm::dbgs() <<
"Checking if '" << Node->getName()
1527 <<
"' is ignored.\n");
1529 if (!Node->getIdentifier())
1532 const StringRef NodeName = Node->getName();
1534 LLVM_DEBUG(llvm::dbgs() <<
"\tName ignored.\n");
1538 const StringRef NodeTypeName = [Node] {
1539 const ASTContext &Ctx = Node->getASTContext();
1540 const SourceManager &SM = Ctx.getSourceManager();
1541 SourceLocation B = Node->getTypeSpecStartLoc();
1542 SourceLocation E = Node->getTypeSpecEndLoc();
1543 const LangOptions LO;
1545 LLVM_DEBUG(llvm::dbgs() <<
"\tType name code is '"
1546 << Lexer::getSourceText(
1547 CharSourceRange::getTokenRange(B, E), SM, LO)
1549 if (B.isMacroID()) {
1550 LLVM_DEBUG(llvm::dbgs() <<
"\t\tBeginning is macro.\n");
1551 B = SM.getTopMacroCallerLoc(B);
1553 if (E.isMacroID()) {
1554 LLVM_DEBUG(llvm::dbgs() <<
"\t\tEnding is macro.\n");
1555 E = Lexer::getLocForEndOfToken(SM.getTopMacroCallerLoc(E), 0, SM, LO);
1557 LLVM_DEBUG(llvm::dbgs() <<
"\tType name code is '"
1558 << Lexer::getSourceText(
1559 CharSourceRange::getTokenRange(B, E), SM, LO)
1562 return Lexer::getSourceText(CharSourceRange::getTokenRange(B, E), SM, LO);
1565 LLVM_DEBUG(llvm::dbgs() <<
"\tType name is '" << NodeTypeName <<
"'\n");
1567 [NodeTypeName](StringRef E) {
1568 return !E.empty() &&
1569 NodeTypeName.ends_with(E);
1571 LLVM_DEBUG(llvm::dbgs() <<
"\tType suffix ignored.\n");
1584template <
typename T, std::
size_t N = SmallDataStructureSize>
1586 llvm::DenseMap<const ParmVarDecl *, llvm::SmallSet<T, N>>;
1588template <
typename T, std::
size_t N = SmallDataStructureSize>
1590 llvm::DenseMap<const ParmVarDecl *, llvm::SmallPtrSet<T, N>>;
1594template <
typename MapTy,
typename ElemTy>
1597 const auto E1Iterator = Map.find(E1);
1598 auto E2Iterator = Map.find(E2);
1599 if (E1Iterator == Map.end() || E2Iterator == Map.end())
1602 return llvm::any_of(E1Iterator->second, [&E2Iterator](
const auto &E1SetElem) {
1603 return E2Iterator->second.contains(E1SetElem);
1613class AppearsInSameExpr :
public RecursiveASTVisitor<AppearsInSameExpr> {
1614 using Base = RecursiveASTVisitor<AppearsInSameExpr>;
1616 const FunctionDecl *FD;
1617 const Expr *CurrentExprOnlyTreeRoot =
nullptr;
1618 llvm::DenseMap<
const ParmVarDecl *,
1619 llvm::SmallPtrSet<const Expr *, SmallDataStructureSize>>
1620 ParentExprsForParamRefs;
1623 void setup(
const FunctionDecl *FD) {
1625 TraverseFunctionDecl(
const_cast<FunctionDecl *
>(FD));
1628 bool operator()(
const ParmVarDecl *Param1,
const ParmVarDecl *Param2)
const {
1633 bool TraverseDecl(Decl *D) {
1634 CurrentExprOnlyTreeRoot =
nullptr;
1635 return Base::TraverseDecl(D);
1638 bool TraverseStmt(Stmt *S, DataRecursionQueue *Queue =
nullptr) {
1639 if (
const auto *E = dyn_cast_or_null<Expr>(S)) {
1640 bool RootSetInCurrentStackFrame =
false;
1641 if (!CurrentExprOnlyTreeRoot) {
1642 CurrentExprOnlyTreeRoot = E;
1643 RootSetInCurrentStackFrame =
true;
1646 const bool Ret = Base::TraverseStmt(S);
1648 if (RootSetInCurrentStackFrame)
1649 CurrentExprOnlyTreeRoot =
nullptr;
1655 CurrentExprOnlyTreeRoot =
nullptr;
1656 return Base::TraverseStmt(S);
1659 bool VisitDeclRefExpr(DeclRefExpr *DRE) {
1660 if (!CurrentExprOnlyTreeRoot)
1663 if (
auto *PVD = dyn_cast<ParmVarDecl>(DRE->getDecl());
1664 PVD && llvm::find(FD->parameters(), PVD))
1665 ParentExprsForParamRefs[PVD].insert(CurrentExprOnlyTreeRoot);
1675class PassedToSameFunction {
1679 void setup(
const FunctionDecl *FD) {
1680 const auto ParamsAsArgsInFnCalls =
1681 match(functionDecl(forEachDescendant(
1682 callExpr(forEachArgumentWithParam(
1683 paramRefExpr(), parmVarDecl().bind(
"passed-to")))
1684 .bind(
"call-expr"))),
1685 *FD, FD->getASTContext());
1686 for (
const auto &Match : ParamsAsArgsInFnCalls) {
1687 const auto *PassedParamOfThisFn = Match.getNodeAs<ParmVarDecl>(
"param");
1688 const auto *CE = Match.getNodeAs<CallExpr>(
"call-expr");
1689 const auto *PassedToParam = Match.getNodeAs<ParmVarDecl>(
"passed-to");
1690 assert(PassedParamOfThisFn && CE && PassedToParam);
1692 const FunctionDecl *CalledFn = CE->getDirectCallee();
1696 std::optional<unsigned> TargetIdx;
1697 const unsigned NumFnParams = CalledFn->getNumParams();
1698 for (
unsigned Idx = 0; Idx < NumFnParams; ++Idx)
1699 if (CalledFn->getParamDecl(Idx) == PassedToParam)
1700 TargetIdx.emplace(Idx);
1702 assert(TargetIdx &&
"Matched, but didn't find index?");
1703 TargetParams[PassedParamOfThisFn].insert(
1704 {CalledFn->getCanonicalDecl(), *TargetIdx});
1708 bool operator()(
const ParmVarDecl *Param1,
const ParmVarDecl *Param2)
const {
1715class AccessedSameMemberOf {
1719 void setup(
const FunctionDecl *FD) {
1720 const auto MembersCalledOnParams =
match(
1721 functionDecl(forEachDescendant(
1722 memberExpr(hasObjectExpression(paramRefExpr())).bind(
"mem-expr"))),
1723 *FD, FD->getASTContext());
1725 for (
const auto &Match : MembersCalledOnParams) {
1726 const auto *AccessedParam = Match.getNodeAs<ParmVarDecl>(
"param");
1727 const auto *ME = Match.getNodeAs<MemberExpr>(
"mem-expr");
1728 assert(AccessedParam && ME);
1729 AccessedMembers[AccessedParam].insert(
1730 ME->getMemberDecl()->getCanonicalDecl());
1734 bool operator()(
const ParmVarDecl *Param1,
const ParmVarDecl *Param2)
const {
1742 SmallVector<const ParmVarDecl *, SmallDataStructureSize> ReturnedParams;
1745 void setup(
const FunctionDecl *FD) {
1747 const auto ParamReturns =
match(functionDecl(forEachDescendant(returnStmt(
1748 hasReturnValue(paramRefExpr())))),
1749 *FD, FD->getASTContext());
1750 for (
const auto &Match : ParamReturns) {
1751 const auto *ReturnedParam = Match.getNodeAs<ParmVarDecl>(
"param");
1752 assert(ReturnedParam);
1754 if (find(FD->parameters(), ReturnedParam) == FD->param_end())
1760 ReturnedParams.emplace_back(ReturnedParam);
1764 bool operator()(
const ParmVarDecl *Param1,
const ParmVarDecl *Param2)
const {
1765 return llvm::is_contained(ReturnedParams, Param1) &&
1766 llvm::is_contained(ReturnedParams, Param2);
1778 relatedness_heuristic::AppearsInSameExpr SameExpr;
1779 relatedness_heuristic::PassedToSameFunction PassToFun;
1780 relatedness_heuristic::AccessedSameMemberOf SameMember;
1781 relatedness_heuristic::Returned Returns;
1790 PassToFun.setup(FD);
1791 SameMember.setup(FD);
1797 bool operator()(
const ParmVarDecl *Param1,
const ParmVarDecl *Param2)
const {
1801 LLVM_DEBUG(llvm::dbgs()
1802 <<
"::: Matching similar usage / relatedness heuristic...\n");
1804 if (SameExpr(Param1, Param2)) {
1805 LLVM_DEBUG(llvm::dbgs() <<
"::: Used in the same expression.\n");
1809 if (PassToFun(Param1, Param2)) {
1810 LLVM_DEBUG(llvm::dbgs()
1811 <<
"::: Passed to same function in different calls.\n");
1815 if (SameMember(Param1, Param2)) {
1816 LLVM_DEBUG(llvm::dbgs()
1817 <<
"::: Same member field access or method called.\n");
1821 if (Returns(Param1, Param2)) {
1822 LLVM_DEBUG(llvm::dbgs() <<
"::: Both parameter returned.\n");
1826 LLVM_DEBUG(llvm::dbgs() <<
"::: None.\n");
1835 const ParmVarDecl *Param1,
const ParmVarDecl *Param2) {
1836 return Suppressor(Param1, Param2);
1840 while (Str.size() < ToLen)
1841 Str.emplace_back(
'\0');
1845 while (Str.size() < ToLen)
1846 Str.insert(Str.begin(),
'\0');
1851 assert(S1.size() >= N && S2.size() >= N);
1852 const StringRef S1Prefix = S1.take_front(S1.size() - N),
1853 S2Prefix = S2.take_front(S2.size() - N);
1854 return S1Prefix == S2Prefix && !S1Prefix.empty();
1859 assert(S1.size() >= N && S2.size() >= N);
1860 const StringRef S1Suffix = S1.take_back(S1.size() - N),
1861 S2Suffix = S2.take_back(S2.size() - N);
1862 return S1Suffix == S2Suffix && !S1Suffix.empty();
1868 StringRef Str1, StringRef Str2) {
1873 const std::size_t BiggerLength = std::max(Str1.size(), Str2.size());
1875 if (BiggerLength <= Threshold)
1882 SmallString<32> S1PadE{Str1}, S2PadE{Str2};
1887 Threshold, StringRef{S1PadE.begin(), BiggerLength},
1888 StringRef{S2PadE.begin(), BiggerLength}))
1891 SmallString<32> S1PadB{Str1}, S2PadB{Str2};
1896 Threshold, StringRef{S1PadB.begin(), BiggerLength},
1897 StringRef{S2PadB.begin(), BiggerLength}))
1908AST_MATCHER_P(FunctionDecl, parameterCountGE,
unsigned, N) {
1909 return Node.getNumParams() >= N;
1913AST_MATCHER(FunctionDecl, isOverloadedUnaryOrBinaryOperator) {
1914 switch (Node.getOverloadedOperator()) {
1919 case OO_Array_Delete:
1920 case OO_Conditional:
1925 return Node.getNumParams() <= 2;
1940static SmallString<64>
getName(
const NamedDecl *ND) {
1941 SmallString<64> Name;
1942 llvm::raw_svector_ostream OS{Name};
1943 ND->getNameForDiagnostic(OS, ND->getASTContext().getPrintingPolicy(),
false);
1958 using namespace model;
1959 return static_cast<bool>(
1961 (MixFlags::TypeAlias | MixFlags::ReferenceBind | MixFlags::Qualifiers));
1967 return model::hasFlag(M.flags(), model::MixFlags::ImplicitConversion);
1974struct FormattedConversionSequence {
1975 std::string DiagnosticText;
1980 bool Trivial =
true;
1982 FormattedConversionSequence(
const PrintingPolicy &
PP,
1983 StringRef StartTypeAsDiagnosed,
1984 const model::ConversionSequence &Conv,
1985 StringRef DestinationTypeAsDiagnosed) {
1986 llvm::raw_string_ostream OS{DiagnosticText};
1989 OS <<
'\'' << StartTypeAsDiagnosed <<
'\'';
1990 std::string LastAddedType = StartTypeAsDiagnosed.str();
1991 std::size_t NumElementsAdded = 1;
1995 const std::string SeqBeginTypeStr = Conv.Begin.getAsString(
PP);
1996 std::string SeqEndTypeStr = Conv.End.getAsString(
PP);
1997 if (StartTypeAsDiagnosed != SeqBeginTypeStr) {
1998 OS <<
" (as '" << SeqBeginTypeStr <<
"')";
1999 LastAddedType = SeqBeginTypeStr;
2003 const auto AddType = [&](StringRef ToAdd) {
2004 if (LastAddedType != ToAdd && ToAdd != SeqEndTypeStr) {
2005 OS <<
" -> '" << ToAdd <<
"'";
2006 LastAddedType = ToAdd.str();
2010 for (
const QualType InvolvedType : Conv.getInvolvedTypesInSequence())
2012 AddType(InvolvedType.getAsString(
PP));
2014 if (LastAddedType != DestinationTypeAsDiagnosed) {
2015 OS <<
" -> '" << DestinationTypeAsDiagnosed <<
"'";
2016 LastAddedType = DestinationTypeAsDiagnosed.str();
2023 if (DestinationTypeAsDiagnosed != SeqEndTypeStr) {
2024 OS <<
" (as '" << SeqEndTypeStr <<
"')";
2025 LastAddedType = SeqEndTypeStr;
2029 if (Trivial && NumElementsAdded > 2)
2039template <
typename E, std::
size_t N>
class InsertOnce {
2040 llvm::SmallSet<E, N> CalledWith;
2043 bool operator()(E El) {
return CalledWith.insert(std::move(El)).second; }
2045 bool calledWith(
const E &El)
const {
return CalledWith.contains(El); }
2048struct SwappedEqualQualTypePair {
2049 QualType LHSType, RHSType;
2051 bool operator==(
const SwappedEqualQualTypePair &Other)
const {
2052 return (LHSType == Other.LHSType && RHSType == Other.RHSType) ||
2053 (LHSType == Other.RHSType && RHSType == Other.LHSType);
2056 bool operator<(
const SwappedEqualQualTypePair &Other)
const {
2057 return LHSType < Other.LHSType && RHSType < Other.RHSType;
2061struct TypeAliasDiagnosticTuple {
2062 QualType LHSType, RHSType, CommonType;
2064 bool operator==(
const TypeAliasDiagnosticTuple &Other)
const {
2065 return CommonType == Other.CommonType &&
2066 ((LHSType == Other.LHSType && RHSType == Other.RHSType) ||
2067 (LHSType == Other.RHSType && RHSType == Other.LHSType));
2070 bool operator<(
const TypeAliasDiagnosticTuple &Other)
const {
2071 return CommonType < Other.CommonType && LHSType < Other.LHSType &&
2072 RHSType < Other.RHSType;
2077class UniqueTypeAliasDiagnosticHelper
2078 :
public InsertOnce<TypeAliasDiagnosticTuple, 8> {
2079 using Base = InsertOnce<TypeAliasDiagnosticTuple, 8>;
2084 bool operator()(QualType LHSType, QualType RHSType, QualType CommonType) {
2085 if (CommonType.isNull() || CommonType == LHSType || CommonType == RHSType)
2086 return Base::operator()({LHSType, RHSType, {}});
2088 const TypeAliasDiagnosticTuple ThreeTuple{LHSType, RHSType, CommonType};
2089 if (!Base::operator()(ThreeTuple))
2092 const bool AlreadySaidLHSAndCommonIsSame =
2093 calledWith({LHSType, CommonType, {}});
2094 const bool AlreadySaidRHSAndCommonIsSame =
2095 calledWith({RHSType, CommonType, {}});
2096 if (AlreadySaidLHSAndCommonIsSame && AlreadySaidRHSAndCommonIsSame) {
2117 Options.get(
"IgnoredParameterTypeSuffixes",
2123 Options.get(
"SuppressParametersUsedTogether",
2126 Options.get(
"NamePrefixSuffixSilenceDissimilarityThreshold",
2132 Options.store(Opts,
"IgnoredParameterNames",
2134 Options.store(Opts,
"IgnoredParameterTypeSuffixes",
2138 Options.store(Opts,
"SuppressParametersUsedTogether",
2140 Options.store(Opts,
"NamePrefixSuffixSilenceDissimilarityThreshold",
2145 const auto BaseConstraints = functionDecl(
2149 unless(isOverloadedUnaryOrBinaryOperator()));
2152 functionDecl(BaseConstraints,
2153 unless(ast_matchers::isTemplateInstantiation()))
2157 functionDecl(BaseConstraints, isExplicitTemplateSpecialization())
2163 const MatchFinder::MatchResult &Result) {
2164 using namespace model;
2167 const auto *FD = Result.Nodes.getNodeAs<FunctionDecl>(
"func");
2170 const PrintingPolicy &
PP = FD->getASTContext().getPrintingPolicy();
2171 const std::size_t NumParams = FD->getNumParams();
2172 std::size_t MixableRangeStartIndex = 0;
2179 LLVM_DEBUG(llvm::dbgs() <<
"Begin analysis of " <<
getName(FD) <<
" with "
2180 << NumParams <<
" parameters...\n");
2181 while (MixableRangeStartIndex < NumParams) {
2182 if (isIgnoredParameter(*
this, FD->getParamDecl(MixableRangeStartIndex))) {
2183 LLVM_DEBUG(llvm::dbgs()
2184 <<
"Parameter #" << MixableRangeStartIndex <<
" ignored.\n");
2185 ++MixableRangeStartIndex;
2189 MixableParameterRange R = modelMixingRange(
2190 *
this, FD, MixableRangeStartIndex, UsageBasedSuppressor);
2191 assert(R.NumParamsChecked > 0 &&
"Ensure forward progress!");
2192 MixableRangeStartIndex += R.NumParamsChecked;
2194 LLVM_DEBUG(llvm::dbgs() <<
"Ignoring range of " << R.NumParamsChecked
2195 <<
" lower than limit.\n");
2199 const bool NeedsAnyTypeNote =
2201 const bool HasAnyImplicits =
2203 const ParmVarDecl *First = R.getFirstParam(), *Last = R.getLastParam();
2204 const std::string FirstParamTypeAsWritten =
2205 First->getType().getAsString(
PP);
2209 if (HasAnyImplicits)
2210 DiagText =
"%0 adjacent parameters of %1 of convertible types are "
2211 "easily swapped by mistake";
2212 else if (NeedsAnyTypeNote)
2213 DiagText =
"%0 adjacent parameters of %1 of similar type are easily "
2214 "swapped by mistake";
2216 DiagText =
"%0 adjacent parameters of %1 of similar type ('%2') are "
2217 "easily swapped by mistake";
2219 const auto Diag = diag(First->getOuterLocStart(), DiagText)
2220 <<
static_cast<unsigned>(R.NumParamsChecked) << FD;
2221 if (!NeedsAnyTypeNote)
2222 Diag << FirstParamTypeAsWritten;
2224 const CharSourceRange HighlightRange = CharSourceRange::getTokenRange(
2225 First->getBeginLoc(), Last->getEndLoc());
2226 Diag << HighlightRange;
2232 diag(First->getLocation(),
"the first parameter in the range is '%0'",
2233 DiagnosticIDs::Note)
2235 << CharSourceRange::getTokenRange(First->getLocation(),
2236 First->getLocation());
2237 diag(Last->getLocation(),
"the last parameter in the range is '%0'",
2238 DiagnosticIDs::Note)
2240 << CharSourceRange::getTokenRange(Last->getLocation(),
2241 Last->getLocation());
2245 UniqueTypeAliasDiagnosticHelper UniqueTypeAlias;
2246 InsertOnce<SwappedEqualQualTypePair, 8> UniqueBindPower;
2247 InsertOnce<SwappedEqualQualTypePair, 8> UniqueImplicitConversion;
2249 for (
const model::Mix &M : R.Mixes) {
2250 assert(M.mixable() &&
"Sentinel or false mix in result.");
2257 const ParmVarDecl *LVar = M.First;
2258 const ParmVarDecl *RVar = M.Second;
2259 const QualType LType = LVar->getType();
2260 const QualType RType = RVar->getType();
2261 const QualType CommonType = M.commonUnderlyingType();
2262 const std::string LTypeStr = LType.getAsString(
PP);
2263 const std::string RTypeStr = RType.getAsString(
PP);
2264 const std::string CommonTypeStr = CommonType.getAsString(
PP);
2266 if (hasFlag(M.flags(), MixFlags::TypeAlias) &&
2267 UniqueTypeAlias(LType, RType, CommonType)) {
2269 bool ExplicitlyPrintCommonType =
false;
2270 if (LTypeStr == CommonTypeStr || RTypeStr == CommonTypeStr) {
2271 if (hasFlag(M.flags(), MixFlags::Qualifiers))
2272 DiagText =
"after resolving type aliases, '%0' and '%1' share a "
2276 "after resolving type aliases, '%0' and '%1' are the same";
2277 }
else if (!CommonType.isNull()) {
2278 DiagText =
"after resolving type aliases, the common type of '%0' "
2280 ExplicitlyPrintCommonType =
true;
2284 diag(LVar->getOuterLocStart(), DiagText, DiagnosticIDs::Note)
2285 << LTypeStr << RTypeStr;
2286 if (ExplicitlyPrintCommonType)
2287 Diag << CommonTypeStr;
2290 if ((hasFlag(M.flags(), MixFlags::ReferenceBind) ||
2291 hasFlag(M.flags(), MixFlags::Qualifiers)) &&
2292 UniqueBindPower({LType, RType})) {
2293 const StringRef DiagText =
2294 "'%0' and '%1' parameters accept and bind the "
2295 "same kind of values";
2296 diag(RVar->getOuterLocStart(), DiagText, DiagnosticIDs::Note)
2297 << LTypeStr << RTypeStr;
2301 UniqueImplicitConversion({LType, RType})) {
2302 const model::ConversionSequence <R =
2303 M.leftToRightConversionSequence();
2304 const model::ConversionSequence &RTL =
2305 M.rightToLeftConversionSequence();
2306 const FormattedConversionSequence LTRFmt{
PP, LTypeStr, LTR, RTypeStr};
2307 const FormattedConversionSequence RTLFmt{
PP, RTypeStr, RTL, LTypeStr};
2309 StringRef DiagText =
"'%0' and '%1' may be implicitly converted";
2310 if (!LTRFmt.Trivial || !RTLFmt.Trivial)
2311 DiagText =
"'%0' and '%1' may be implicitly converted: %2, %3";
2315 diag(RVar->getOuterLocStart(), DiagText, DiagnosticIDs::Note)
2316 << LTypeStr << RTypeStr;
2318 if (!LTRFmt.Trivial || !RTLFmt.Trivial)
2319 Diag << LTRFmt.DiagnosticText << RTLFmt.DiagnosticText;
2322 const StringRef ConversionFunctionDiagText =
2323 "the implicit conversion involves the "
2324 "%select{|converting constructor|conversion operator}0 "
2326 if (
const FunctionDecl *LFD = LTR.getUserDefinedConversionFunction())
2327 diag(LFD->getLocation(), ConversionFunctionDiagText,
2328 DiagnosticIDs::Note)
2329 <<
static_cast<unsigned>(LTR.UDConvKind)
2330 << LTR.getUserDefinedConversionHighlight();
2331 if (
const FunctionDecl *RFD = RTL.getUserDefinedConversionFunction())
2332 diag(RFD->getLocation(), ConversionFunctionDiagText,
2333 DiagnosticIDs::Note)
2334 <<
static_cast<unsigned>(RTL.UDConvKind)
2335 << 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