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 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 llvm::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");
1566 if (!NodeTypeName.empty()) {
1568 [NodeTypeName](StringRef E) {
1569 return !E.empty() && NodeTypeName.ends_with(E);
1571 LLVM_DEBUG(llvm::dbgs() <<
"\tType suffix ignored.\n");
1585template <
typename T, std::
size_t N = SmallDataStructureSize>
1587 llvm::DenseMap<const ParmVarDecl *, llvm::SmallSet<T, N>>;
1589template <
typename T, std::
size_t N = SmallDataStructureSize>
1591 llvm::DenseMap<const ParmVarDecl *, llvm::SmallPtrSet<T, N>>;
1595template <
typename MapTy,
typename ElemTy>
1598 auto E1Iterator = Map.find(E1);
1599 auto E2Iterator = Map.find(E2);
1600 if (E1Iterator == Map.end() || E2Iterator == Map.end())
1603 return llvm::any_of(E1Iterator->second, [&E2Iterator](
const auto &E1SetElem) {
1604 return E2Iterator->second.contains(E1SetElem);
1614class AppearsInSameExpr :
public RecursiveASTVisitor<AppearsInSameExpr> {
1615 using Base = RecursiveASTVisitor<AppearsInSameExpr>;
1617 const FunctionDecl *FD;
1618 const Expr *CurrentExprOnlyTreeRoot =
nullptr;
1619 llvm::DenseMap<
const ParmVarDecl *,
1620 llvm::SmallPtrSet<const Expr *, SmallDataStructureSize>>
1621 ParentExprsForParamRefs;
1624 void setup(
const FunctionDecl *FD) {
1626 TraverseFunctionDecl(
const_cast<FunctionDecl *
>(FD));
1629 bool operator()(
const ParmVarDecl *Param1,
const ParmVarDecl *Param2)
const {
1634 bool TraverseDecl(Decl *D) {
1635 CurrentExprOnlyTreeRoot =
nullptr;
1636 return Base::TraverseDecl(D);
1639 bool TraverseStmt(Stmt *S, DataRecursionQueue *Queue =
nullptr) {
1640 if (
auto *E = dyn_cast_or_null<Expr>(S)) {
1641 bool RootSetInCurrentStackFrame =
false;
1642 if (!CurrentExprOnlyTreeRoot) {
1643 CurrentExprOnlyTreeRoot = E;
1644 RootSetInCurrentStackFrame =
true;
1647 const bool Ret = Base::TraverseStmt(S);
1649 if (RootSetInCurrentStackFrame)
1650 CurrentExprOnlyTreeRoot =
nullptr;
1656 CurrentExprOnlyTreeRoot =
nullptr;
1657 return Base::TraverseStmt(S);
1660 bool VisitDeclRefExpr(DeclRefExpr *DRE) {
1661 if (!CurrentExprOnlyTreeRoot)
1664 if (
auto *PVD = dyn_cast<ParmVarDecl>(DRE->getDecl()))
1665 if (llvm::find(FD->parameters(), PVD))
1666 ParentExprsForParamRefs[PVD].insert(CurrentExprOnlyTreeRoot);
1676class PassedToSameFunction {
1680 void setup(
const FunctionDecl *FD) {
1681 auto ParamsAsArgsInFnCalls =
1682 match(functionDecl(forEachDescendant(
1683 callExpr(forEachArgumentWithParam(
1684 paramRefExpr(), parmVarDecl().bind(
"passed-to")))
1685 .bind(
"call-expr"))),
1686 *FD, FD->getASTContext());
1687 for (
const auto &Match : ParamsAsArgsInFnCalls) {
1688 const auto *PassedParamOfThisFn = Match.getNodeAs<ParmVarDecl>(
"param");
1689 const auto *CE = Match.getNodeAs<CallExpr>(
"call-expr");
1690 const auto *PassedToParam = Match.getNodeAs<ParmVarDecl>(
"passed-to");
1691 assert(PassedParamOfThisFn && CE && PassedToParam);
1693 const FunctionDecl *CalledFn = CE->getDirectCallee();
1697 std::optional<unsigned> TargetIdx;
1698 const unsigned NumFnParams = CalledFn->getNumParams();
1699 for (
unsigned Idx = 0; Idx < NumFnParams; ++Idx)
1700 if (CalledFn->getParamDecl(Idx) == PassedToParam)
1701 TargetIdx.emplace(Idx);
1703 assert(TargetIdx &&
"Matched, but didn't find index?");
1704 TargetParams[PassedParamOfThisFn].insert(
1705 {CalledFn->getCanonicalDecl(), *TargetIdx});
1709 bool operator()(
const ParmVarDecl *Param1,
const ParmVarDecl *Param2)
const {
1716class AccessedSameMemberOf {
1720 void setup(
const FunctionDecl *FD) {
1721 auto MembersCalledOnParams =
match(
1722 functionDecl(forEachDescendant(
1723 memberExpr(hasObjectExpression(paramRefExpr())).bind(
"mem-expr"))),
1724 *FD, FD->getASTContext());
1726 for (
const auto &Match : MembersCalledOnParams) {
1727 const auto *AccessedParam = Match.getNodeAs<ParmVarDecl>(
"param");
1728 const auto *ME = Match.getNodeAs<MemberExpr>(
"mem-expr");
1729 assert(AccessedParam && ME);
1730 AccessedMembers[AccessedParam].insert(
1731 ME->getMemberDecl()->getCanonicalDecl());
1735 bool operator()(
const ParmVarDecl *Param1,
const ParmVarDecl *Param2)
const {
1743 llvm::SmallVector<const ParmVarDecl *, SmallDataStructureSize> ReturnedParams;
1746 void setup(
const FunctionDecl *FD) {
1748 auto ParamReturns =
match(functionDecl(forEachDescendant(
1749 returnStmt(hasReturnValue(paramRefExpr())))),
1750 *FD, FD->getASTContext());
1751 for (
const auto &Match : ParamReturns) {
1752 const auto *ReturnedParam = Match.getNodeAs<ParmVarDecl>(
"param");
1753 assert(ReturnedParam);
1755 if (find(FD->parameters(), ReturnedParam) == FD->param_end())
1761 ReturnedParams.emplace_back(ReturnedParam);
1765 bool operator()(
const ParmVarDecl *Param1,
const ParmVarDecl *Param2)
const {
1766 return llvm::is_contained(ReturnedParams, Param1) &&
1767 llvm::is_contained(ReturnedParams, Param2);
1779 relatedness_heuristic::AppearsInSameExpr SameExpr;
1780 relatedness_heuristic::PassedToSameFunction PassToFun;
1781 relatedness_heuristic::AccessedSameMemberOf SameMember;
1782 relatedness_heuristic::Returned Returns;
1791 PassToFun.setup(FD);
1792 SameMember.setup(FD);
1798 bool operator()(
const ParmVarDecl *Param1,
const ParmVarDecl *Param2)
const {
1802 LLVM_DEBUG(llvm::dbgs()
1803 <<
"::: Matching similar usage / relatedness heuristic...\n");
1805 if (SameExpr(Param1, Param2)) {
1806 LLVM_DEBUG(llvm::dbgs() <<
"::: Used in the same expression.\n");
1810 if (PassToFun(Param1, Param2)) {
1811 LLVM_DEBUG(llvm::dbgs()
1812 <<
"::: Passed to same function in different calls.\n");
1816 if (SameMember(Param1, Param2)) {
1817 LLVM_DEBUG(llvm::dbgs()
1818 <<
"::: Same member field access or method called.\n");
1822 if (Returns(Param1, Param2)) {
1823 LLVM_DEBUG(llvm::dbgs() <<
"::: Both parameter returned.\n");
1827 LLVM_DEBUG(llvm::dbgs() <<
"::: None.\n");
1836 const ParmVarDecl *Param1,
const ParmVarDecl *Param2) {
1837 return Suppressor(Param1, Param2);
1841 while (Str.size() < ToLen)
1842 Str.emplace_back(
'\0');
1846 while (Str.size() < ToLen)
1847 Str.insert(Str.begin(),
'\0');
1852 assert(S1.size() >= N && S2.size() >= N);
1853 const StringRef S1Prefix = S1.take_front(S1.size() - N),
1854 S2Prefix = S2.take_front(S2.size() - N);
1855 return S1Prefix == S2Prefix && !S1Prefix.empty();
1860 assert(S1.size() >= N && S2.size() >= N);
1861 const StringRef S1Suffix = S1.take_back(S1.size() - N),
1862 S2Suffix = S2.take_back(S2.size() - N);
1863 return S1Suffix == S2Suffix && !S1Suffix.empty();
1869 StringRef Str1, StringRef Str2) {
1874 const std::size_t BiggerLength = std::max(Str1.size(), Str2.size());
1876 if (BiggerLength <= Threshold)
1883 SmallString<32> S1PadE{Str1}, S2PadE{Str2};
1888 Threshold, StringRef{S1PadE.begin(), BiggerLength},
1889 StringRef{S2PadE.begin(), BiggerLength}))
1892 SmallString<32> S1PadB{Str1}, S2PadB{Str2};
1897 Threshold, StringRef{S1PadB.begin(), BiggerLength},
1898 StringRef{S2PadB.begin(), BiggerLength}))
1909AST_MATCHER_P(FunctionDecl, parameterCountGE,
unsigned, N) {
1910 return Node.getNumParams() >= N;
1914AST_MATCHER(FunctionDecl, isOverloadedUnaryOrBinaryOperator) {
1915 switch (Node.getOverloadedOperator()) {
1920 case OO_Array_Delete:
1921 case OO_Conditional:
1926 return Node.getNumParams() <= 2;
1941static SmallString<64>
getName(
const NamedDecl *ND) {
1942 SmallString<64> Name;
1943 llvm::raw_svector_ostream OS{Name};
1944 ND->getNameForDiagnostic(OS, ND->getASTContext().getPrintingPolicy(),
false);
1959 using namespace model;
1960 return static_cast<bool>(
1962 (MixFlags::TypeAlias | MixFlags::ReferenceBind | MixFlags::Qualifiers));
1968 return model::hasFlag(M.flags(), model::MixFlags::ImplicitConversion);
1975struct FormattedConversionSequence {
1976 std::string DiagnosticText;
1981 bool Trivial =
true;
1983 FormattedConversionSequence(
const PrintingPolicy &
PP,
1984 StringRef StartTypeAsDiagnosed,
1985 const model::ConversionSequence &Conv,
1986 StringRef DestinationTypeAsDiagnosed) {
1987 llvm::raw_string_ostream OS{DiagnosticText};
1990 OS <<
'\'' << StartTypeAsDiagnosed <<
'\'';
1991 std::string LastAddedType = StartTypeAsDiagnosed.str();
1992 std::size_t NumElementsAdded = 1;
1996 const std::string SeqBeginTypeStr = Conv.Begin.getAsString(
PP);
1997 std::string SeqEndTypeStr = Conv.End.getAsString(
PP);
1998 if (StartTypeAsDiagnosed != SeqBeginTypeStr) {
1999 OS <<
" (as '" << SeqBeginTypeStr <<
"')";
2000 LastAddedType = SeqBeginTypeStr;
2004 auto AddType = [&](StringRef ToAdd) {
2005 if (LastAddedType != ToAdd && ToAdd != SeqEndTypeStr) {
2006 OS <<
" -> '" << ToAdd <<
"'";
2007 LastAddedType = ToAdd.str();
2011 for (
const QualType InvolvedType : Conv.getInvolvedTypesInSequence())
2013 AddType(InvolvedType.getAsString(
PP));
2015 if (LastAddedType != DestinationTypeAsDiagnosed) {
2016 OS <<
" -> '" << DestinationTypeAsDiagnosed <<
"'";
2017 LastAddedType = DestinationTypeAsDiagnosed.str();
2024 if (DestinationTypeAsDiagnosed != SeqEndTypeStr) {
2025 OS <<
" (as '" << SeqEndTypeStr <<
"')";
2026 LastAddedType = SeqEndTypeStr;
2030 if (Trivial && NumElementsAdded > 2)
2040template <
typename E, std::
size_t N>
class InsertOnce {
2041 llvm::SmallSet<E, N> CalledWith;
2044 bool operator()(E El) {
return CalledWith.insert(std::move(El)).second; }
2046 bool calledWith(
const E &El)
const {
return CalledWith.contains(El); }
2049struct SwappedEqualQualTypePair {
2050 QualType LHSType, RHSType;
2052 bool operator==(
const SwappedEqualQualTypePair &Other)
const {
2053 return (LHSType == Other.LHSType && RHSType == Other.RHSType) ||
2054 (LHSType == Other.RHSType && RHSType == Other.LHSType);
2057 bool operator<(
const SwappedEqualQualTypePair &Other)
const {
2058 return LHSType < Other.LHSType && RHSType < Other.RHSType;
2062struct TypeAliasDiagnosticTuple {
2063 QualType LHSType, RHSType, CommonType;
2065 bool operator==(
const TypeAliasDiagnosticTuple &Other)
const {
2066 return CommonType == Other.CommonType &&
2067 ((LHSType == Other.LHSType && RHSType == Other.RHSType) ||
2068 (LHSType == Other.RHSType && RHSType == Other.LHSType));
2071 bool operator<(
const TypeAliasDiagnosticTuple &Other)
const {
2072 return CommonType < Other.CommonType && LHSType < Other.LHSType &&
2073 RHSType < Other.RHSType;
2078class UniqueTypeAliasDiagnosticHelper
2079 :
public InsertOnce<TypeAliasDiagnosticTuple, 8> {
2080 using Base = InsertOnce<TypeAliasDiagnosticTuple, 8>;
2085 bool operator()(QualType LHSType, QualType RHSType, QualType CommonType) {
2086 if (CommonType.isNull() || CommonType == LHSType || CommonType == RHSType)
2087 return Base::operator()({LHSType, RHSType, {}});
2089 const TypeAliasDiagnosticTuple ThreeTuple{LHSType, RHSType, CommonType};
2090 if (!Base::operator()(ThreeTuple))
2093 const bool AlreadySaidLHSAndCommonIsSame =
2094 calledWith({LHSType, CommonType, {}});
2095 const bool AlreadySaidRHSAndCommonIsSame =
2096 calledWith({RHSType, CommonType, {}});
2097 if (AlreadySaidLHSAndCommonIsSame && AlreadySaidRHSAndCommonIsSame) {
2118 Options.get(
"IgnoredParameterTypeSuffixes",
2124 Options.get(
"SuppressParametersUsedTogether",
2127 Options.get(
"NamePrefixSuffixSilenceDissimilarityThreshold",
2133 Options.store(Opts,
"IgnoredParameterNames",
2135 Options.store(Opts,
"IgnoredParameterTypeSuffixes",
2139 Options.store(Opts,
"SuppressParametersUsedTogether",
2141 Options.store(Opts,
"NamePrefixSuffixSilenceDissimilarityThreshold",
2146 const auto BaseConstraints = functionDecl(
2150 unless(isOverloadedUnaryOrBinaryOperator()));
2153 functionDecl(BaseConstraints,
2154 unless(ast_matchers::isTemplateInstantiation()))
2158 functionDecl(BaseConstraints, isExplicitTemplateSpecialization())
2164 const MatchFinder::MatchResult &Result) {
2165 using namespace model;
2168 const auto *FD = Result.Nodes.getNodeAs<FunctionDecl>(
"func");
2171 const PrintingPolicy &
PP = FD->getASTContext().getPrintingPolicy();
2172 const std::size_t NumParams = FD->getNumParams();
2173 std::size_t MixableRangeStartIndex = 0;
2180 LLVM_DEBUG(llvm::dbgs() <<
"Begin analysis of " <<
getName(FD) <<
" with "
2181 << NumParams <<
" parameters...\n");
2182 while (MixableRangeStartIndex < NumParams) {
2183 if (isIgnoredParameter(*
this, FD->getParamDecl(MixableRangeStartIndex))) {
2184 LLVM_DEBUG(llvm::dbgs()
2185 <<
"Parameter #" << MixableRangeStartIndex <<
" ignored.\n");
2186 ++MixableRangeStartIndex;
2190 MixableParameterRange R = modelMixingRange(
2191 *
this, FD, MixableRangeStartIndex, UsageBasedSuppressor);
2192 assert(R.NumParamsChecked > 0 &&
"Ensure forward progress!");
2193 MixableRangeStartIndex += R.NumParamsChecked;
2195 LLVM_DEBUG(llvm::dbgs() <<
"Ignoring range of " << R.NumParamsChecked
2196 <<
" lower than limit.\n");
2200 const bool NeedsAnyTypeNote =
2202 const bool HasAnyImplicits =
2204 const ParmVarDecl *First = R.getFirstParam(), *Last = R.getLastParam();
2205 const std::string FirstParamTypeAsWritten =
2206 First->getType().getAsString(
PP);
2210 if (HasAnyImplicits)
2211 DiagText =
"%0 adjacent parameters of %1 of convertible types are "
2212 "easily swapped by mistake";
2213 else if (NeedsAnyTypeNote)
2214 DiagText =
"%0 adjacent parameters of %1 of similar type are easily "
2215 "swapped by mistake";
2217 DiagText =
"%0 adjacent parameters of %1 of similar type ('%2') are "
2218 "easily swapped by mistake";
2220 auto Diag = diag(First->getOuterLocStart(), DiagText)
2221 <<
static_cast<unsigned>(R.NumParamsChecked) << FD;
2222 if (!NeedsAnyTypeNote)
2223 Diag << FirstParamTypeAsWritten;
2225 const CharSourceRange HighlightRange = CharSourceRange::getTokenRange(
2226 First->getBeginLoc(), Last->getEndLoc());
2227 Diag << HighlightRange;
2233 diag(First->getLocation(),
"the first parameter in the range is '%0'",
2234 DiagnosticIDs::Note)
2236 << CharSourceRange::getTokenRange(First->getLocation(),
2237 First->getLocation());
2238 diag(Last->getLocation(),
"the last parameter in the range is '%0'",
2239 DiagnosticIDs::Note)
2241 << CharSourceRange::getTokenRange(Last->getLocation(),
2242 Last->getLocation());
2246 UniqueTypeAliasDiagnosticHelper UniqueTypeAlias;
2247 InsertOnce<SwappedEqualQualTypePair, 8> UniqueBindPower;
2248 InsertOnce<SwappedEqualQualTypePair, 8> UniqueImplicitConversion;
2250 for (
const model::Mix &M : R.Mixes) {
2251 assert(M.mixable() &&
"Sentinel or false mix in result.");
2258 const ParmVarDecl *LVar = M.First;
2259 const ParmVarDecl *RVar = M.Second;
2260 const QualType LType = LVar->getType();
2261 const QualType RType = RVar->getType();
2262 const QualType CommonType = M.commonUnderlyingType();
2263 const std::string LTypeStr = LType.getAsString(
PP);
2264 const std::string RTypeStr = RType.getAsString(
PP);
2265 const std::string CommonTypeStr = CommonType.getAsString(
PP);
2267 if (hasFlag(M.flags(), MixFlags::TypeAlias) &&
2268 UniqueTypeAlias(LType, RType, CommonType)) {
2270 bool ExplicitlyPrintCommonType =
false;
2271 if (LTypeStr == CommonTypeStr || RTypeStr == CommonTypeStr) {
2272 if (hasFlag(M.flags(), MixFlags::Qualifiers))
2273 DiagText =
"after resolving type aliases, '%0' and '%1' share a "
2277 "after resolving type aliases, '%0' and '%1' are the same";
2278 }
else if (!CommonType.isNull()) {
2279 DiagText =
"after resolving type aliases, the common type of '%0' "
2281 ExplicitlyPrintCommonType =
true;
2285 diag(LVar->getOuterLocStart(), DiagText, DiagnosticIDs::Note)
2286 << LTypeStr << RTypeStr;
2287 if (ExplicitlyPrintCommonType)
2288 Diag << CommonTypeStr;
2291 if ((hasFlag(M.flags(), MixFlags::ReferenceBind) ||
2292 hasFlag(M.flags(), MixFlags::Qualifiers)) &&
2293 UniqueBindPower({LType, RType})) {
2294 const StringRef DiagText =
2295 "'%0' and '%1' parameters accept and bind the "
2296 "same kind of values";
2297 diag(RVar->getOuterLocStart(), DiagText, DiagnosticIDs::Note)
2298 << LTypeStr << RTypeStr;
2302 UniqueImplicitConversion({LType, RType})) {
2303 const model::ConversionSequence <R =
2304 M.leftToRightConversionSequence();
2305 const model::ConversionSequence &RTL =
2306 M.rightToLeftConversionSequence();
2307 const FormattedConversionSequence LTRFmt{
PP, LTypeStr, LTR, RTypeStr};
2308 const FormattedConversionSequence RTLFmt{
PP, RTypeStr, RTL, LTypeStr};
2310 StringRef DiagText =
"'%0' and '%1' may be implicitly converted";
2311 if (!LTRFmt.Trivial || !RTLFmt.Trivial)
2312 DiagText =
"'%0' and '%1' may be implicitly converted: %2, %3";
2316 diag(RVar->getOuterLocStart(), DiagText, DiagnosticIDs::Note)
2317 << LTypeStr << RTypeStr;
2319 if (!LTRFmt.Trivial || !RTLFmt.Trivial)
2320 Diag << LTRFmt.DiagnosticText << RTLFmt.DiagnosticText;
2323 const StringRef ConversionFunctionDiagText =
2324 "the implicit conversion involves the "
2325 "%select{|converting constructor|conversion operator}0 "
2327 if (
const FunctionDecl *LFD = LTR.getUserDefinedConversionFunction())
2328 diag(LFD->getLocation(), ConversionFunctionDiagText,
2329 DiagnosticIDs::Note)
2330 <<
static_cast<unsigned>(LTR.UDConvKind)
2331 << LTR.getUserDefinedConversionHighlight();
2332 if (
const FunctionDecl *RFD = RTL.getUserDefinedConversionFunction())
2333 diag(RFD->getLocation(), ConversionFunctionDiagText,
2334 DiagnosticIDs::Note)
2335 <<
static_cast<unsigned>(RTL.UDConvKind)
2336 << 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