2330 lines · cpp
1//===----------------------------------------------------------------------===//2//3// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.4// See https://llvm.org/LICENSE.txt for license information.5// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception6//7//===----------------------------------------------------------------------===//8 9#include "EasilySwappableParametersCheck.h"10#include "../utils/OptionsUtils.h"11#include "clang/AST/ASTContext.h"12#include "clang/AST/RecursiveASTVisitor.h"13#include "clang/ASTMatchers/ASTMatchFinder.h"14#include "clang/Lex/Lexer.h"15#include "llvm/ADT/SmallSet.h"16 17#define DEBUG_TYPE "EasilySwappableParametersCheck"18#include "llvm/Support/Debug.h"19#include <optional>20 21namespace optutils = clang::tidy::utils::options;22 23/// The default value for the MinimumLength check option.24static constexpr std::size_t DefaultMinimumLength = 2;25 26/// The default value for ignored parameter names.27static constexpr llvm::StringLiteral DefaultIgnoredParameterNames = "\"\";"28 "iterator;"29 "Iterator;"30 "begin;"31 "Begin;"32 "end;"33 "End;"34 "first;"35 "First;"36 "last;"37 "Last;"38 "lhs;"39 "LHS;"40 "rhs;"41 "RHS";42 43/// The default value for ignored parameter type suffixes.44static constexpr llvm::StringLiteral DefaultIgnoredParameterTypeSuffixes =45 "bool;"46 "Bool;"47 "_Bool;"48 "it;"49 "It;"50 "iterator;"51 "Iterator;"52 "inputit;"53 "InputIt;"54 "forwardit;"55 "ForwardIt;"56 "bidirit;"57 "BidirIt;"58 "constiterator;"59 "const_iterator;"60 "Const_Iterator;"61 "Constiterator;"62 "ConstIterator;"63 "RandomIt;"64 "randomit;"65 "random_iterator;"66 "ReverseIt;"67 "reverse_iterator;"68 "reverse_const_iterator;"69 "ConstReverseIterator;"70 "Const_Reverse_Iterator;"71 "const_reverse_iterator;"72 "Constreverseiterator;"73 "constreverseiterator";74 75/// The default value for the QualifiersMix check option.76static constexpr bool DefaultQualifiersMix = false;77 78/// The default value for the ModelImplicitConversions check option.79static constexpr bool DefaultModelImplicitConversions = true;80 81/// The default value for suppressing diagnostics about parameters that are82/// used together.83static constexpr bool DefaultSuppressParametersUsedTogether = true;84 85/// The default value for the NamePrefixSuffixSilenceDissimilarityThreshold86/// check option.87static constexpr std::size_t88 DefaultNamePrefixSuffixSilenceDissimilarityTreshold = 1;89 90using namespace clang::ast_matchers;91 92namespace clang::tidy::bugprone {93 94using TheCheck = EasilySwappableParametersCheck;95 96namespace filter {97class SimilarlyUsedParameterPairSuppressor;98 99static bool isIgnoredParameter(const TheCheck &Check, const ParmVarDecl *Node);100static inline bool101isSimilarlyUsedParameter(const SimilarlyUsedParameterPairSuppressor &Suppressor,102 const ParmVarDecl *Param1, const ParmVarDecl *Param2);103static bool prefixSuffixCoverUnderThreshold(std::size_t Threshold,104 StringRef Str1, StringRef Str2);105} // namespace filter106 107namespace model {108 109/// The language features involved in allowing the mix between two parameters.110enum class MixFlags : unsigned char {111 Invalid = 0, ///< Sentinel bit pattern. DO NOT USE!112 113 /// Certain constructs (such as pointers to noexcept/non-noexcept functions)114 /// have the same CanonicalType, which would result in false positives.115 /// During the recursive modelling call, this flag is set if a later diagnosed116 /// canonical type equivalence should be thrown away.117 WorkaroundDisableCanonicalEquivalence = 1,118 119 None = 2, ///< Mix between the two parameters is not possible.120 Trivial = 4, ///< The two mix trivially, and are the exact same type.121 Canonical = 8, ///< The two mix because the types refer to the same122 /// CanonicalType, but we do not elaborate as to how.123 TypeAlias = 16, ///< The path from one type to the other involves124 /// desugaring type aliases.125 ReferenceBind = 32, ///< The mix involves the binding power of "const &".126 Qualifiers = 64, ///< The mix involves change in the qualifiers.127 ImplicitConversion = 128, ///< The mixing of the parameters is possible128 /// through implicit conversions between the types.129 130 LLVM_MARK_AS_BITMASK_ENUM(/* LargestValue =*/ImplicitConversion)131};132LLVM_ENABLE_BITMASK_ENUMS_IN_NAMESPACE();133 134/// Returns whether the SearchedFlag is turned on in the Data.135static inline bool hasFlag(MixFlags Data, MixFlags SearchedFlag) {136 assert(SearchedFlag != MixFlags::Invalid &&137 "can't be used to detect lack of all bits!");138 139 // "Data & SearchedFlag" would need static_cast<bool>() in conditions.140 return (Data & SearchedFlag) == SearchedFlag;141}142 143#ifndef NDEBUG144 145// The modelling logic of this check is more complex than usual, and146// potentially hard to understand without the ability to see into the147// representation during the recursive descent. This debug code is only148// compiled in 'Debug' mode, or if LLVM_ENABLE_ASSERTIONS config is turned on.149 150/// Formats the MixFlags enum into a useful, user-readable representation.151static inline std::string formatMixFlags(MixFlags F) {152 if (F == MixFlags::Invalid)153 return "#Inv!";154 155 SmallString<8> Str{"-------"};156 157 if (hasFlag(F, MixFlags::None))158 // Shows the None bit explicitly, as it can be applied in the recursion159 // even if other bits are set.160 Str[0] = '!';161 if (hasFlag(F, MixFlags::Trivial))162 Str[1] = 'T';163 if (hasFlag(F, MixFlags::Canonical))164 Str[2] = 'C';165 if (hasFlag(F, MixFlags::TypeAlias))166 Str[3] = 't';167 if (hasFlag(F, MixFlags::ReferenceBind))168 Str[4] = '&';169 if (hasFlag(F, MixFlags::Qualifiers))170 Str[5] = 'Q';171 if (hasFlag(F, MixFlags::ImplicitConversion))172 Str[6] = 'i';173 174 if (hasFlag(F, MixFlags::WorkaroundDisableCanonicalEquivalence))175 Str.append("(~C)");176 177 return Str.str().str();178}179 180#endif // NDEBUG181 182/// The results of the steps of an Implicit Conversion Sequence is saved in183/// an instance of this record.184///185/// A ConversionSequence maps the steps of the conversion with a member for186/// each type involved in the conversion. Imagine going from a hypothetical187/// Complex class to projecting it to the real part as a const double.188///189/// I.e., given:190///191/// struct Complex {192/// operator double() const;193/// };194///195/// void functionBeingAnalysed(Complex C, const double R);196///197/// we will get the following sequence:198///199/// (Begin=) Complex200///201/// The first standard conversion is a qualification adjustment.202/// (AfterFirstStandard=) const Complex203///204/// Then the user-defined conversion is executed.205/// (UDConvOp.ConversionOperatorResultType=) double206///207/// Then this 'double' is qualifier-adjusted to 'const double'.208/// (AfterSecondStandard=) double209///210/// The conversion's result has now been calculated, so it ends here.211/// (End=) double.212///213/// Explicit storing of Begin and End in this record is needed, because214/// getting to what Begin and End here are needs further resolution of types,215/// e.g. in the case of typedefs:216///217/// using Comp = Complex;218/// using CD = const double;219/// void functionBeingAnalysed2(Comp C, CD R);220///221/// In this case, the user will be diagnosed with a potential conversion222/// between the two typedefs as written in the code, but to elaborate the223/// reasoning behind this conversion, we also need to show what the typedefs224/// mean. See FormattedConversionSequence towards the bottom of this file!225struct ConversionSequence {226 enum UserDefinedConversionKind { UDCK_None, UDCK_Ctor, UDCK_Oper };227 228 struct UserDefinedConvertingConstructor {229 const CXXConstructorDecl *Fun;230 QualType ConstructorParameterType;231 QualType UserDefinedType;232 };233 234 struct UserDefinedConversionOperator {235 const CXXConversionDecl *Fun;236 QualType UserDefinedType;237 QualType ConversionOperatorResultType;238 };239 240 /// The type the conversion stared from.241 QualType Begin;242 243 /// The intermediate type after the first Standard Conversion Sequence.244 QualType AfterFirstStandard;245 246 /// The details of the user-defined conversion involved, as a tagged union.247 union {248 char None;249 UserDefinedConvertingConstructor UDConvCtor;250 UserDefinedConversionOperator UDConvOp;251 };252 UserDefinedConversionKind UDConvKind;253 254 /// The intermediate type after performing the second Standard Conversion255 /// Sequence.256 QualType AfterSecondStandard;257 258 /// The result type the conversion targeted.259 QualType End;260 261 ConversionSequence() : None(0), UDConvKind(UDCK_None) {}262 ConversionSequence(QualType From, QualType To)263 : Begin(From), None(0), UDConvKind(UDCK_None), End(To) {}264 265 explicit operator bool() const {266 return !AfterFirstStandard.isNull() || UDConvKind != UDCK_None ||267 !AfterSecondStandard.isNull();268 }269 270 /// Returns all the "steps" (non-unique and non-similar) types involved in271 /// the conversion sequence. This method does **NOT** return Begin and End.272 SmallVector<QualType, 4> getInvolvedTypesInSequence() const {273 SmallVector<QualType, 4> Ret;274 auto EmplaceIfDifferent = [&Ret](QualType QT) {275 if (QT.isNull())276 return;277 if (Ret.empty())278 Ret.emplace_back(QT);279 else if (Ret.back() != QT)280 Ret.emplace_back(QT);281 };282 283 EmplaceIfDifferent(AfterFirstStandard);284 switch (UDConvKind) {285 case UDCK_Ctor:286 EmplaceIfDifferent(UDConvCtor.ConstructorParameterType);287 EmplaceIfDifferent(UDConvCtor.UserDefinedType);288 break;289 case UDCK_Oper:290 EmplaceIfDifferent(UDConvOp.UserDefinedType);291 EmplaceIfDifferent(UDConvOp.ConversionOperatorResultType);292 break;293 case UDCK_None:294 break;295 }296 EmplaceIfDifferent(AfterSecondStandard);297 298 return Ret;299 }300 301 /// Updates the steps of the conversion sequence with the steps from the302 /// other instance.303 ///304 /// \note This method does not check if the resulting conversion sequence is305 /// sensible!306 ConversionSequence &update(const ConversionSequence &RHS) {307 if (!RHS.AfterFirstStandard.isNull())308 AfterFirstStandard = RHS.AfterFirstStandard;309 switch (RHS.UDConvKind) {310 case UDCK_Ctor:311 UDConvKind = UDCK_Ctor;312 UDConvCtor = RHS.UDConvCtor;313 break;314 case UDCK_Oper:315 UDConvKind = UDCK_Oper;316 UDConvOp = RHS.UDConvOp;317 break;318 case UDCK_None:319 break;320 }321 if (!RHS.AfterSecondStandard.isNull())322 AfterSecondStandard = RHS.AfterSecondStandard;323 324 return *this;325 }326 327 /// Sets the user-defined conversion to the given constructor.328 void setConversion(const UserDefinedConvertingConstructor &UDCC) {329 UDConvKind = UDCK_Ctor;330 UDConvCtor = UDCC;331 }332 333 /// Sets the user-defined conversion to the given operator.334 void setConversion(const UserDefinedConversionOperator &UDCO) {335 UDConvKind = UDCK_Oper;336 UDConvOp = UDCO;337 }338 339 /// Returns the type in the conversion that's formally "in our hands" once340 /// the user-defined conversion is executed.341 QualType getTypeAfterUserDefinedConversion() const {342 switch (UDConvKind) {343 case UDCK_Ctor:344 return UDConvCtor.UserDefinedType;345 case UDCK_Oper:346 return UDConvOp.ConversionOperatorResultType;347 case UDCK_None:348 return {};349 }350 llvm_unreachable("Invalid UDConv kind.");351 }352 353 const CXXMethodDecl *getUserDefinedConversionFunction() const {354 switch (UDConvKind) {355 case UDCK_Ctor:356 return UDConvCtor.Fun;357 case UDCK_Oper:358 return UDConvOp.Fun;359 case UDCK_None:360 return {};361 }362 llvm_unreachable("Invalid UDConv kind.");363 }364 365 /// Returns the SourceRange in the text that corresponds to the interesting366 /// part of the user-defined conversion. This is either the parameter type367 /// in a converting constructor, or the conversion result type in a conversion368 /// operator.369 SourceRange getUserDefinedConversionHighlight() const {370 switch (UDConvKind) {371 case UDCK_Ctor:372 return UDConvCtor.Fun->getParamDecl(0)->getSourceRange();373 case UDCK_Oper:374 // getReturnTypeSourceRange() does not work for CXXConversionDecls as the375 // returned type is physically behind the declaration's name ("operator").376 if (const FunctionTypeLoc FTL = UDConvOp.Fun->getFunctionTypeLoc())377 if (const TypeLoc RetLoc = FTL.getReturnLoc())378 return RetLoc.getSourceRange();379 return {};380 case UDCK_None:381 return {};382 }383 llvm_unreachable("Invalid UDConv kind.");384 }385};386 387/// Contains the metadata for the mixability result between two types,388/// independently of which parameters they were calculated from.389struct MixData {390 /// The flag bits of the mix indicating what language features allow for it.391 MixFlags Flags = MixFlags::Invalid;392 393 /// A potentially calculated common underlying type after desugaring, that394 /// both sides of the mix can originate from.395 QualType CommonType;396 397 /// The steps an implicit conversion performs to get from one type to the398 /// other.399 ConversionSequence Conversion, ConversionRTL;400 401 /// True if the MixData was specifically created with only a one-way402 /// conversion modelled.403 bool CreatedFromOneWayConversion = false;404 405 MixData(MixFlags Flags) : Flags(Flags) {}406 MixData(MixFlags Flags, QualType CommonType)407 : Flags(Flags), CommonType(CommonType) {}408 MixData(MixFlags Flags, ConversionSequence Conv)409 : Flags(Flags), Conversion(Conv), CreatedFromOneWayConversion(true) {}410 MixData(MixFlags Flags, ConversionSequence LTR, ConversionSequence RTL)411 : Flags(Flags), Conversion(LTR), ConversionRTL(RTL) {}412 MixData(MixFlags Flags, QualType CommonType, ConversionSequence LTR,413 ConversionSequence RTL)414 : Flags(Flags), CommonType(CommonType), Conversion(LTR),415 ConversionRTL(RTL) {}416 417 void sanitize() {418 assert(Flags != MixFlags::Invalid && "sanitize() called on invalid bitvec");419 420 const MixFlags CanonicalAndWorkaround =421 MixFlags::Canonical | MixFlags::WorkaroundDisableCanonicalEquivalence;422 if ((Flags & CanonicalAndWorkaround) == CanonicalAndWorkaround) {423 // A workaround for too eagerly equivalent canonical types was requested,424 // and a canonical equivalence was proven. Fulfill the request and throw425 // this result away.426 Flags = MixFlags::None;427 return;428 }429 430 if (hasFlag(Flags, MixFlags::None)) {431 // If anywhere down the recursion a potential mix "path" is deemed432 // impossible, throw away all the other bits because the mix is not433 // possible.434 Flags = MixFlags::None;435 return;436 }437 438 if (Flags == MixFlags::Trivial)439 return;440 441 if (static_cast<bool>(Flags ^ MixFlags::Trivial))442 // If the mix involves somewhere trivial equivalence but down the443 // recursion other bit(s) were set, remove the trivial bit, as it is not444 // trivial.445 Flags &= ~MixFlags::Trivial;446 447 bool ShouldHaveImplicitConvFlag = false;448 if (CreatedFromOneWayConversion && Conversion)449 ShouldHaveImplicitConvFlag = true;450 else if (!CreatedFromOneWayConversion && Conversion && ConversionRTL)451 // Only say that we have implicit conversion mix possibility if it is452 // bidirectional. Otherwise, the compiler would report an *actual* swap453 // at a call site...454 ShouldHaveImplicitConvFlag = true;455 456 if (ShouldHaveImplicitConvFlag)457 Flags |= MixFlags::ImplicitConversion;458 else459 Flags &= ~MixFlags::ImplicitConversion;460 }461 462 bool isValid() const { return Flags >= MixFlags::None; }463 464 bool indicatesMixability() const { return Flags > MixFlags::None; }465 466 /// Add the specified flag bits to the flags.467 MixData operator|(MixFlags EnableFlags) const {468 if (CreatedFromOneWayConversion) {469 MixData M{Flags | EnableFlags, Conversion};470 M.CommonType = CommonType;471 return M;472 }473 return {Flags | EnableFlags, CommonType, Conversion, ConversionRTL};474 }475 476 /// Add the specified flag bits to the flags.477 MixData &operator|=(MixFlags EnableFlags) {478 Flags |= EnableFlags;479 return *this;480 }481 482 template <typename F> MixData withCommonTypeTransformed(const F &Func) const {483 if (CommonType.isNull())484 return *this;485 486 const QualType NewCommonType = Func(CommonType);487 488 if (CreatedFromOneWayConversion) {489 MixData M{Flags, Conversion};490 M.CommonType = NewCommonType;491 return M;492 }493 494 return {Flags, NewCommonType, Conversion, ConversionRTL};495 }496};497 498/// A named tuple that contains the information for a mix between two concrete499/// parameters.500struct Mix {501 const ParmVarDecl *First, *Second;502 MixData Data;503 504 Mix(const ParmVarDecl *F, const ParmVarDecl *S, MixData Data)505 : First(F), Second(S), Data(std::move(Data)) {}506 507 void sanitize() { Data.sanitize(); }508 MixFlags flags() const { return Data.Flags; }509 bool flagsValid() const { return Data.isValid(); }510 bool mixable() const { return Data.indicatesMixability(); }511 QualType commonUnderlyingType() const { return Data.CommonType; }512 const ConversionSequence &leftToRightConversionSequence() const {513 return Data.Conversion;514 }515 const ConversionSequence &rightToLeftConversionSequence() const {516 return Data.ConversionRTL;517 }518};519 520// NOLINTNEXTLINE(misc-redundant-expression): Seems to be a bogus warning.521static_assert(std::is_trivially_copyable_v<Mix> &&522 std::is_trivially_move_constructible_v<Mix> &&523 std::is_trivially_move_assignable_v<Mix>,524 "Keep frequently used data simple!");525 526struct MixableParameterRange {527 /// A container for Mixes.528 using MixVector = SmallVector<Mix, 8>;529 530 /// The number of parameters iterated to build the instance.531 std::size_t NumParamsChecked = 0;532 533 /// The individual flags and supporting information for the mixes.534 MixVector Mixes;535 536 /// Gets the leftmost parameter of the range.537 const ParmVarDecl *getFirstParam() const {538 // The first element is the LHS of the very first mix in the range.539 assert(!Mixes.empty());540 return Mixes.front().First;541 }542 543 /// Gets the rightmost parameter of the range.544 const ParmVarDecl *getLastParam() const {545 // The builder function breaks building an instance of this type if it546 // finds something that can not be mixed with the rest, by going *forward*547 // in the list of parameters. So at any moment of break, the RHS of the last548 // element of the mix vector is also the last element of the mixing range.549 assert(!Mixes.empty());550 return Mixes.back().Second;551 }552};553 554/// Helper enum for the recursive calls in the modelling that toggle what kinds555/// of implicit conversions are to be modelled.556enum class ImplicitConversionModellingMode : unsigned char {557 ///< No implicit conversions are modelled.558 None,559 560 ///< The full implicit conversion sequence is modelled.561 All,562 563 ///< Only model a unidirectional implicit conversion and within it only one564 /// standard conversion sequence.565 OneWaySingleStandardOnly566};567 568static MixData569isLRefEquallyBindingToType(const TheCheck &Check,570 const LValueReferenceType *LRef, QualType Ty,571 const ASTContext &Ctx, bool IsRefRHS,572 ImplicitConversionModellingMode ImplicitMode);573 574static MixData575approximateImplicitConversion(const TheCheck &Check, QualType LType,576 QualType RType, const ASTContext &Ctx,577 ImplicitConversionModellingMode ImplicitMode);578 579static inline bool isUselessSugar(const Type *T) {580 return isa<AttributedType, DecayedType, ParenType>(T);581}582 583namespace {584 585struct NonCVRQualifiersResult {586 /// True if the types are qualified in a way that even after equating or587 /// removing local CVR qualification, even if the unqualified types588 /// themselves would mix, the qualified ones don't, because there are some589 /// other local qualifiers that are not equal.590 bool HasMixabilityBreakingQualifiers;591 592 /// The set of equal qualifiers between the two types.593 Qualifiers CommonQualifiers;594};595 596} // namespace597 598/// Returns if the two types are qualified in a way that ever after equating or599/// removing local CVR qualification, even if the unqualified types would mix,600/// the qualified ones don't, because there are some other local qualifiers601/// that aren't equal.602static NonCVRQualifiersResult603getNonCVRQualifiers(const ASTContext &Ctx, QualType LType, QualType RType) {604 LLVM_DEBUG(llvm::dbgs() << ">>> getNonCVRQualifiers for LType:\n";605 LType.dump(llvm::dbgs(), Ctx); llvm::dbgs() << "\nand RType:\n";606 RType.dump(llvm::dbgs(), Ctx); llvm::dbgs() << '\n';);607 Qualifiers LQual = LType.getLocalQualifiers(),608 RQual = RType.getLocalQualifiers();609 610 // Strip potential CVR. That is handled by the check option QualifiersMix.611 LQual.removeCVRQualifiers();612 RQual.removeCVRQualifiers();613 614 NonCVRQualifiersResult Ret;615 Ret.CommonQualifiers = Qualifiers::removeCommonQualifiers(LQual, RQual);616 617 LLVM_DEBUG(llvm::dbgs() << "--- hasNonCVRMixabilityBreakingQualifiers. "618 "Removed common qualifiers: ";619 Ret.CommonQualifiers.print(llvm::dbgs(), Ctx.getPrintingPolicy());620 llvm::dbgs() << "\n\tremaining on LType: ";621 LQual.print(llvm::dbgs(), Ctx.getPrintingPolicy());622 llvm::dbgs() << "\n\tremaining on RType: ";623 RQual.print(llvm::dbgs(), Ctx.getPrintingPolicy());624 llvm::dbgs() << '\n';);625 626 // If there are no other non-cvr non-common qualifiers left, we can deduce627 // that mixability isn't broken.628 Ret.HasMixabilityBreakingQualifiers =629 LQual.hasQualifiers() || RQual.hasQualifiers();630 631 return Ret;632}633 634/// Approximate the way how LType and RType might refer to "essentially the635/// same" type, in a sense that at a particular call site, an expression of636/// type LType and RType might be successfully passed to a variable (in our637/// specific case, a parameter) of type RType and LType, respectively.638/// Note the swapped order!639///640/// The returned data structure is not guaranteed to be properly set, as this641/// function is potentially recursive. It is the caller's responsibility to642/// call sanitize() on the result once the recursion is over.643static MixData644calculateMixability(const TheCheck &Check, QualType LType, QualType RType,645 const ASTContext &Ctx,646 ImplicitConversionModellingMode ImplicitMode) {647 LLVM_DEBUG(llvm::dbgs() << ">>> calculateMixability for LType:\n";648 LType.dump(llvm::dbgs(), Ctx); llvm::dbgs() << "\nand RType:\n";649 RType.dump(llvm::dbgs(), Ctx); llvm::dbgs() << '\n';);650 if (LType == RType) {651 LLVM_DEBUG(llvm::dbgs() << "<<< calculateMixability. Trivial equality.\n");652 return {MixFlags::Trivial, LType};653 }654 655 // Dissolve certain type sugars that do not affect the mixability of one type656 // with the other, and also do not require any sort of elaboration for the657 // user to understand.658 if (isUselessSugar(LType.getTypePtr())) {659 LLVM_DEBUG(llvm::dbgs()660 << "--- calculateMixability. LHS is useless sugar.\n");661 return calculateMixability(Check, LType.getSingleStepDesugaredType(Ctx),662 RType, Ctx, ImplicitMode);663 }664 if (isUselessSugar(RType.getTypePtr())) {665 LLVM_DEBUG(llvm::dbgs()666 << "--- calculateMixability. RHS is useless sugar.\n");667 return calculateMixability(668 Check, LType, RType.getSingleStepDesugaredType(Ctx), Ctx, ImplicitMode);669 }670 671 const auto *LLRef = LType->getAs<LValueReferenceType>();672 const auto *RLRef = RType->getAs<LValueReferenceType>();673 if (LLRef && RLRef) {674 LLVM_DEBUG(llvm::dbgs() << "--- calculateMixability. LHS and RHS are &.\n");675 676 return calculateMixability(Check, LLRef->getPointeeType(),677 RLRef->getPointeeType(), Ctx, ImplicitMode)678 .withCommonTypeTransformed(679 [&Ctx](QualType QT) { return Ctx.getLValueReferenceType(QT); });680 }681 // At a particular call site, what could be passed to a 'T' or 'const T' might682 // also be passed to a 'const T &' without the call site putting a direct683 // side effect on the passed expressions.684 if (LLRef) {685 LLVM_DEBUG(llvm::dbgs() << "--- calculateMixability. LHS is &.\n");686 return isLRefEquallyBindingToType(Check, LLRef, RType, Ctx, false,687 ImplicitMode) |688 MixFlags::ReferenceBind;689 }690 if (RLRef) {691 LLVM_DEBUG(llvm::dbgs() << "--- calculateMixability. RHS is &.\n");692 return isLRefEquallyBindingToType(Check, RLRef, LType, Ctx, true,693 ImplicitMode) |694 MixFlags::ReferenceBind;695 }696 697 if (LType->getAs<TypedefType>()) {698 LLVM_DEBUG(llvm::dbgs() << "--- calculateMixability. LHS is typedef.\n");699 return calculateMixability(Check, LType.getSingleStepDesugaredType(Ctx),700 RType, Ctx, ImplicitMode) |701 MixFlags::TypeAlias;702 }703 if (RType->getAs<TypedefType>()) {704 LLVM_DEBUG(llvm::dbgs() << "--- calculateMixability. RHS is typedef.\n");705 return calculateMixability(Check, LType,706 RType.getSingleStepDesugaredType(Ctx), Ctx,707 ImplicitMode) |708 MixFlags::TypeAlias;709 }710 711 // A parameter of type 'cvr1 T' and another of potentially differently712 // qualified 'cvr2 T' may bind with the same power, if the user so requested.713 //714 // Whether to do this check for the inner unqualified types.715 bool CompareUnqualifiedTypes = false;716 if (LType.getLocalCVRQualifiers() != RType.getLocalCVRQualifiers()) {717 LLVM_DEBUG(if (LType.getLocalCVRQualifiers()) {718 llvm::dbgs() << "--- calculateMixability. LHS has CVR-Qualifiers: ";719 Qualifiers::fromCVRMask(LType.getLocalCVRQualifiers())720 .print(llvm::dbgs(), Ctx.getPrintingPolicy());721 llvm::dbgs() << '\n';722 });723 LLVM_DEBUG(if (RType.getLocalCVRQualifiers()) {724 llvm::dbgs() << "--- calculateMixability. RHS has CVR-Qualifiers: ";725 Qualifiers::fromCVRMask(RType.getLocalCVRQualifiers())726 .print(llvm::dbgs(), Ctx.getPrintingPolicy());727 llvm::dbgs() << '\n';728 });729 730 if (!Check.QualifiersMix) {731 LLVM_DEBUG(llvm::dbgs()732 << "<<< calculateMixability. QualifiersMix turned off - not "733 "mixable.\n");734 return {MixFlags::None};735 }736 737 CompareUnqualifiedTypes = true;738 }739 // Whether the two types had the same CVR qualifiers.740 bool OriginallySameQualifiers = false;741 if (LType.getLocalCVRQualifiers() == RType.getLocalCVRQualifiers() &&742 LType.getLocalCVRQualifiers() != 0) {743 LLVM_DEBUG(if (LType.getLocalCVRQualifiers()) {744 llvm::dbgs()745 << "--- calculateMixability. LHS and RHS have same CVR-Qualifiers: ";746 Qualifiers::fromCVRMask(LType.getLocalCVRQualifiers())747 .print(llvm::dbgs(), Ctx.getPrintingPolicy());748 llvm::dbgs() << '\n';749 });750 751 CompareUnqualifiedTypes = true;752 OriginallySameQualifiers = true;753 }754 755 if (CompareUnqualifiedTypes) {756 NonCVRQualifiersResult AdditionalQuals =757 getNonCVRQualifiers(Ctx, LType, RType);758 if (AdditionalQuals.HasMixabilityBreakingQualifiers) {759 LLVM_DEBUG(llvm::dbgs() << "<<< calculateMixability. Additional "760 "non-equal incompatible qualifiers.\n");761 return {MixFlags::None};762 }763 764 const MixData UnqualifiedMixability =765 calculateMixability(Check, LType.getLocalUnqualifiedType(),766 RType.getLocalUnqualifiedType(), Ctx, ImplicitMode)767 .withCommonTypeTransformed([&AdditionalQuals, &Ctx](QualType QT) {768 // Once the mixability was deduced, apply the qualifiers common769 // to the two type back onto the diagnostic printout.770 return Ctx.getQualifiedType(QT, AdditionalQuals.CommonQualifiers);771 });772 773 if (!OriginallySameQualifiers)774 // User-enabled qualifier change modelled for the mix.775 return UnqualifiedMixability | MixFlags::Qualifiers;776 777 // Apply the same qualifier back into the found common type if they were778 // the same.779 return UnqualifiedMixability.withCommonTypeTransformed(780 [&Ctx, LType](QualType QT) {781 return Ctx.getQualifiedType(QT, LType.getLocalQualifiers());782 });783 }784 785 // Certain constructs match on the last catch-all getCanonicalType() equality,786 // which is perhaps something not what we want. If this variable is true,787 // the canonical type equality will be ignored.788 bool RecursiveReturnDiscardingCanonicalType = false;789 790 if (LType->isPointerType() && RType->isPointerType()) {791 // If both types are pointers, and pointed to the exact same type,792 // LType == RType took care of that. Try to see if the pointee type has793 // some other match. However, this must not consider implicit conversions.794 LLVM_DEBUG(llvm::dbgs()795 << "--- calculateMixability. LHS and RHS are Ptrs.\n");796 MixData MixOfPointee =797 calculateMixability(Check, LType->getPointeeType(),798 RType->getPointeeType(), Ctx,799 ImplicitConversionModellingMode::None)800 .withCommonTypeTransformed(801 [&Ctx](QualType QT) { return Ctx.getPointerType(QT); });802 if (hasFlag(MixOfPointee.Flags,803 MixFlags::WorkaroundDisableCanonicalEquivalence))804 RecursiveReturnDiscardingCanonicalType = true;805 806 MixOfPointee.sanitize();807 if (MixOfPointee.indicatesMixability()) {808 LLVM_DEBUG(llvm::dbgs()809 << "<<< calculateMixability. Pointees are mixable.\n");810 return MixOfPointee;811 }812 }813 814 if (ImplicitMode > ImplicitConversionModellingMode::None) {815 LLVM_DEBUG(llvm::dbgs() << "--- calculateMixability. Start implicit...\n");816 const MixData MixLTR =817 approximateImplicitConversion(Check, LType, RType, Ctx, ImplicitMode);818 LLVM_DEBUG(819 if (hasFlag(MixLTR.Flags, MixFlags::ImplicitConversion)) llvm::dbgs()820 << "--- calculateMixability. Implicit Left -> Right found.\n";);821 822 if (ImplicitMode ==823 ImplicitConversionModellingMode::OneWaySingleStandardOnly &&824 MixLTR.Conversion && !MixLTR.Conversion.AfterFirstStandard.isNull() &&825 MixLTR.Conversion.UDConvKind == ConversionSequence::UDCK_None &&826 MixLTR.Conversion.AfterSecondStandard.isNull()) {827 // The invoker of the method requested only modelling a single standard828 // conversion, in only the forward direction, and they got just that.829 LLVM_DEBUG(llvm::dbgs() << "<<< calculateMixability. Implicit "830 "conversion, one-way, standard-only.\n");831 return {MixFlags::ImplicitConversion, MixLTR.Conversion};832 }833 834 // Otherwise if the invoker requested a full modelling, do the other835 // direction as well.836 const MixData MixRTL =837 approximateImplicitConversion(Check, RType, LType, Ctx, ImplicitMode);838 LLVM_DEBUG(839 if (hasFlag(MixRTL.Flags, MixFlags::ImplicitConversion)) llvm::dbgs()840 << "--- calculateMixability. Implicit Right -> Left found.\n";);841 842 if (MixLTR.Conversion && MixRTL.Conversion) {843 LLVM_DEBUG(844 llvm::dbgs()845 << "<<< calculateMixability. Implicit conversion, bidirectional.\n");846 return {MixFlags::ImplicitConversion, MixLTR.Conversion,847 MixRTL.Conversion};848 }849 }850 851 if (RecursiveReturnDiscardingCanonicalType)852 LLVM_DEBUG(llvm::dbgs() << "--- calculateMixability. Before CanonicalType, "853 "Discard was enabled.\n");854 855 // Certain kinds unfortunately need to be side-stepped for canonical type856 // matching.857 if (LType->getAs<FunctionProtoType>() || RType->getAs<FunctionProtoType>()) {858 // Unfortunately, the canonical type of a function pointer becomes the859 // same even if exactly one is "noexcept" and the other isn't, making us860 // give a false positive report irrespective of implicit conversions.861 LLVM_DEBUG(llvm::dbgs()862 << "--- calculateMixability. Discarding potential canonical "863 "equivalence on FunctionProtoTypes.\n");864 RecursiveReturnDiscardingCanonicalType = true;865 }866 867 MixData MixToReturn{MixFlags::None};868 869 // If none of the previous logic found a match, try if Clang otherwise870 // believes the types to be the same.871 const QualType LCanonical = LType.getCanonicalType();872 if (LCanonical == RType.getCanonicalType()) {873 LLVM_DEBUG(llvm::dbgs()874 << "<<< calculateMixability. Same CanonicalType.\n");875 MixToReturn = {MixFlags::Canonical, LCanonical};876 }877 878 if (RecursiveReturnDiscardingCanonicalType)879 MixToReturn |= MixFlags::WorkaroundDisableCanonicalEquivalence;880 881 LLVM_DEBUG(if (MixToReturn.Flags == MixFlags::None) llvm::dbgs()882 << "<<< calculateMixability. No match found.\n");883 return MixToReturn;884}885 886/// Calculates if the reference binds an expression of the given type. This is887/// true iff 'LRef' is some 'const T &' type, and the 'Ty' is 'T' or 'const T'.888///889/// \param ImplicitMode is forwarded in the possible recursive call to890/// calculateMixability.891static MixData892isLRefEquallyBindingToType(const TheCheck &Check,893 const LValueReferenceType *LRef, QualType Ty,894 const ASTContext &Ctx, bool IsRefRHS,895 ImplicitConversionModellingMode ImplicitMode) {896 LLVM_DEBUG(llvm::dbgs() << ">>> isLRefEquallyBindingToType for LRef:\n";897 LRef->dump(llvm::dbgs(), Ctx); llvm::dbgs() << "\nand Type:\n";898 Ty.dump(llvm::dbgs(), Ctx); llvm::dbgs() << '\n';);899 900 QualType ReferredType = LRef->getPointeeType();901 if (!ReferredType.isLocalConstQualified() &&902 ReferredType->getAs<TypedefType>()) {903 LLVM_DEBUG(904 llvm::dbgs()905 << "--- isLRefEquallyBindingToType. Non-const LRef to Typedef.\n");906 ReferredType = ReferredType.getDesugaredType(Ctx);907 if (!ReferredType.isLocalConstQualified()) {908 LLVM_DEBUG(llvm::dbgs()909 << "<<< isLRefEquallyBindingToType. Typedef is not const.\n");910 return {MixFlags::None};911 }912 913 LLVM_DEBUG(llvm::dbgs() << "--- isLRefEquallyBindingToType. Typedef is "914 "const, considering as const LRef.\n");915 } else if (!ReferredType.isLocalConstQualified()) {916 LLVM_DEBUG(llvm::dbgs()917 << "<<< isLRefEquallyBindingToType. Not const LRef.\n");918 return {MixFlags::None};919 };920 921 assert(ReferredType.isLocalConstQualified() &&922 "Reaching this point means we are sure LRef is effectively a const&.");923 924 if (ReferredType == Ty) {925 LLVM_DEBUG(926 llvm::dbgs()927 << "<<< isLRefEquallyBindingToType. Type of referred matches.\n");928 return {MixFlags::Trivial, ReferredType};929 }930 931 QualType NonConstReferredType = ReferredType;932 NonConstReferredType.removeLocalConst();933 if (NonConstReferredType == Ty) {934 LLVM_DEBUG(llvm::dbgs() << "<<< isLRefEquallyBindingToType. Type of "935 "referred matches to non-const qualified.\n");936 return {MixFlags::Trivial, NonConstReferredType};937 }938 939 LLVM_DEBUG(940 llvm::dbgs()941 << "--- isLRefEquallyBindingToType. Checking mix for underlying type.\n");942 return IsRefRHS ? calculateMixability(Check, Ty, NonConstReferredType, Ctx,943 ImplicitMode)944 : calculateMixability(Check, NonConstReferredType, Ty, Ctx,945 ImplicitMode);946}947 948static inline bool isDerivedToBase(const CXXRecordDecl *Derived,949 const CXXRecordDecl *Base) {950 return Derived && Base && Derived->isCompleteDefinition() &&951 Base->isCompleteDefinition() && Derived->isDerivedFrom(Base);952}953 954static std::optional<QualType>955approximateStandardConversionSequence(const TheCheck &Check, QualType From,956 QualType To, const ASTContext &Ctx) {957 LLVM_DEBUG(llvm::dbgs() << ">>> approximateStdConv for LType:\n";958 From.dump(llvm::dbgs(), Ctx); llvm::dbgs() << "\nand RType:\n";959 To.dump(llvm::dbgs(), Ctx); llvm::dbgs() << '\n';);960 961 // A standard conversion sequence consists of the following, in order:962 // * Maybe either LValue->RValue conv., Array->Ptr conv., Function->Ptr conv.963 // * Maybe Numeric promotion or conversion.964 // * Maybe function pointer conversion.965 // * Maybe qualifier adjustments.966 QualType WorkType = From;967 // Get out the qualifiers of the original type. This will always be968 // re-applied to the WorkType to ensure it is the same qualification as the969 // original From was.970 auto FastQualifiersToApply = static_cast<unsigned>(971 From.split().Quals.getAsOpaqueValue() & Qualifiers::FastMask);972 973 // LValue->RValue is irrelevant for the check, because it is a thing to be974 // done at a call site, and will be performed if need be performed.975 976 // Array->Pointer decay is handled by the main method in desugaring977 // the parameter's DecayedType as "useless sugar".978 979 // Function->Pointer conversions are also irrelevant, because a980 // "FunctionType" cannot be the type of a parameter variable, so this981 // conversion is only meaningful at call sites.982 983 // Numeric promotions and conversions.984 const auto *FromBuiltin = WorkType->getAs<BuiltinType>();985 const auto *ToBuiltin = To->getAs<BuiltinType>();986 const bool FromNumeric = FromBuiltin && (FromBuiltin->isIntegerType() ||987 FromBuiltin->isFloatingType());988 const bool ToNumeric =989 ToBuiltin && (ToBuiltin->isIntegerType() || ToBuiltin->isFloatingType());990 if (FromNumeric && ToNumeric) {991 // If both are integral types, the numeric conversion is performed.992 // Reapply the qualifiers of the original type, however, so993 // "const int -> double" in this case moves over to994 // "const double -> double".995 LLVM_DEBUG(llvm::dbgs()996 << "--- approximateStdConv. Conversion between numerics.\n");997 WorkType = QualType{ToBuiltin, FastQualifiersToApply};998 }999 1000 const auto *FromEnum = WorkType->getAsCanonical<EnumType>();1001 const auto *ToEnum = To->getAs<EnumType>();1002 if (FromEnum && ToNumeric && FromEnum->isUnscopedEnumerationType()) {1003 // Unscoped enumerations (or enumerations in C) convert to numerics.1004 LLVM_DEBUG(llvm::dbgs()1005 << "--- approximateStdConv. Unscoped enum to numeric.\n");1006 WorkType = QualType{ToBuiltin, FastQualifiersToApply};1007 } else if (FromNumeric && ToEnum && ToEnum->isUnscopedEnumerationType()) {1008 // Numeric types convert to enumerations only in C.1009 if (Ctx.getLangOpts().CPlusPlus) {1010 LLVM_DEBUG(llvm::dbgs() << "<<< approximateStdConv. Numeric to unscoped "1011 "enum, not possible in C++!\n");1012 return {};1013 }1014 1015 LLVM_DEBUG(llvm::dbgs()1016 << "--- approximateStdConv. Numeric to unscoped enum.\n");1017 WorkType = QualType{ToEnum, FastQualifiersToApply};1018 }1019 1020 // Check for pointer conversions.1021 const auto *FromPtr = WorkType->getAs<PointerType>();1022 const auto *ToPtr = To->getAs<PointerType>();1023 if (FromPtr && ToPtr) {1024 if (ToPtr->isVoidPointerType()) {1025 LLVM_DEBUG(llvm::dbgs() << "--- approximateStdConv. To void pointer.\n");1026 WorkType = QualType{ToPtr, FastQualifiersToApply};1027 }1028 1029 const auto *FromRecordPtr = FromPtr->getPointeeCXXRecordDecl();1030 const auto *ToRecordPtr = ToPtr->getPointeeCXXRecordDecl();1031 if (isDerivedToBase(FromRecordPtr, ToRecordPtr)) {1032 LLVM_DEBUG(llvm::dbgs() << "--- approximateStdConv. Derived* to Base*\n");1033 WorkType = QualType{ToPtr, FastQualifiersToApply};1034 }1035 }1036 1037 // Model the slicing Derived-to-Base too, as "BaseT temporary = derived;"1038 // can also be compiled.1039 const auto *FromRecord = WorkType->getAsCXXRecordDecl();1040 const auto *ToRecord = To->getAsCXXRecordDecl();1041 if (isDerivedToBase(FromRecord, ToRecord)) {1042 LLVM_DEBUG(llvm::dbgs() << "--- approximateStdConv. Derived To Base.\n");1043 WorkType = QualType{1044 ToRecord->getASTContext().getCanonicalTagType(ToRecord)->getTypePtr(),1045 FastQualifiersToApply};1046 }1047 1048 if (Ctx.getLangOpts().CPlusPlus17 && FromPtr && ToPtr) {1049 // Function pointer conversion: A noexcept function pointer can be passed1050 // to a non-noexcept one.1051 const auto *FromFunctionPtr =1052 FromPtr->getPointeeType()->getAs<FunctionProtoType>();1053 const auto *ToFunctionPtr =1054 ToPtr->getPointeeType()->getAs<FunctionProtoType>();1055 if (FromFunctionPtr && ToFunctionPtr &&1056 FromFunctionPtr->hasNoexceptExceptionSpec() &&1057 !ToFunctionPtr->hasNoexceptExceptionSpec()) {1058 LLVM_DEBUG(llvm::dbgs() << "--- approximateStdConv. noexcept function "1059 "pointer to non-noexcept.\n");1060 WorkType = QualType{ToPtr, FastQualifiersToApply};1061 }1062 }1063 1064 // Qualifier adjustments are modelled according to the user's request in1065 // the QualifiersMix check config.1066 LLVM_DEBUG(llvm::dbgs()1067 << "--- approximateStdConv. Trying qualifier adjustment...\n");1068 MixData QualConv = calculateMixability(Check, WorkType, To, Ctx,1069 ImplicitConversionModellingMode::None);1070 QualConv.sanitize();1071 if (hasFlag(QualConv.Flags, MixFlags::Qualifiers)) {1072 LLVM_DEBUG(llvm::dbgs()1073 << "<<< approximateStdConv. Qualifiers adjusted.\n");1074 WorkType = To;1075 }1076 1077 if (ASTContext::hasSameType(WorkType, To)) {1078 LLVM_DEBUG(llvm::dbgs() << "<<< approximateStdConv. Reached 'To' type.\n");1079 return {Ctx.getCommonSugaredType(WorkType, To)};1080 }1081 1082 LLVM_DEBUG(llvm::dbgs() << "<<< approximateStdConv. Did not reach 'To'.\n");1083 return {};1084}1085 1086namespace {1087 1088/// Helper class for storing possible user-defined conversion calls that1089/// *could* take place in an implicit conversion, and selecting the one that1090/// most likely *does*, if any.1091class UserDefinedConversionSelector {1092public:1093 /// The conversion associated with a conversion function, together with the1094 /// mixability flags of the conversion function's parameter or return type1095 /// to the rest of the sequence the selector is used in, and the sequence1096 /// that applied through the conversion itself.1097 struct PreparedConversion {1098 const CXXMethodDecl *ConversionFun;1099 MixFlags Flags;1100 ConversionSequence Seq;1101 1102 PreparedConversion(const CXXMethodDecl *CMD, MixFlags F,1103 ConversionSequence S)1104 : ConversionFun(CMD), Flags(F), Seq(S) {}1105 };1106 1107 UserDefinedConversionSelector(const TheCheck &Check) : Check(Check) {}1108 1109 /// Adds the conversion between the two types for the given function into1110 /// the possible implicit conversion set. FromType and ToType is either:1111 /// * the result of a standard sequence and a converting ctor parameter1112 /// * the return type of a conversion operator and the expected target of1113 /// an implicit conversion.1114 void addConversion(const CXXMethodDecl *ConvFun, QualType FromType,1115 QualType ToType) {1116 // Try to go from the FromType to the ToType with only a single implicit1117 // conversion, to see if the conversion function is applicable.1118 MixData Mix = calculateMixability(1119 Check, FromType, ToType, ConvFun->getASTContext(),1120 ImplicitConversionModellingMode::OneWaySingleStandardOnly);1121 Mix.sanitize();1122 if (!Mix.indicatesMixability())1123 return;1124 1125 LLVM_DEBUG(llvm::dbgs() << "--- tryConversion. Found viable with flags: "1126 << formatMixFlags(Mix.Flags) << '\n');1127 FlaggedConversions.emplace_back(ConvFun, Mix.Flags, Mix.Conversion);1128 }1129 1130 /// Selects the best conversion function that is applicable from the1131 /// prepared set of potential conversion functions taken.1132 std::optional<PreparedConversion> operator()() const {1133 if (FlaggedConversions.empty()) {1134 LLVM_DEBUG(llvm::dbgs() << "--- selectUserDefinedConv. Empty.\n");1135 return {};1136 }1137 if (FlaggedConversions.size() == 1) {1138 LLVM_DEBUG(llvm::dbgs() << "--- selectUserDefinedConv. Single.\n");1139 return FlaggedConversions.front();1140 }1141 1142 std::optional<PreparedConversion> BestConversion;1143 unsigned short HowManyGoodConversions = 0;1144 for (const auto &Prepared : FlaggedConversions) {1145 LLVM_DEBUG(llvm::dbgs() << "--- selectUserDefinedConv. Candidate flags: "1146 << formatMixFlags(Prepared.Flags) << '\n');1147 if (!BestConversion) {1148 BestConversion = Prepared;1149 ++HowManyGoodConversions;1150 continue;1151 }1152 1153 const bool BestConversionHasImplicit =1154 hasFlag(BestConversion->Flags, MixFlags::ImplicitConversion);1155 const bool ThisConversionHasImplicit =1156 hasFlag(Prepared.Flags, MixFlags::ImplicitConversion);1157 if (!BestConversionHasImplicit && ThisConversionHasImplicit)1158 // This is a worse conversion, because a better one was found earlier.1159 continue;1160 1161 if (BestConversionHasImplicit && !ThisConversionHasImplicit) {1162 // If the so far best selected conversion needs a previous implicit1163 // conversion to match the user-defined converting function, but this1164 // conversion does not, this is a better conversion, and we can throw1165 // away the previously selected conversion(s).1166 BestConversion = Prepared;1167 HowManyGoodConversions = 1;1168 continue;1169 }1170 1171 if (BestConversionHasImplicit == ThisConversionHasImplicit)1172 // The current conversion is the same in term of goodness than the1173 // already selected one.1174 ++HowManyGoodConversions;1175 }1176 1177 if (HowManyGoodConversions == 1) {1178 LLVM_DEBUG(llvm::dbgs()1179 << "--- selectUserDefinedConv. Unique result. Flags: "1180 << formatMixFlags(BestConversion->Flags) << '\n');1181 return BestConversion;1182 }1183 1184 LLVM_DEBUG(llvm::dbgs()1185 << "--- selectUserDefinedConv. No, or ambiguous.\n");1186 return {};1187 }1188 1189private:1190 llvm::SmallVector<PreparedConversion, 2> FlaggedConversions;1191 const TheCheck &Check;1192};1193 1194} // namespace1195 1196static std::optional<ConversionSequence>1197tryConversionOperators(const TheCheck &Check, const CXXRecordDecl *RD,1198 QualType ToType) {1199 if (!RD || !RD->isCompleteDefinition())1200 return {};1201 RD = RD->getDefinition();1202 1203 LLVM_DEBUG(llvm::dbgs() << ">>> tryConversionOperators: " << RD->getName()1204 << " to:\n";1205 ToType.dump(llvm::dbgs(), RD->getASTContext());1206 llvm::dbgs() << '\n';);1207 1208 UserDefinedConversionSelector ConversionSet{Check};1209 1210 for (const NamedDecl *Method : RD->getVisibleConversionFunctions()) {1211 const auto *Con = dyn_cast<CXXConversionDecl>(Method);1212 if (!Con || Con->isExplicit())1213 continue;1214 LLVM_DEBUG(llvm::dbgs() << "--- tryConversionOperators. Trying:\n";1215 Con->dump(llvm::dbgs()); llvm::dbgs() << '\n';);1216 1217 // Try to go from the result of conversion operator to the expected type,1218 // without calculating another user-defined conversion.1219 ConversionSet.addConversion(Con, Con->getConversionType(), ToType);1220 }1221 1222 if (std::optional<UserDefinedConversionSelector::PreparedConversion>1223 SelectedConversion = ConversionSet()) {1224 const CanQualType RecordType = RD->getASTContext().getCanonicalTagType(RD);1225 1226 ConversionSequence Result{RecordType, ToType};1227 // The conversion from the operator call's return type to ToType was1228 // modelled as a "pre-conversion" in the operator call, but it is the1229 // "post-conversion" from the point of view of the original conversion1230 // we are modelling.1231 Result.AfterSecondStandard = SelectedConversion->Seq.AfterFirstStandard;1232 1233 ConversionSequence::UserDefinedConversionOperator ConvOp;1234 ConvOp.Fun = cast<CXXConversionDecl>(SelectedConversion->ConversionFun);1235 ConvOp.UserDefinedType = RecordType;1236 ConvOp.ConversionOperatorResultType = ConvOp.Fun->getConversionType();1237 Result.setConversion(ConvOp);1238 1239 LLVM_DEBUG(llvm::dbgs() << "<<< tryConversionOperators. Found result.\n");1240 return Result;1241 }1242 1243 LLVM_DEBUG(llvm::dbgs() << "<<< tryConversionOperators. No conversion.\n");1244 return {};1245}1246 1247static std::optional<ConversionSequence>1248tryConvertingConstructors(const TheCheck &Check, QualType FromType,1249 const CXXRecordDecl *RD) {1250 if (!RD || !RD->isCompleteDefinition())1251 return {};1252 RD = RD->getDefinition();1253 1254 LLVM_DEBUG(llvm::dbgs() << ">>> tryConveringConstructors: " << RD->getName()1255 << " from:\n";1256 FromType.dump(llvm::dbgs(), RD->getASTContext());1257 llvm::dbgs() << '\n';);1258 1259 UserDefinedConversionSelector ConversionSet{Check};1260 1261 for (const CXXConstructorDecl *Con : RD->ctors()) {1262 if (Con->isCopyOrMoveConstructor() ||1263 !Con->isConvertingConstructor(/* AllowExplicit =*/false))1264 continue;1265 LLVM_DEBUG(llvm::dbgs() << "--- tryConvertingConstructors. Trying:\n";1266 Con->dump(llvm::dbgs()); llvm::dbgs() << '\n';);1267 1268 // Try to go from the original FromType to the converting constructor's1269 // parameter type without another user-defined conversion.1270 ConversionSet.addConversion(Con, FromType, Con->getParamDecl(0)->getType());1271 }1272 1273 if (std::optional<UserDefinedConversionSelector::PreparedConversion>1274 SelectedConversion = ConversionSet()) {1275 const CanQualType RecordType = RD->getASTContext().getCanonicalTagType(RD);1276 1277 ConversionSequence Result{FromType, RecordType};1278 Result.AfterFirstStandard = SelectedConversion->Seq.AfterFirstStandard;1279 1280 ConversionSequence::UserDefinedConvertingConstructor Ctor;1281 Ctor.Fun = cast<CXXConstructorDecl>(SelectedConversion->ConversionFun);1282 Ctor.ConstructorParameterType = Ctor.Fun->getParamDecl(0)->getType();1283 Ctor.UserDefinedType = RecordType;1284 Result.setConversion(Ctor);1285 1286 LLVM_DEBUG(llvm::dbgs()1287 << "<<< tryConvertingConstructors. Found result.\n");1288 return Result;1289 }1290 1291 LLVM_DEBUG(llvm::dbgs() << "<<< tryConvertingConstructors. No conversion.\n");1292 return {};1293}1294 1295/// Returns whether an expression of LType can be used in an RType context, as1296/// per the implicit conversion rules.1297///1298/// Note: the result of this operation, unlike that of calculateMixability, is1299/// **NOT** symmetric.1300static MixData1301approximateImplicitConversion(const TheCheck &Check, QualType LType,1302 QualType RType, const ASTContext &Ctx,1303 ImplicitConversionModellingMode ImplicitMode) {1304 LLVM_DEBUG(llvm::dbgs() << ">>> approximateImplicitConversion for LType:\n";1305 LType.dump(llvm::dbgs(), Ctx); llvm::dbgs() << "\nand RType:\n";1306 RType.dump(llvm::dbgs(), Ctx);1307 llvm::dbgs() << "\nimplicit mode: "; switch (ImplicitMode) {1308 case ImplicitConversionModellingMode::None:1309 llvm::dbgs() << "None";1310 break;1311 case ImplicitConversionModellingMode::All:1312 llvm::dbgs() << "All";1313 break;1314 case ImplicitConversionModellingMode::OneWaySingleStandardOnly:1315 llvm::dbgs() << "OneWay, Single, STD Only";1316 break;1317 } llvm::dbgs() << '\n';);1318 if (LType == RType)1319 return {MixFlags::Trivial, LType};1320 1321 // An implicit conversion sequence consists of the following, in order:1322 // * Maybe standard conversion sequence.1323 // * Maybe user-defined conversion.1324 // * Maybe standard conversion sequence.1325 ConversionSequence ImplicitSeq{LType, RType};1326 QualType WorkType = LType;1327 1328 std::optional<QualType> AfterFirstStdConv =1329 approximateStandardConversionSequence(Check, LType, RType, Ctx);1330 if (AfterFirstStdConv) {1331 LLVM_DEBUG(llvm::dbgs() << "--- approximateImplicitConversion. Standard "1332 "Pre-Conversion found!\n");1333 ImplicitSeq.AfterFirstStandard = *AfterFirstStdConv;1334 WorkType = ImplicitSeq.AfterFirstStandard;1335 }1336 1337 if (ImplicitMode == ImplicitConversionModellingMode::OneWaySingleStandardOnly)1338 // If the caller only requested modelling of a standard conversion, bail.1339 return {ImplicitSeq.AfterFirstStandard.isNull()1340 ? MixFlags::None1341 : MixFlags::ImplicitConversion,1342 ImplicitSeq};1343 1344 if (Ctx.getLangOpts().CPlusPlus) {1345 bool FoundConversionOperator = false, FoundConvertingCtor = false;1346 1347 if (const auto *LRD = WorkType->getAsCXXRecordDecl()) {1348 std::optional<ConversionSequence> ConversionOperatorResult =1349 tryConversionOperators(Check, LRD, RType);1350 if (ConversionOperatorResult) {1351 LLVM_DEBUG(llvm::dbgs() << "--- approximateImplicitConversion. Found "1352 "conversion operator.\n");1353 ImplicitSeq.update(*ConversionOperatorResult);1354 WorkType = ImplicitSeq.getTypeAfterUserDefinedConversion();1355 FoundConversionOperator = true;1356 }1357 }1358 1359 if (const auto *RRD = RType->getAsCXXRecordDecl()) {1360 // Use the original "LType" here, and not WorkType, because the1361 // conversion to the converting constructors' parameters will be1362 // modelled in the recursive call.1363 std::optional<ConversionSequence> ConvCtorResult =1364 tryConvertingConstructors(Check, LType, RRD);1365 if (ConvCtorResult) {1366 LLVM_DEBUG(llvm::dbgs() << "--- approximateImplicitConversion. Found "1367 "converting constructor.\n");1368 ImplicitSeq.update(*ConvCtorResult);1369 WorkType = ImplicitSeq.getTypeAfterUserDefinedConversion();1370 FoundConvertingCtor = true;1371 }1372 }1373 1374 if (FoundConversionOperator && FoundConvertingCtor) {1375 // If both an operator and a ctor matches, the sequence is ambiguous.1376 LLVM_DEBUG(llvm::dbgs()1377 << "<<< approximateImplicitConversion. Found both "1378 "user-defined conversion kinds in the same sequence!\n");1379 return {MixFlags::None};1380 }1381 }1382 1383 // After the potential user-defined conversion, another standard conversion1384 // sequence might exist.1385 LLVM_DEBUG(1386 llvm::dbgs()1387 << "--- approximateImplicitConversion. Try to find post-conversion.\n");1388 const MixData SecondStdConv = approximateImplicitConversion(1389 Check, WorkType, RType, Ctx,1390 ImplicitConversionModellingMode::OneWaySingleStandardOnly);1391 if (SecondStdConv.indicatesMixability()) {1392 LLVM_DEBUG(llvm::dbgs() << "--- approximateImplicitConversion. Standard "1393 "Post-Conversion found!\n");1394 1395 // The single-step modelling puts the modelled conversion into the "PreStd"1396 // variable in the recursive call, but from the PoV of this function, it is1397 // the post-conversion.1398 ImplicitSeq.AfterSecondStandard =1399 SecondStdConv.Conversion.AfterFirstStandard;1400 WorkType = ImplicitSeq.AfterSecondStandard;1401 }1402 1403 if (ImplicitSeq) {1404 LLVM_DEBUG(llvm::dbgs()1405 << "<<< approximateImplicitConversion. Found a conversion.\n");1406 return {MixFlags::ImplicitConversion, ImplicitSeq};1407 }1408 1409 LLVM_DEBUG(1410 llvm::dbgs() << "<<< approximateImplicitConversion. No match found.\n");1411 return {MixFlags::None};1412}1413 1414static MixableParameterRange modelMixingRange(1415 const TheCheck &Check, const FunctionDecl *FD, std::size_t StartIndex,1416 const filter::SimilarlyUsedParameterPairSuppressor &UsageBasedSuppressor) {1417 const std::size_t NumParams = FD->getNumParams();1418 assert(StartIndex < NumParams && "out of bounds for start");1419 const ASTContext &Ctx = FD->getASTContext();1420 1421 MixableParameterRange Ret;1422 // A parameter at index 'StartIndex' had been trivially "checked".1423 Ret.NumParamsChecked = 1;1424 1425 for (std::size_t I = StartIndex + 1; I < NumParams; ++I) {1426 const ParmVarDecl *Ith = FD->getParamDecl(I);1427 const StringRef ParamName = Ith->getName();1428 LLVM_DEBUG(llvm::dbgs()1429 << "Check param #" << I << " '" << ParamName << "'...\n");1430 if (filter::isIgnoredParameter(Check, Ith)) {1431 LLVM_DEBUG(llvm::dbgs() << "Param #" << I << " is ignored. Break!\n");1432 break;1433 }1434 1435 const StringRef PrevParamName = FD->getParamDecl(I - 1)->getName();1436 if (!ParamName.empty() && !PrevParamName.empty() &&1437 filter::prefixSuffixCoverUnderThreshold(1438 Check.NamePrefixSuffixSilenceDissimilarityThreshold, PrevParamName,1439 ParamName)) {1440 LLVM_DEBUG(llvm::dbgs() << "Parameter '" << ParamName1441 << "' follows a pattern with previous parameter '"1442 << PrevParamName << "'. Break!\n");1443 break;1444 }1445 1446 // Now try to go forward and build the range of [Start, ..., I, I + 1, ...]1447 // parameters that can be messed up at a call site.1448 MixableParameterRange::MixVector MixesOfIth;1449 for (std::size_t J = StartIndex; J < I; ++J) {1450 const ParmVarDecl *Jth = FD->getParamDecl(J);1451 LLVM_DEBUG(llvm::dbgs()1452 << "Check mix of #" << J << " against #" << I << "...\n");1453 1454 if (isSimilarlyUsedParameter(UsageBasedSuppressor, Ith, Jth)) {1455 // Consider the two similarly used parameters to not be possible in a1456 // mix-up at the user's request, if they enabled this heuristic.1457 LLVM_DEBUG(llvm::dbgs() << "Parameters #" << I << " and #" << J1458 << " deemed related, ignoring...\n");1459 1460 // If the parameter #I and #J mixes, then I is mixable with something1461 // in the current range, so the range has to be broken and I not1462 // included.1463 MixesOfIth.clear();1464 break;1465 }1466 1467 Mix M{Jth, Ith,1468 calculateMixability(Check, Jth->getType(), Ith->getType(), Ctx,1469 Check.ModelImplicitConversions1470 ? ImplicitConversionModellingMode::All1471 : ImplicitConversionModellingMode::None)};1472 LLVM_DEBUG(llvm::dbgs() << "Mix flags (raw) : "1473 << formatMixFlags(M.flags()) << '\n');1474 M.sanitize();1475 LLVM_DEBUG(llvm::dbgs() << "Mix flags (after sanitize): "1476 << formatMixFlags(M.flags()) << '\n');1477 1478 assert(M.flagsValid() && "All flags decayed!");1479 1480 if (M.mixable())1481 MixesOfIth.emplace_back(std::move(M));1482 }1483 1484 if (MixesOfIth.empty()) {1485 // If there weren't any new mixes stored for Ith, the range is1486 // [Start, ..., I].1487 LLVM_DEBUG(llvm::dbgs()1488 << "Param #" << I1489 << " does not mix with any in the current range. Break!\n");1490 break;1491 }1492 1493 Ret.Mixes.insert(Ret.Mixes.end(), MixesOfIth.begin(), MixesOfIth.end());1494 ++Ret.NumParamsChecked; // Otherwise a new param was iterated.1495 }1496 1497 return Ret;1498}1499 1500} // namespace model1501 1502namespace {1503/// Matches DeclRefExprs and their ignorable wrappers to ParmVarDecls.1504AST_MATCHER_FUNCTION(ast_matchers::internal::Matcher<Stmt>, paramRefExpr) {1505 return expr(ignoringParenImpCasts(ignoringElidableConstructorCall(1506 declRefExpr(to(parmVarDecl().bind("param"))))));1507}1508} // namespace1509 1510namespace filter {1511 1512/// Returns whether the parameter's name or the parameter's type's name is1513/// configured by the user to be ignored from analysis and diagnostic.1514static bool isIgnoredParameter(const TheCheck &Check, const ParmVarDecl *Node) {1515 LLVM_DEBUG(llvm::dbgs() << "Checking if '" << Node->getName()1516 << "' is ignored.\n");1517 1518 if (!Node->getIdentifier())1519 return llvm::is_contained(Check.IgnoredParameterNames, "\"\"");1520 1521 const StringRef NodeName = Node->getName();1522 if (llvm::is_contained(Check.IgnoredParameterNames, NodeName)) {1523 LLVM_DEBUG(llvm::dbgs() << "\tName ignored.\n");1524 return true;1525 }1526 1527 const StringRef NodeTypeName = [Node] {1528 const ASTContext &Ctx = Node->getASTContext();1529 const SourceManager &SM = Ctx.getSourceManager();1530 SourceLocation B = Node->getTypeSpecStartLoc();1531 SourceLocation E = Node->getTypeSpecEndLoc();1532 const LangOptions LO;1533 1534 LLVM_DEBUG(llvm::dbgs() << "\tType name code is '"1535 << Lexer::getSourceText(1536 CharSourceRange::getTokenRange(B, E), SM, LO)1537 << "'...\n");1538 if (B.isMacroID()) {1539 LLVM_DEBUG(llvm::dbgs() << "\t\tBeginning is macro.\n");1540 B = SM.getTopMacroCallerLoc(B);1541 }1542 if (E.isMacroID()) {1543 LLVM_DEBUG(llvm::dbgs() << "\t\tEnding is macro.\n");1544 E = Lexer::getLocForEndOfToken(SM.getTopMacroCallerLoc(E), 0, SM, LO);1545 }1546 LLVM_DEBUG(llvm::dbgs() << "\tType name code is '"1547 << Lexer::getSourceText(1548 CharSourceRange::getTokenRange(B, E), SM, LO)1549 << "'...\n");1550 1551 return Lexer::getSourceText(CharSourceRange::getTokenRange(B, E), SM, LO);1552 }();1553 1554 LLVM_DEBUG(llvm::dbgs() << "\tType name is '" << NodeTypeName << "'\n");1555 if (!NodeTypeName.empty()) {1556 if (llvm::any_of(Check.IgnoredParameterTypeSuffixes,1557 [NodeTypeName](StringRef E) {1558 return !E.empty() && NodeTypeName.ends_with(E);1559 })) {1560 LLVM_DEBUG(llvm::dbgs() << "\tType suffix ignored.\n");1561 return true;1562 }1563 }1564 1565 return false;1566}1567 1568/// This namespace contains the implementations for the suppression of1569/// diagnostics from similarly-used ("related") parameters.1570namespace relatedness_heuristic {1571 1572static constexpr std::size_t SmallDataStructureSize = 4;1573 1574template <typename T, std::size_t N = SmallDataStructureSize>1575using ParamToSmallSetMap =1576 llvm::DenseMap<const ParmVarDecl *, llvm::SmallSet<T, N>>;1577 1578template <typename T, std::size_t N = SmallDataStructureSize>1579using ParamToSmallPtrSetMap =1580 llvm::DenseMap<const ParmVarDecl *, llvm::SmallPtrSet<T, N>>;1581 1582/// Returns whether the sets mapped to the two elements in the map have at1583/// least one element in common.1584template <typename MapTy, typename ElemTy>1585static bool lazyMapOfSetsIntersectionExists(const MapTy &Map, const ElemTy &E1,1586 const ElemTy &E2) {1587 auto E1Iterator = Map.find(E1);1588 auto E2Iterator = Map.find(E2);1589 if (E1Iterator == Map.end() || E2Iterator == Map.end())1590 return false;1591 1592 for (const auto &E1SetElem : E1Iterator->second)1593 if (E2Iterator->second.contains(E1SetElem))1594 return true;1595 1596 return false;1597}1598 1599/// Implements the heuristic that marks two parameters related if there is1600/// a usage for both in the same strict expression subtree. A strict1601/// expression subtree is a tree which only includes Expr nodes, i.e. no1602/// Stmts and no Decls.1603class AppearsInSameExpr : public RecursiveASTVisitor<AppearsInSameExpr> {1604 using Base = RecursiveASTVisitor<AppearsInSameExpr>;1605 1606 const FunctionDecl *FD;1607 const Expr *CurrentExprOnlyTreeRoot = nullptr;1608 llvm::DenseMap<const ParmVarDecl *,1609 llvm::SmallPtrSet<const Expr *, SmallDataStructureSize>>1610 ParentExprsForParamRefs;1611 1612public:1613 void setup(const FunctionDecl *FD) {1614 this->FD = FD;1615 TraverseFunctionDecl(const_cast<FunctionDecl *>(FD));1616 }1617 1618 bool operator()(const ParmVarDecl *Param1, const ParmVarDecl *Param2) const {1619 return lazyMapOfSetsIntersectionExists(ParentExprsForParamRefs, Param1,1620 Param2);1621 }1622 1623 bool TraverseDecl(Decl *D) {1624 CurrentExprOnlyTreeRoot = nullptr;1625 return Base::TraverseDecl(D);1626 }1627 1628 bool TraverseStmt(Stmt *S, DataRecursionQueue *Queue = nullptr) {1629 if (auto *E = dyn_cast_or_null<Expr>(S)) {1630 bool RootSetInCurrentStackFrame = false;1631 if (!CurrentExprOnlyTreeRoot) {1632 CurrentExprOnlyTreeRoot = E;1633 RootSetInCurrentStackFrame = true;1634 }1635 1636 const bool Ret = Base::TraverseStmt(S);1637 1638 if (RootSetInCurrentStackFrame)1639 CurrentExprOnlyTreeRoot = nullptr;1640 1641 return Ret;1642 }1643 1644 // A Stmt breaks the strictly Expr subtree.1645 CurrentExprOnlyTreeRoot = nullptr;1646 return Base::TraverseStmt(S);1647 }1648 1649 bool VisitDeclRefExpr(DeclRefExpr *DRE) {1650 if (!CurrentExprOnlyTreeRoot)1651 return true;1652 1653 if (auto *PVD = dyn_cast<ParmVarDecl>(DRE->getDecl()))1654 if (llvm::find(FD->parameters(), PVD))1655 ParentExprsForParamRefs[PVD].insert(CurrentExprOnlyTreeRoot);1656 1657 return true;1658 }1659};1660 1661/// Implements the heuristic that marks two parameters related if there are1662/// two separate calls to the same function (overload) and the parameters are1663/// passed to the same index in both calls, i.e f(a, b) and f(a, c) passes1664/// b and c to the same index (2) of f(), marking them related.1665class PassedToSameFunction {1666 ParamToSmallSetMap<std::pair<const FunctionDecl *, unsigned>> TargetParams;1667 1668public:1669 void setup(const FunctionDecl *FD) {1670 auto ParamsAsArgsInFnCalls =1671 match(functionDecl(forEachDescendant(1672 callExpr(forEachArgumentWithParam(1673 paramRefExpr(), parmVarDecl().bind("passed-to")))1674 .bind("call-expr"))),1675 *FD, FD->getASTContext());1676 for (const auto &Match : ParamsAsArgsInFnCalls) {1677 const auto *PassedParamOfThisFn = Match.getNodeAs<ParmVarDecl>("param");1678 const auto *CE = Match.getNodeAs<CallExpr>("call-expr");1679 const auto *PassedToParam = Match.getNodeAs<ParmVarDecl>("passed-to");1680 assert(PassedParamOfThisFn && CE && PassedToParam);1681 1682 const FunctionDecl *CalledFn = CE->getDirectCallee();1683 if (!CalledFn)1684 continue;1685 1686 std::optional<unsigned> TargetIdx;1687 const unsigned NumFnParams = CalledFn->getNumParams();1688 for (unsigned Idx = 0; Idx < NumFnParams; ++Idx)1689 if (CalledFn->getParamDecl(Idx) == PassedToParam)1690 TargetIdx.emplace(Idx);1691 1692 assert(TargetIdx && "Matched, but didn't find index?");1693 TargetParams[PassedParamOfThisFn].insert(1694 {CalledFn->getCanonicalDecl(), *TargetIdx});1695 }1696 }1697 1698 bool operator()(const ParmVarDecl *Param1, const ParmVarDecl *Param2) const {1699 return lazyMapOfSetsIntersectionExists(TargetParams, Param1, Param2);1700 }1701};1702 1703/// Implements the heuristic that marks two parameters related if the same1704/// member is accessed (referred to) inside the current function's body.1705class AccessedSameMemberOf {1706 ParamToSmallPtrSetMap<const Decl *> AccessedMembers;1707 1708public:1709 void setup(const FunctionDecl *FD) {1710 auto MembersCalledOnParams = match(1711 functionDecl(forEachDescendant(1712 memberExpr(hasObjectExpression(paramRefExpr())).bind("mem-expr"))),1713 *FD, FD->getASTContext());1714 1715 for (const auto &Match : MembersCalledOnParams) {1716 const auto *AccessedParam = Match.getNodeAs<ParmVarDecl>("param");1717 const auto *ME = Match.getNodeAs<MemberExpr>("mem-expr");1718 assert(AccessedParam && ME);1719 AccessedMembers[AccessedParam].insert(1720 ME->getMemberDecl()->getCanonicalDecl());1721 }1722 }1723 1724 bool operator()(const ParmVarDecl *Param1, const ParmVarDecl *Param2) const {1725 return lazyMapOfSetsIntersectionExists(AccessedMembers, Param1, Param2);1726 }1727};1728 1729/// Implements the heuristic that marks two parameters related if different1730/// ReturnStmts return them from the function.1731class Returned {1732 llvm::SmallVector<const ParmVarDecl *, SmallDataStructureSize> ReturnedParams;1733 1734public:1735 void setup(const FunctionDecl *FD) {1736 // TODO: Handle co_return.1737 auto ParamReturns = match(functionDecl(forEachDescendant(1738 returnStmt(hasReturnValue(paramRefExpr())))),1739 *FD, FD->getASTContext());1740 for (const auto &Match : ParamReturns) {1741 const auto *ReturnedParam = Match.getNodeAs<ParmVarDecl>("param");1742 assert(ReturnedParam);1743 1744 if (find(FD->parameters(), ReturnedParam) == FD->param_end())1745 // Inside the subtree of a FunctionDecl there might be ReturnStmts of1746 // a parameter that isn't the parameter of the function, e.g. in the1747 // case of lambdas.1748 continue;1749 1750 ReturnedParams.emplace_back(ReturnedParam);1751 }1752 }1753 1754 bool operator()(const ParmVarDecl *Param1, const ParmVarDecl *Param2) const {1755 return llvm::is_contained(ReturnedParams, Param1) &&1756 llvm::is_contained(ReturnedParams, Param2);1757 }1758};1759 1760} // namespace relatedness_heuristic1761 1762/// Helper class that is used to detect if two parameters of the same function1763/// are used in a similar fashion, to suppress the result.1764class SimilarlyUsedParameterPairSuppressor {1765 const bool Enabled;1766 relatedness_heuristic::AppearsInSameExpr SameExpr;1767 relatedness_heuristic::PassedToSameFunction PassToFun;1768 relatedness_heuristic::AccessedSameMemberOf SameMember;1769 relatedness_heuristic::Returned Returns;1770 1771public:1772 SimilarlyUsedParameterPairSuppressor(const FunctionDecl *FD, bool Enable)1773 : Enabled(Enable) {1774 if (!Enable)1775 return;1776 1777 SameExpr.setup(FD);1778 PassToFun.setup(FD);1779 SameMember.setup(FD);1780 Returns.setup(FD);1781 }1782 1783 /// Returns whether the specified two parameters are deemed similarly used1784 /// or related by the heuristics.1785 bool operator()(const ParmVarDecl *Param1, const ParmVarDecl *Param2) const {1786 if (!Enabled)1787 return false;1788 1789 LLVM_DEBUG(llvm::dbgs()1790 << "::: Matching similar usage / relatedness heuristic...\n");1791 1792 if (SameExpr(Param1, Param2)) {1793 LLVM_DEBUG(llvm::dbgs() << "::: Used in the same expression.\n");1794 return true;1795 }1796 1797 if (PassToFun(Param1, Param2)) {1798 LLVM_DEBUG(llvm::dbgs()1799 << "::: Passed to same function in different calls.\n");1800 return true;1801 }1802 1803 if (SameMember(Param1, Param2)) {1804 LLVM_DEBUG(llvm::dbgs()1805 << "::: Same member field access or method called.\n");1806 return true;1807 }1808 1809 if (Returns(Param1, Param2)) {1810 LLVM_DEBUG(llvm::dbgs() << "::: Both parameter returned.\n");1811 return true;1812 }1813 1814 LLVM_DEBUG(llvm::dbgs() << "::: None.\n");1815 return false;1816 }1817};1818 1819// (This function hoists the call to operator() of the wrapper, so we do not1820// need to define the previous class at the top of the file.)1821static inline bool1822isSimilarlyUsedParameter(const SimilarlyUsedParameterPairSuppressor &Suppressor,1823 const ParmVarDecl *Param1, const ParmVarDecl *Param2) {1824 return Suppressor(Param1, Param2);1825}1826 1827static void padStringAtEnd(SmallVectorImpl<char> &Str, std::size_t ToLen) {1828 while (Str.size() < ToLen)1829 Str.emplace_back('\0');1830}1831 1832static void padStringAtBegin(SmallVectorImpl<char> &Str, std::size_t ToLen) {1833 while (Str.size() < ToLen)1834 Str.insert(Str.begin(), '\0');1835}1836 1837static bool isCommonPrefixWithoutSomeCharacters(std::size_t N, StringRef S1,1838 StringRef S2) {1839 assert(S1.size() >= N && S2.size() >= N);1840 const StringRef S1Prefix = S1.take_front(S1.size() - N),1841 S2Prefix = S2.take_front(S2.size() - N);1842 return S1Prefix == S2Prefix && !S1Prefix.empty();1843}1844 1845static bool isCommonSuffixWithoutSomeCharacters(std::size_t N, StringRef S1,1846 StringRef S2) {1847 assert(S1.size() >= N && S2.size() >= N);1848 const StringRef S1Suffix = S1.take_back(S1.size() - N),1849 S2Suffix = S2.take_back(S2.size() - N);1850 return S1Suffix == S2Suffix && !S1Suffix.empty();1851}1852 1853/// Returns whether the two strings are prefixes or suffixes of each other with1854/// at most Threshold characters differing on the non-common end.1855static bool prefixSuffixCoverUnderThreshold(std::size_t Threshold,1856 StringRef Str1, StringRef Str2) {1857 if (Threshold == 0)1858 return false;1859 1860 // Pad the two strings to the longer length.1861 const std::size_t BiggerLength = std::max(Str1.size(), Str2.size());1862 1863 if (BiggerLength <= Threshold)1864 // If the length of the strings is still smaller than the threshold, they1865 // would be covered by an empty prefix/suffix with the rest differing.1866 // (E.g. "A" and "X" with Threshold = 1 would mean we think they are1867 // similar and do not warn about them, which is a too eager assumption.)1868 return false;1869 1870 SmallString<32> S1PadE{Str1}, S2PadE{Str2};1871 padStringAtEnd(S1PadE, BiggerLength);1872 padStringAtEnd(S2PadE, BiggerLength);1873 1874 if (isCommonPrefixWithoutSomeCharacters(1875 Threshold, StringRef{S1PadE.begin(), BiggerLength},1876 StringRef{S2PadE.begin(), BiggerLength}))1877 return true;1878 1879 SmallString<32> S1PadB{Str1}, S2PadB{Str2};1880 padStringAtBegin(S1PadB, BiggerLength);1881 padStringAtBegin(S2PadB, BiggerLength);1882 1883 if (isCommonSuffixWithoutSomeCharacters(1884 Threshold, StringRef{S1PadB.begin(), BiggerLength},1885 StringRef{S2PadB.begin(), BiggerLength}))1886 return true;1887 1888 return false;1889}1890 1891} // namespace filter1892 1893namespace {1894 1895/// Matches functions that have at least the specified amount of parameters.1896AST_MATCHER_P(FunctionDecl, parameterCountGE, unsigned, N) {1897 return Node.getNumParams() >= N;1898}1899 1900/// Matches *any* overloaded unary and binary operators.1901AST_MATCHER(FunctionDecl, isOverloadedUnaryOrBinaryOperator) {1902 switch (Node.getOverloadedOperator()) {1903 case OO_None:1904 case OO_New:1905 case OO_Delete:1906 case OO_Array_New:1907 case OO_Array_Delete:1908 case OO_Conditional:1909 case OO_Coawait:1910 return false;1911 1912 default:1913 return Node.getNumParams() <= 2;1914 }1915}1916 1917} // namespace1918 1919/// Returns the DefaultMinimumLength if the Value of requested minimum length1920/// is less than 2. Minimum lengths of 0 or 1 are not accepted.1921static inline unsigned clampMinimumLength(const unsigned Value) {1922 return Value < 2 ? DefaultMinimumLength : Value;1923}1924 1925// FIXME: Maybe unneeded, getNameForDiagnostic() is expected to change to return1926// a crafted location when the node itself is unnamed. (See D84658, D85033.)1927/// Returns the diagnostic-friendly name of the node, or empty string.1928static SmallString<64> getName(const NamedDecl *ND) {1929 SmallString<64> Name;1930 llvm::raw_svector_ostream OS{Name};1931 ND->getNameForDiagnostic(OS, ND->getASTContext().getPrintingPolicy(), false);1932 return Name;1933}1934 1935/// Returns the diagnostic-friendly name of the node, or a constant value.1936static SmallString<64> getNameOrUnnamed(const NamedDecl *ND) {1937 auto Name = getName(ND);1938 if (Name.empty())1939 Name = "<unnamed>";1940 return Name;1941}1942 1943/// Returns whether a particular Mix between two parameters should have the1944/// types involved diagnosed to the user. This is only a flag check.1945static inline bool needsToPrintTypeInDiagnostic(const model::Mix &M) {1946 using namespace model;1947 return static_cast<bool>(1948 M.flags() &1949 (MixFlags::TypeAlias | MixFlags::ReferenceBind | MixFlags::Qualifiers));1950}1951 1952/// Returns whether a particular Mix between the two parameters should have1953/// implicit conversions elaborated.1954static inline bool needsToElaborateImplicitConversion(const model::Mix &M) {1955 return hasFlag(M.flags(), model::MixFlags::ImplicitConversion);1956}1957 1958namespace {1959 1960/// This class formats a conversion sequence into a "Ty1 -> Ty2 -> Ty3" line1961/// that can be used in diagnostics.1962struct FormattedConversionSequence {1963 std::string DiagnosticText;1964 1965 /// The formatted sequence is trivial if it is "Ty1 -> Ty2", but Ty1 and1966 /// Ty2 are the types that are shown in the code. A trivial diagnostic1967 /// does not need to be printed.1968 bool Trivial = true;1969 1970 FormattedConversionSequence(const PrintingPolicy &PP,1971 StringRef StartTypeAsDiagnosed,1972 const model::ConversionSequence &Conv,1973 StringRef DestinationTypeAsDiagnosed) {1974 llvm::raw_string_ostream OS{DiagnosticText};1975 1976 // Print the type name as it is printed in other places in the diagnostic.1977 OS << '\'' << StartTypeAsDiagnosed << '\'';1978 std::string LastAddedType = StartTypeAsDiagnosed.str();1979 std::size_t NumElementsAdded = 1;1980 1981 // However, the parameter's defined type might not be what the implicit1982 // conversion started with, e.g. if a typedef is found to convert.1983 const std::string SeqBeginTypeStr = Conv.Begin.getAsString(PP);1984 std::string SeqEndTypeStr = Conv.End.getAsString(PP);1985 if (StartTypeAsDiagnosed != SeqBeginTypeStr) {1986 OS << " (as '" << SeqBeginTypeStr << "')";1987 LastAddedType = SeqBeginTypeStr;1988 Trivial = false;1989 }1990 1991 auto AddType = [&](StringRef ToAdd) {1992 if (LastAddedType != ToAdd && ToAdd != SeqEndTypeStr) {1993 OS << " -> '" << ToAdd << "'";1994 LastAddedType = ToAdd.str();1995 ++NumElementsAdded;1996 }1997 };1998 for (const QualType InvolvedType : Conv.getInvolvedTypesInSequence())1999 // Print every type that's unique in the sequence into the diagnosis.2000 AddType(InvolvedType.getAsString(PP));2001 2002 if (LastAddedType != DestinationTypeAsDiagnosed) {2003 OS << " -> '" << DestinationTypeAsDiagnosed << "'";2004 LastAddedType = DestinationTypeAsDiagnosed.str();2005 ++NumElementsAdded;2006 }2007 2008 // Same reasoning as with the Begin, e.g. if the converted-to type is a2009 // typedef, it will not be the same inside the conversion sequence (where2010 // the model already tore off typedefs) as in the code.2011 if (DestinationTypeAsDiagnosed != SeqEndTypeStr) {2012 OS << " (as '" << SeqEndTypeStr << "')";2013 LastAddedType = SeqEndTypeStr;2014 Trivial = false;2015 }2016 2017 if (Trivial && NumElementsAdded > 2)2018 // If the thing is still marked trivial but we have more than the2019 // from and to types added, it should not be trivial, and elaborated2020 // when printing the diagnostic.2021 Trivial = false;2022 }2023};2024 2025/// Retains the elements called with and returns whether the call is done with2026/// a new element.2027template <typename E, std::size_t N> class InsertOnce {2028 llvm::SmallSet<E, N> CalledWith;2029 2030public:2031 bool operator()(E El) { return CalledWith.insert(std::move(El)).second; }2032 2033 bool calledWith(const E &El) const { return CalledWith.contains(El); }2034};2035 2036struct SwappedEqualQualTypePair {2037 QualType LHSType, RHSType;2038 2039 bool operator==(const SwappedEqualQualTypePair &Other) const {2040 return (LHSType == Other.LHSType && RHSType == Other.RHSType) ||2041 (LHSType == Other.RHSType && RHSType == Other.LHSType);2042 }2043 2044 bool operator<(const SwappedEqualQualTypePair &Other) const {2045 return LHSType < Other.LHSType && RHSType < Other.RHSType;2046 }2047};2048 2049struct TypeAliasDiagnosticTuple {2050 QualType LHSType, RHSType, CommonType;2051 2052 bool operator==(const TypeAliasDiagnosticTuple &Other) const {2053 return CommonType == Other.CommonType &&2054 ((LHSType == Other.LHSType && RHSType == Other.RHSType) ||2055 (LHSType == Other.RHSType && RHSType == Other.LHSType));2056 }2057 2058 bool operator<(const TypeAliasDiagnosticTuple &Other) const {2059 return CommonType < Other.CommonType && LHSType < Other.LHSType &&2060 RHSType < Other.RHSType;2061 }2062};2063 2064/// Helper class to only emit a diagnostic related to MixFlags::TypeAlias once.2065class UniqueTypeAliasDiagnosticHelper2066 : public InsertOnce<TypeAliasDiagnosticTuple, 8> {2067 using Base = InsertOnce<TypeAliasDiagnosticTuple, 8>;2068 2069public:2070 /// Returns whether the diagnostic for LHSType and RHSType which are both2071 /// referring to CommonType being the same has not been emitted already.2072 bool operator()(QualType LHSType, QualType RHSType, QualType CommonType) {2073 if (CommonType.isNull() || CommonType == LHSType || CommonType == RHSType)2074 return Base::operator()({LHSType, RHSType, {}});2075 2076 const TypeAliasDiagnosticTuple ThreeTuple{LHSType, RHSType, CommonType};2077 if (!Base::operator()(ThreeTuple))2078 return false;2079 2080 const bool AlreadySaidLHSAndCommonIsSame =2081 calledWith({LHSType, CommonType, {}});2082 const bool AlreadySaidRHSAndCommonIsSame =2083 calledWith({RHSType, CommonType, {}});2084 if (AlreadySaidLHSAndCommonIsSame && AlreadySaidRHSAndCommonIsSame) {2085 // "SomeInt == int" && "SomeOtherInt == int" => "Common(SomeInt,2086 // SomeOtherInt) == int", no need to diagnose it. Save the 3-tuple only2087 // for shortcut if it ever appears again.2088 return false;2089 }2090 2091 return true;2092 }2093};2094 2095} // namespace2096 2097EasilySwappableParametersCheck::EasilySwappableParametersCheck(2098 StringRef Name, ClangTidyContext *Context)2099 : ClangTidyCheck(Name, Context),2100 MinimumLength(clampMinimumLength(2101 Options.get("MinimumLength", DefaultMinimumLength))),2102 IgnoredParameterNames(optutils::parseStringList(2103 Options.get("IgnoredParameterNames", DefaultIgnoredParameterNames))),2104 IgnoredParameterTypeSuffixes(optutils::parseStringList(2105 Options.get("IgnoredParameterTypeSuffixes",2106 DefaultIgnoredParameterTypeSuffixes))),2107 QualifiersMix(Options.get("QualifiersMix", DefaultQualifiersMix)),2108 ModelImplicitConversions(Options.get("ModelImplicitConversions",2109 DefaultModelImplicitConversions)),2110 SuppressParametersUsedTogether(2111 Options.get("SuppressParametersUsedTogether",2112 DefaultSuppressParametersUsedTogether)),2113 NamePrefixSuffixSilenceDissimilarityThreshold(2114 Options.get("NamePrefixSuffixSilenceDissimilarityThreshold",2115 DefaultNamePrefixSuffixSilenceDissimilarityTreshold)) {}2116 2117void EasilySwappableParametersCheck::storeOptions(2118 ClangTidyOptions::OptionMap &Opts) {2119 Options.store(Opts, "MinimumLength", MinimumLength);2120 Options.store(Opts, "IgnoredParameterNames",2121 optutils::serializeStringList(IgnoredParameterNames));2122 Options.store(Opts, "IgnoredParameterTypeSuffixes",2123 optutils::serializeStringList(IgnoredParameterTypeSuffixes));2124 Options.store(Opts, "QualifiersMix", QualifiersMix);2125 Options.store(Opts, "ModelImplicitConversions", ModelImplicitConversions);2126 Options.store(Opts, "SuppressParametersUsedTogether",2127 SuppressParametersUsedTogether);2128 Options.store(Opts, "NamePrefixSuffixSilenceDissimilarityThreshold",2129 NamePrefixSuffixSilenceDissimilarityThreshold);2130}2131 2132void EasilySwappableParametersCheck::registerMatchers(MatchFinder *Finder) {2133 const auto BaseConstraints = functionDecl(2134 // Only report for definition nodes, as fixing the issues reported2135 // requires the user to be able to change code.2136 isDefinition(), parameterCountGE(MinimumLength),2137 unless(isOverloadedUnaryOrBinaryOperator()));2138 2139 Finder->addMatcher(2140 functionDecl(BaseConstraints,2141 unless(ast_matchers::isTemplateInstantiation()))2142 .bind("func"),2143 this);2144 Finder->addMatcher(2145 functionDecl(BaseConstraints, isExplicitTemplateSpecialization())2146 .bind("func"),2147 this);2148}2149 2150void EasilySwappableParametersCheck::check(2151 const MatchFinder::MatchResult &Result) {2152 using namespace model;2153 using namespace filter;2154 2155 const auto *FD = Result.Nodes.getNodeAs<FunctionDecl>("func");2156 assert(FD);2157 2158 const PrintingPolicy &PP = FD->getASTContext().getPrintingPolicy();2159 const std::size_t NumParams = FD->getNumParams();2160 std::size_t MixableRangeStartIndex = 0;2161 2162 // Spawn one suppressor and if the user requested, gather information from2163 // the AST for the parameters' usages.2164 const filter::SimilarlyUsedParameterPairSuppressor UsageBasedSuppressor{2165 FD, SuppressParametersUsedTogether};2166 2167 LLVM_DEBUG(llvm::dbgs() << "Begin analysis of " << getName(FD) << " with "2168 << NumParams << " parameters...\n");2169 while (MixableRangeStartIndex < NumParams) {2170 if (isIgnoredParameter(*this, FD->getParamDecl(MixableRangeStartIndex))) {2171 LLVM_DEBUG(llvm::dbgs()2172 << "Parameter #" << MixableRangeStartIndex << " ignored.\n");2173 ++MixableRangeStartIndex;2174 continue;2175 }2176 2177 MixableParameterRange R = modelMixingRange(2178 *this, FD, MixableRangeStartIndex, UsageBasedSuppressor);2179 assert(R.NumParamsChecked > 0 && "Ensure forward progress!");2180 MixableRangeStartIndex += R.NumParamsChecked;2181 if (R.NumParamsChecked < MinimumLength) {2182 LLVM_DEBUG(llvm::dbgs() << "Ignoring range of " << R.NumParamsChecked2183 << " lower than limit.\n");2184 continue;2185 }2186 2187 const bool NeedsAnyTypeNote =2188 llvm::any_of(R.Mixes, needsToPrintTypeInDiagnostic);2189 const bool HasAnyImplicits =2190 llvm::any_of(R.Mixes, needsToElaborateImplicitConversion);2191 const ParmVarDecl *First = R.getFirstParam(), *Last = R.getLastParam();2192 const std::string FirstParamTypeAsWritten =2193 First->getType().getAsString(PP);2194 {2195 StringRef DiagText;2196 2197 if (HasAnyImplicits)2198 DiagText = "%0 adjacent parameters of %1 of convertible types are "2199 "easily swapped by mistake";2200 else if (NeedsAnyTypeNote)2201 DiagText = "%0 adjacent parameters of %1 of similar type are easily "2202 "swapped by mistake";2203 else2204 DiagText = "%0 adjacent parameters of %1 of similar type ('%2') are "2205 "easily swapped by mistake";2206 2207 auto Diag = diag(First->getOuterLocStart(), DiagText)2208 << static_cast<unsigned>(R.NumParamsChecked) << FD;2209 if (!NeedsAnyTypeNote)2210 Diag << FirstParamTypeAsWritten;2211 2212 const CharSourceRange HighlightRange = CharSourceRange::getTokenRange(2213 First->getBeginLoc(), Last->getEndLoc());2214 Diag << HighlightRange;2215 }2216 2217 // There is a chance that the previous highlight did not succeed, e.g. when2218 // the two parameters are on different lines. For clarity, show the user2219 // the involved variable explicitly.2220 diag(First->getLocation(), "the first parameter in the range is '%0'",2221 DiagnosticIDs::Note)2222 << getNameOrUnnamed(First)2223 << CharSourceRange::getTokenRange(First->getLocation(),2224 First->getLocation());2225 diag(Last->getLocation(), "the last parameter in the range is '%0'",2226 DiagnosticIDs::Note)2227 << getNameOrUnnamed(Last)2228 << CharSourceRange::getTokenRange(Last->getLocation(),2229 Last->getLocation());2230 2231 // Helper classes to silence elaborative diagnostic notes that would be2232 // too verbose.2233 UniqueTypeAliasDiagnosticHelper UniqueTypeAlias;2234 InsertOnce<SwappedEqualQualTypePair, 8> UniqueBindPower;2235 InsertOnce<SwappedEqualQualTypePair, 8> UniqueImplicitConversion;2236 2237 for (const model::Mix &M : R.Mixes) {2238 assert(M.mixable() && "Sentinel or false mix in result.");2239 if (!needsToPrintTypeInDiagnostic(M) &&2240 !needsToElaborateImplicitConversion(M))2241 continue;2242 2243 // Typedefs might result in the type of the variable needing to be2244 // emitted to a note diagnostic, so prepare it.2245 const ParmVarDecl *LVar = M.First;2246 const ParmVarDecl *RVar = M.Second;2247 const QualType LType = LVar->getType();2248 const QualType RType = RVar->getType();2249 const QualType CommonType = M.commonUnderlyingType();2250 const std::string LTypeStr = LType.getAsString(PP);2251 const std::string RTypeStr = RType.getAsString(PP);2252 const std::string CommonTypeStr = CommonType.getAsString(PP);2253 2254 if (hasFlag(M.flags(), MixFlags::TypeAlias) &&2255 UniqueTypeAlias(LType, RType, CommonType)) {2256 StringRef DiagText;2257 bool ExplicitlyPrintCommonType = false;2258 if (LTypeStr == CommonTypeStr || RTypeStr == CommonTypeStr) {2259 if (hasFlag(M.flags(), MixFlags::Qualifiers))2260 DiagText = "after resolving type aliases, '%0' and '%1' share a "2261 "common type";2262 else2263 DiagText =2264 "after resolving type aliases, '%0' and '%1' are the same";2265 } else if (!CommonType.isNull()) {2266 DiagText = "after resolving type aliases, the common type of '%0' "2267 "and '%1' is '%2'";2268 ExplicitlyPrintCommonType = true;2269 }2270 2271 auto Diag =2272 diag(LVar->getOuterLocStart(), DiagText, DiagnosticIDs::Note)2273 << LTypeStr << RTypeStr;2274 if (ExplicitlyPrintCommonType)2275 Diag << CommonTypeStr;2276 }2277 2278 if ((hasFlag(M.flags(), MixFlags::ReferenceBind) ||2279 hasFlag(M.flags(), MixFlags::Qualifiers)) &&2280 UniqueBindPower({LType, RType})) {2281 const StringRef DiagText =2282 "'%0' and '%1' parameters accept and bind the "2283 "same kind of values";2284 diag(RVar->getOuterLocStart(), DiagText, DiagnosticIDs::Note)2285 << LTypeStr << RTypeStr;2286 }2287 2288 if (needsToElaborateImplicitConversion(M) &&2289 UniqueImplicitConversion({LType, RType})) {2290 const model::ConversionSequence <R =2291 M.leftToRightConversionSequence();2292 const model::ConversionSequence &RTL =2293 M.rightToLeftConversionSequence();2294 const FormattedConversionSequence LTRFmt{PP, LTypeStr, LTR, RTypeStr};2295 const FormattedConversionSequence RTLFmt{PP, RTypeStr, RTL, LTypeStr};2296 2297 StringRef DiagText = "'%0' and '%1' may be implicitly converted";2298 if (!LTRFmt.Trivial || !RTLFmt.Trivial)2299 DiagText = "'%0' and '%1' may be implicitly converted: %2, %3";2300 2301 {2302 auto Diag =2303 diag(RVar->getOuterLocStart(), DiagText, DiagnosticIDs::Note)2304 << LTypeStr << RTypeStr;2305 2306 if (!LTRFmt.Trivial || !RTLFmt.Trivial)2307 Diag << LTRFmt.DiagnosticText << RTLFmt.DiagnosticText;2308 }2309 2310 const StringRef ConversionFunctionDiagText =2311 "the implicit conversion involves the "2312 "%select{|converting constructor|conversion operator}0 "2313 "declared here";2314 if (const FunctionDecl *LFD = LTR.getUserDefinedConversionFunction())2315 diag(LFD->getLocation(), ConversionFunctionDiagText,2316 DiagnosticIDs::Note)2317 << static_cast<unsigned>(LTR.UDConvKind)2318 << LTR.getUserDefinedConversionHighlight();2319 if (const FunctionDecl *RFD = RTL.getUserDefinedConversionFunction())2320 diag(RFD->getLocation(), ConversionFunctionDiagText,2321 DiagnosticIDs::Note)2322 << static_cast<unsigned>(RTL.UDConvKind)2323 << RTL.getUserDefinedConversionHighlight();2324 }2325 }2326 }2327}2328 2329} // namespace clang::tidy::bugprone2330