| 1 | //===--- EasilySwappableParametersCheck.cpp - clang-tidy ------------------===// |
| 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-exception |
| 6 | // |
| 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 | |
| 21 | namespace optutils = clang::tidy::utils::options; |
| 22 | |
| 23 | /// The default value for the MinimumLength check option. |
| 24 | static constexpr std::size_t DefaultMinimumLength = 2; |
| 25 | |
| 26 | /// The default value for ignored parameter names. |
| 27 | static 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. |
| 44 | static 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. |
| 76 | static constexpr bool DefaultQualifiersMix = false; |
| 77 | |
| 78 | /// The default value for the ModelImplicitConversions check option. |
| 79 | static constexpr bool DefaultModelImplicitConversions = true; |
| 80 | |
| 81 | /// The default value for suppressing diagnostics about parameters that are |
| 82 | /// used together. |
| 83 | static constexpr bool DefaultSuppressParametersUsedTogether = true; |
| 84 | |
| 85 | /// The default value for the NamePrefixSuffixSilenceDissimilarityTreshold |
| 86 | /// check option. |
| 87 | static constexpr std::size_t |
| 88 | DefaultNamePrefixSuffixSilenceDissimilarityTreshold = 1; |
| 89 | |
| 90 | using namespace clang::ast_matchers; |
| 91 | |
| 92 | namespace clang::tidy::bugprone { |
| 93 | |
| 94 | using TheCheck = EasilySwappableParametersCheck; |
| 95 | |
| 96 | namespace filter { |
| 97 | class SimilarlyUsedParameterPairSuppressor; |
| 98 | |
| 99 | static bool isIgnoredParameter(const TheCheck &Check, const ParmVarDecl *Node); |
| 100 | static inline bool |
| 101 | isSimilarlyUsedParameter(const SimilarlyUsedParameterPairSuppressor &Suppressor, |
| 102 | const ParmVarDecl *Param1, const ParmVarDecl *Param2); |
| 103 | static bool prefixSuffixCoverUnderThreshold(std::size_t Threshold, |
| 104 | StringRef Str1, StringRef Str2); |
| 105 | } // namespace filter |
| 106 | |
| 107 | namespace model { |
| 108 | |
| 109 | /// The language features involved in allowing the mix between two parameters. |
| 110 | enum 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 diagnosed |
| 116 | /// 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 same |
| 122 | /// CanonicalType, but we do not elaborate as to how. |
| 123 | TypeAlias = 16, ///< The path from one type to the other involves |
| 124 | /// 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 possible |
| 128 | /// through implicit conversions between the types. |
| 129 | |
| 130 | LLVM_MARK_AS_BITMASK_ENUM(/* LargestValue =*/ImplicitConversion) |
| 131 | }; |
| 132 | LLVM_ENABLE_BITMASK_ENUMS_IN_NAMESPACE(); |
| 133 | |
| 134 | /// Returns whether the SearchedFlag is turned on in the Data. |
| 135 | static 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 NDEBUG |
| 144 | |
| 145 | // The modelling logic of this check is more complex than usual, and |
| 146 | // potentially hard to understand without the ability to see into the |
| 147 | // representation during the recursive descent. This debug code is only |
| 148 | // 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. |
| 151 | static inline std::string formatMixFlags(MixFlags F) { |
| 152 | if (F == MixFlags::Invalid) |
| 153 | return "#Inv!" ; |
| 154 | |
| 155 | SmallString<8> Str{"-------" }; |
| 156 | |
| 157 | if (hasFlag(Data: F, SearchedFlag: MixFlags::None)) |
| 158 | // Shows the None bit explicitly, as it can be applied in the recursion |
| 159 | // even if other bits are set. |
| 160 | Str[0] = '!'; |
| 161 | if (hasFlag(Data: F, SearchedFlag: MixFlags::Trivial)) |
| 162 | Str[1] = 'T'; |
| 163 | if (hasFlag(Data: F, SearchedFlag: MixFlags::Canonical)) |
| 164 | Str[2] = 'C'; |
| 165 | if (hasFlag(Data: F, SearchedFlag: MixFlags::TypeAlias)) |
| 166 | Str[3] = 't'; |
| 167 | if (hasFlag(Data: F, SearchedFlag: MixFlags::ReferenceBind)) |
| 168 | Str[4] = '&'; |
| 169 | if (hasFlag(Data: F, SearchedFlag: MixFlags::Qualifiers)) |
| 170 | Str[5] = 'Q'; |
| 171 | if (hasFlag(Data: F, SearchedFlag: MixFlags::ImplicitConversion)) |
| 172 | Str[6] = 'i'; |
| 173 | |
| 174 | if (hasFlag(Data: F, SearchedFlag: MixFlags::WorkaroundDisableCanonicalEquivalence)) |
| 175 | Str.append(RHS: "(~C)" ); |
| 176 | |
| 177 | return Str.str().str(); |
| 178 | } |
| 179 | |
| 180 | #endif // NDEBUG |
| 181 | |
| 182 | /// The results of the steps of an Implicit Conversion Sequence is saved in |
| 183 | /// an instance of this record. |
| 184 | /// |
| 185 | /// A ConversionSequence maps the steps of the conversion with a member for |
| 186 | /// each type involved in the conversion. Imagine going from a hypothetical |
| 187 | /// 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=) Complex |
| 200 | /// |
| 201 | /// The first standard conversion is a qualification adjustment. |
| 202 | /// (AfterFirstStandard=) const Complex |
| 203 | /// |
| 204 | /// Then the user-defined conversion is executed. |
| 205 | /// (UDConvOp.ConversionOperatorResultType=) double |
| 206 | /// |
| 207 | /// Then this 'double' is qualifier-adjusted to 'const double'. |
| 208 | /// (AfterSecondStandard=) double |
| 209 | /// |
| 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, because |
| 214 | /// 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 conversion |
| 222 | /// between the two typedefs as written in the code, but to elaborate the |
| 223 | /// reasoning behind this conversion, we also need to show what the typedefs |
| 224 | /// mean. See FormattedConversionSequence towards the bottom of this file! |
| 225 | struct 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 Conversion |
| 255 | /// 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 in |
| 271 | /// 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(Args&: QT); |
| 279 | else if (Ret.back() != QT) |
| 280 | Ret.emplace_back(Args&: 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 the |
| 302 | /// other instance. |
| 303 | /// |
| 304 | /// \note This method does not check if the resulting conversion sequence is |
| 305 | /// 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" once |
| 340 | /// 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 interesting |
| 366 | /// part of the user-defined conversion. This is either the parameter type |
| 367 | /// in a converting constructor, or the conversion result type in a conversion |
| 368 | /// 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 the |
| 375 | // 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. |
| 389 | struct 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, that |
| 394 | /// 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 the |
| 398 | /// other. |
| 399 | ConversionSequence Conversion, ConversionRTL; |
| 400 | |
| 401 | /// True if the MixData was specifically created with only a one-way |
| 402 | /// 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 | 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 throw |
| 425 | // this result away. |
| 426 | Flags = MixFlags::None; |
| 427 | return; |
| 428 | } |
| 429 | |
| 430 | if (hasFlag(Data: Flags, SearchedFlag: MixFlags::None)) { |
| 431 | // If anywhere down the recursion a potential mix "path" is deemed |
| 432 | // impossible, throw away all the other bits because the mix is not |
| 433 | // 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 the |
| 443 | // recursion other bit(s) were set, remove the trivial bit, as it is not |
| 444 | // 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 is |
| 452 | // bidirectional. Otherwise, the compiler would report an *actual* swap |
| 453 | // at a call site... |
| 454 | ShouldHaveImplicitConvFlag = true; |
| 455 | |
| 456 | if (ShouldHaveImplicitConvFlag) |
| 457 | Flags |= MixFlags::ImplicitConversion; |
| 458 | else |
| 459 | 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 | 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 concrete |
| 499 | /// parameters. |
| 500 | struct 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. |
| 521 | static_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 | |
| 526 | struct 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 it |
| 546 | // 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 last |
| 548 | // 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 kinds |
| 555 | /// of implicit conversions are to be modelled. |
| 556 | enum 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 one |
| 564 | /// standard conversion sequence. |
| 565 | OneWaySingleStandardOnly |
| 566 | }; |
| 567 | |
| 568 | static MixData |
| 569 | isLRefEquallyBindingToType(const TheCheck &Check, |
| 570 | const LValueReferenceType *LRef, QualType Ty, |
| 571 | const ASTContext &Ctx, bool IsRefRHS, |
| 572 | ImplicitConversionModellingMode ImplicitMode); |
| 573 | |
| 574 | static MixData |
| 575 | approximateImplicitConversion(const TheCheck &Check, QualType LType, |
| 576 | QualType RType, const ASTContext &Ctx, |
| 577 | ImplicitConversionModellingMode ImplicitMode); |
| 578 | |
| 579 | static inline bool isUselessSugar(const Type *T) { |
| 580 | return isa<AttributedType, DecayedType, ElaboratedType, ParenType>(Val: T); |
| 581 | } |
| 582 | |
| 583 | namespace { |
| 584 | |
| 585 | struct NonCVRQualifiersResult { |
| 586 | /// True if the types are qualified in a way that even after equating or |
| 587 | /// removing local CVR qualification, even if the unqualified types |
| 588 | /// themselves would mix, the qualified ones don't, because there are some |
| 589 | /// 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 | } // namespace |
| 597 | |
| 598 | /// Returns if the two types are qualified in a way that ever after equating or |
| 599 | /// removing local CVR qualification, even if the unqualified types would mix, |
| 600 | /// the qualified ones don't, because there are some other local qualifiers |
| 601 | /// that aren't equal. |
| 602 | static NonCVRQualifiersResult |
| 603 | getNonCVRQualifiers(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(L&: LQual, R&: 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 deduce |
| 627 | // 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 the |
| 635 | /// same" type, in a sense that at a particular call site, an expression of |
| 636 | /// type LType and RType might be successfully passed to a variable (in our |
| 637 | /// 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 this |
| 641 | /// function is potentially recursive. It is the caller's responsibility to |
| 642 | /// call sanitize() on the result once the recursion is over. |
| 643 | static MixData |
| 644 | calculateMixability(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 type |
| 656 | // with the other, and also do not require any sort of elaboration for the |
| 657 | // user to understand. |
| 658 | if (isUselessSugar(T: LType.getTypePtr())) { |
| 659 | LLVM_DEBUG(llvm::dbgs() |
| 660 | << "--- calculateMixability. LHS is useless sugar.\n" ); |
| 661 | return calculateMixability(Check, LType: LType.getSingleStepDesugaredType(Context: Ctx), |
| 662 | RType, Ctx, ImplicitMode); |
| 663 | } |
| 664 | if (isUselessSugar(T: RType.getTypePtr())) { |
| 665 | LLVM_DEBUG(llvm::dbgs() |
| 666 | << "--- calculateMixability. RHS is useless sugar.\n" ); |
| 667 | return calculateMixability( |
| 668 | Check, LType, RType: RType.getSingleStepDesugaredType(Context: 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(T: QT); }); |
| 680 | } |
| 681 | // At a particular call site, what could be passed to a 'T' or 'const T' might |
| 682 | // also be passed to a 'const T &' without the call site putting a direct |
| 683 | // side effect on the passed expressions. |
| 684 | if (LLRef) { |
| 685 | LLVM_DEBUG(llvm::dbgs() << "--- calculateMixability. LHS is &.\n" ); |
| 686 | return isLRefEquallyBindingToType(Check, LRef: LLRef, Ty: RType, Ctx, IsRefRHS: false, |
| 687 | ImplicitMode) | |
| 688 | MixFlags::ReferenceBind; |
| 689 | } |
| 690 | if (RLRef) { |
| 691 | LLVM_DEBUG(llvm::dbgs() << "--- calculateMixability. RHS is &.\n" ); |
| 692 | return isLRefEquallyBindingToType(Check, LRef: RLRef, Ty: LType, Ctx, IsRefRHS: 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: LType.getSingleStepDesugaredType(Context: 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: RType.getSingleStepDesugaredType(Context: Ctx), Ctx, |
| 707 | ImplicitMode) | |
| 708 | MixFlags::TypeAlias; |
| 709 | } |
| 710 | |
| 711 | // A parameter of type 'cvr1 T' and another of potentially differently |
| 712 | // 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 | MixData UnqualifiedMixability = |
| 765 | calculateMixability(Check, LType: LType.getLocalUnqualifiedType(), |
| 766 | RType: RType.getLocalUnqualifiedType(), Ctx, ImplicitMode) |
| 767 | .withCommonTypeTransformed(Func: [&AdditionalQuals, &Ctx](QualType QT) { |
| 768 | // Once the mixability was deduced, apply the qualifiers common |
| 769 | // to the two type back onto the diagnostic printout. |
| 770 | return Ctx.getQualifiedType(T: QT, Qs: 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 were |
| 778 | // the same. |
| 779 | return UnqualifiedMixability.withCommonTypeTransformed( |
| 780 | Func: [&Ctx, LType](QualType QT) { |
| 781 | return Ctx.getQualifiedType(T: QT, Qs: 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 has |
| 793 | // 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: LType->getPointeeType(), |
| 798 | RType: RType->getPointeeType(), Ctx, |
| 799 | ImplicitMode: ImplicitConversionModellingMode::None) |
| 800 | .withCommonTypeTransformed( |
| 801 | Func: [&Ctx](QualType QT) { return Ctx.getPointerType(T: QT); }); |
| 802 | if (hasFlag(Data: MixOfPointee.Flags, |
| 803 | SearchedFlag: 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 | 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 standard |
| 828 | // 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 other |
| 835 | // direction as well. |
| 836 | MixData MixRTL = |
| 837 | approximateImplicitConversion(Check, LType: RType, 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 type |
| 856 | // matching. |
| 857 | if (LType->getAs<FunctionProtoType>() || RType->getAs<FunctionProtoType>()) { |
| 858 | // Unfortunately, the canonical type of a function pointer becomes the |
| 859 | // same even if exactly one is "noexcept" and the other isn't, making us |
| 860 | // 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 otherwise |
| 870 | // believes the types to be the same. |
| 871 | 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 is |
| 887 | /// 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 to |
| 890 | /// calculateMixability. |
| 891 | static MixData |
| 892 | isLRefEquallyBindingToType(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(Context: 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, LType: Ty, RType: NonConstReferredType, Ctx, |
| 943 | ImplicitMode) |
| 944 | : calculateMixability(Check, LType: NonConstReferredType, RType: Ty, Ctx, |
| 945 | ImplicitMode); |
| 946 | } |
| 947 | |
| 948 | static inline bool isDerivedToBase(const CXXRecordDecl *Derived, |
| 949 | const CXXRecordDecl *Base) { |
| 950 | return Derived && Base && Derived->isCompleteDefinition() && |
| 951 | Base->isCompleteDefinition() && Derived->isDerivedFrom(Base); |
| 952 | } |
| 953 | |
| 954 | static std::optional<QualType> |
| 955 | approximateStandardConversionSequence(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 be |
| 968 | // re-applied to the WorkType to ensure it is the same qualification as the |
| 969 | // 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 be |
| 974 | // 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 desugaring |
| 977 | // the parameter's DecayedType as "useless sugar". |
| 978 | |
| 979 | // Function->Pointer conversions are also irrelevant, because a |
| 980 | // "FunctionType" cannot be the type of a parameter variable, so this |
| 981 | // 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 | bool FromNumeric = FromBuiltin && (FromBuiltin->isIntegerType() || |
| 987 | FromBuiltin->isFloatingType()); |
| 988 | 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, so |
| 993 | // "const int -> double" in this case moves over to |
| 994 | // "const double -> double". |
| 995 | LLVM_DEBUG(llvm::dbgs() |
| 996 | << "--- approximateStdConv. Conversion between numerics.\n" ); |
| 997 | WorkType = QualType{ToBuiltin, FastQualifiersToApply}; |
| 998 | } |
| 999 | |
| 1000 | const auto * = WorkType->getAs<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(Derived: FromRecord, Base: ToRecord)) { |
| 1042 | LLVM_DEBUG(llvm::dbgs() << "--- approximateStdConv. Derived To Base.\n" ); |
| 1043 | WorkType = QualType{ToRecord->getTypeForDecl(), FastQualifiersToApply}; |
| 1044 | } |
| 1045 | |
| 1046 | if (Ctx.getLangOpts().CPlusPlus17 && FromPtr && ToPtr) { |
| 1047 | // Function pointer conversion: A noexcept function pointer can be passed |
| 1048 | // to a non-noexcept one. |
| 1049 | const auto *FromFunctionPtr = |
| 1050 | FromPtr->getPointeeType()->getAs<FunctionProtoType>(); |
| 1051 | const auto *ToFunctionPtr = |
| 1052 | ToPtr->getPointeeType()->getAs<FunctionProtoType>(); |
| 1053 | if (FromFunctionPtr && ToFunctionPtr && |
| 1054 | FromFunctionPtr->hasNoexceptExceptionSpec() && |
| 1055 | !ToFunctionPtr->hasNoexceptExceptionSpec()) { |
| 1056 | LLVM_DEBUG(llvm::dbgs() << "--- approximateStdConv. noexcept function " |
| 1057 | "pointer to non-noexcept.\n" ); |
| 1058 | WorkType = QualType{ToPtr, FastQualifiersToApply}; |
| 1059 | } |
| 1060 | } |
| 1061 | |
| 1062 | // Qualifier adjustments are modelled according to the user's request in |
| 1063 | // the QualifiersMix check config. |
| 1064 | LLVM_DEBUG(llvm::dbgs() |
| 1065 | << "--- approximateStdConv. Trying qualifier adjustment...\n" ); |
| 1066 | MixData QualConv = calculateMixability(Check, LType: WorkType, RType: To, Ctx, |
| 1067 | ImplicitMode: ImplicitConversionModellingMode::None); |
| 1068 | QualConv.sanitize(); |
| 1069 | if (hasFlag(Data: QualConv.Flags, SearchedFlag: MixFlags::Qualifiers)) { |
| 1070 | LLVM_DEBUG(llvm::dbgs() |
| 1071 | << "<<< approximateStdConv. Qualifiers adjusted.\n" ); |
| 1072 | WorkType = To; |
| 1073 | } |
| 1074 | |
| 1075 | if (WorkType == To) { |
| 1076 | LLVM_DEBUG(llvm::dbgs() << "<<< approximateStdConv. Reached 'To' type.\n" ); |
| 1077 | return {WorkType}; |
| 1078 | } |
| 1079 | |
| 1080 | LLVM_DEBUG(llvm::dbgs() << "<<< approximateStdConv. Did not reach 'To'.\n" ); |
| 1081 | return {}; |
| 1082 | } |
| 1083 | |
| 1084 | namespace { |
| 1085 | |
| 1086 | /// Helper class for storing possible user-defined conversion calls that |
| 1087 | /// *could* take place in an implicit conversion, and selecting the one that |
| 1088 | /// most likely *does*, if any. |
| 1089 | class UserDefinedConversionSelector { |
| 1090 | public: |
| 1091 | /// The conversion associated with a conversion function, together with the |
| 1092 | /// mixability flags of the conversion function's parameter or return type |
| 1093 | /// to the rest of the sequence the selector is used in, and the sequence |
| 1094 | /// that applied through the conversion itself. |
| 1095 | struct PreparedConversion { |
| 1096 | const CXXMethodDecl *ConversionFun; |
| 1097 | MixFlags Flags; |
| 1098 | ConversionSequence Seq; |
| 1099 | |
| 1100 | PreparedConversion(const CXXMethodDecl *CMD, MixFlags F, |
| 1101 | ConversionSequence S) |
| 1102 | : ConversionFun(CMD), Flags(F), Seq(S) {} |
| 1103 | }; |
| 1104 | |
| 1105 | UserDefinedConversionSelector(const TheCheck &Check) : Check(Check) {} |
| 1106 | |
| 1107 | /// Adds the conversion between the two types for the given function into |
| 1108 | /// the possible implicit conversion set. FromType and ToType is either: |
| 1109 | /// * the result of a standard sequence and a converting ctor parameter |
| 1110 | /// * the return type of a conversion operator and the expected target of |
| 1111 | /// an implicit conversion. |
| 1112 | void addConversion(const CXXMethodDecl *ConvFun, QualType FromType, |
| 1113 | QualType ToType) { |
| 1114 | // Try to go from the FromType to the ToType with only a single implicit |
| 1115 | // conversion, to see if the conversion function is applicable. |
| 1116 | MixData Mix = calculateMixability( |
| 1117 | Check, FromType, ToType, ConvFun->getASTContext(), |
| 1118 | ImplicitConversionModellingMode::OneWaySingleStandardOnly); |
| 1119 | Mix.sanitize(); |
| 1120 | if (!Mix.indicatesMixability()) |
| 1121 | return; |
| 1122 | |
| 1123 | LLVM_DEBUG(llvm::dbgs() << "--- tryConversion. Found viable with flags: " |
| 1124 | << formatMixFlags(Mix.Flags) << '\n'); |
| 1125 | FlaggedConversions.emplace_back(ConvFun, Mix.Flags, Mix.Conversion); |
| 1126 | } |
| 1127 | |
| 1128 | /// Selects the best conversion function that is applicable from the |
| 1129 | /// prepared set of potential conversion functions taken. |
| 1130 | std::optional<PreparedConversion> operator()() const { |
| 1131 | if (FlaggedConversions.empty()) { |
| 1132 | LLVM_DEBUG(llvm::dbgs() << "--- selectUserDefinedConv. Empty.\n" ); |
| 1133 | return {}; |
| 1134 | } |
| 1135 | if (FlaggedConversions.size() == 1) { |
| 1136 | LLVM_DEBUG(llvm::dbgs() << "--- selectUserDefinedConv. Single.\n" ); |
| 1137 | return FlaggedConversions.front(); |
| 1138 | } |
| 1139 | |
| 1140 | std::optional<PreparedConversion> BestConversion; |
| 1141 | unsigned short HowManyGoodConversions = 0; |
| 1142 | for (const auto &Prepared : FlaggedConversions) { |
| 1143 | LLVM_DEBUG(llvm::dbgs() << "--- selectUserDefinedConv. Candidate flags: " |
| 1144 | << formatMixFlags(Prepared.Flags) << '\n'); |
| 1145 | if (!BestConversion) { |
| 1146 | BestConversion = Prepared; |
| 1147 | ++HowManyGoodConversions; |
| 1148 | continue; |
| 1149 | } |
| 1150 | |
| 1151 | bool BestConversionHasImplicit = |
| 1152 | hasFlag(Data: BestConversion->Flags, SearchedFlag: MixFlags::ImplicitConversion); |
| 1153 | bool ThisConversionHasImplicit = |
| 1154 | hasFlag(Data: Prepared.Flags, SearchedFlag: MixFlags::ImplicitConversion); |
| 1155 | if (!BestConversionHasImplicit && ThisConversionHasImplicit) |
| 1156 | // This is a worse conversion, because a better one was found earlier. |
| 1157 | continue; |
| 1158 | |
| 1159 | if (BestConversionHasImplicit && !ThisConversionHasImplicit) { |
| 1160 | // If the so far best selected conversion needs a previous implicit |
| 1161 | // conversion to match the user-defined converting function, but this |
| 1162 | // conversion does not, this is a better conversion, and we can throw |
| 1163 | // away the previously selected conversion(s). |
| 1164 | BestConversion = Prepared; |
| 1165 | HowManyGoodConversions = 1; |
| 1166 | continue; |
| 1167 | } |
| 1168 | |
| 1169 | if (BestConversionHasImplicit == ThisConversionHasImplicit) |
| 1170 | // The current conversion is the same in term of goodness than the |
| 1171 | // already selected one. |
| 1172 | ++HowManyGoodConversions; |
| 1173 | } |
| 1174 | |
| 1175 | if (HowManyGoodConversions == 1) { |
| 1176 | LLVM_DEBUG(llvm::dbgs() |
| 1177 | << "--- selectUserDefinedConv. Unique result. Flags: " |
| 1178 | << formatMixFlags(BestConversion->Flags) << '\n'); |
| 1179 | return BestConversion; |
| 1180 | } |
| 1181 | |
| 1182 | LLVM_DEBUG(llvm::dbgs() |
| 1183 | << "--- selectUserDefinedConv. No, or ambiguous.\n" ); |
| 1184 | return {}; |
| 1185 | } |
| 1186 | |
| 1187 | private: |
| 1188 | llvm::SmallVector<PreparedConversion, 2> FlaggedConversions; |
| 1189 | const TheCheck &Check; |
| 1190 | }; |
| 1191 | |
| 1192 | } // namespace |
| 1193 | |
| 1194 | static std::optional<ConversionSequence> |
| 1195 | tryConversionOperators(const TheCheck &Check, const CXXRecordDecl *RD, |
| 1196 | QualType ToType) { |
| 1197 | if (!RD || !RD->isCompleteDefinition()) |
| 1198 | return {}; |
| 1199 | RD = RD->getDefinition(); |
| 1200 | |
| 1201 | LLVM_DEBUG(llvm::dbgs() << ">>> tryConversionOperators: " << RD->getName() |
| 1202 | << " to:\n" ; |
| 1203 | ToType.dump(llvm::dbgs(), RD->getASTContext()); |
| 1204 | llvm::dbgs() << '\n';); |
| 1205 | |
| 1206 | UserDefinedConversionSelector ConversionSet{Check}; |
| 1207 | |
| 1208 | for (const NamedDecl *Method : RD->getVisibleConversionFunctions()) { |
| 1209 | const auto *Con = dyn_cast<CXXConversionDecl>(Val: Method); |
| 1210 | if (!Con || Con->isExplicit()) |
| 1211 | continue; |
| 1212 | LLVM_DEBUG(llvm::dbgs() << "--- tryConversionOperators. Trying:\n" ; |
| 1213 | Con->dump(llvm::dbgs()); llvm::dbgs() << '\n';); |
| 1214 | |
| 1215 | // Try to go from the result of conversion operator to the expected type, |
| 1216 | // without calculating another user-defined conversion. |
| 1217 | ConversionSet.addConversion(Con, Con->getConversionType(), ToType); |
| 1218 | } |
| 1219 | |
| 1220 | if (std::optional<UserDefinedConversionSelector::PreparedConversion> |
| 1221 | SelectedConversion = ConversionSet()) { |
| 1222 | QualType RecordType{RD->getTypeForDecl(), 0}; |
| 1223 | |
| 1224 | ConversionSequence Result{RecordType, ToType}; |
| 1225 | // The conversion from the operator call's return type to ToType was |
| 1226 | // modelled as a "pre-conversion" in the operator call, but it is the |
| 1227 | // "post-conversion" from the point of view of the original conversion |
| 1228 | // we are modelling. |
| 1229 | Result.AfterSecondStandard = SelectedConversion->Seq.AfterFirstStandard; |
| 1230 | |
| 1231 | ConversionSequence::UserDefinedConversionOperator ConvOp; |
| 1232 | ConvOp.Fun = cast<CXXConversionDecl>(Val: SelectedConversion->ConversionFun); |
| 1233 | ConvOp.UserDefinedType = RecordType; |
| 1234 | ConvOp.ConversionOperatorResultType = ConvOp.Fun->getConversionType(); |
| 1235 | Result.setConversion(ConvOp); |
| 1236 | |
| 1237 | LLVM_DEBUG(llvm::dbgs() << "<<< tryConversionOperators. Found result.\n" ); |
| 1238 | return Result; |
| 1239 | } |
| 1240 | |
| 1241 | LLVM_DEBUG(llvm::dbgs() << "<<< tryConversionOperators. No conversion.\n" ); |
| 1242 | return {}; |
| 1243 | } |
| 1244 | |
| 1245 | static std::optional<ConversionSequence> |
| 1246 | tryConvertingConstructors(const TheCheck &Check, QualType FromType, |
| 1247 | const CXXRecordDecl *RD) { |
| 1248 | if (!RD || !RD->isCompleteDefinition()) |
| 1249 | return {}; |
| 1250 | RD = RD->getDefinition(); |
| 1251 | |
| 1252 | LLVM_DEBUG(llvm::dbgs() << ">>> tryConveringConstructors: " << RD->getName() |
| 1253 | << " from:\n" ; |
| 1254 | FromType.dump(llvm::dbgs(), RD->getASTContext()); |
| 1255 | llvm::dbgs() << '\n';); |
| 1256 | |
| 1257 | UserDefinedConversionSelector ConversionSet{Check}; |
| 1258 | |
| 1259 | for (const CXXConstructorDecl *Con : RD->ctors()) { |
| 1260 | if (Con->isCopyOrMoveConstructor() || |
| 1261 | !Con->isConvertingConstructor(/* AllowExplicit =*/false)) |
| 1262 | continue; |
| 1263 | LLVM_DEBUG(llvm::dbgs() << "--- tryConvertingConstructors. Trying:\n" ; |
| 1264 | Con->dump(llvm::dbgs()); llvm::dbgs() << '\n';); |
| 1265 | |
| 1266 | // Try to go from the original FromType to the converting constructor's |
| 1267 | // parameter type without another user-defined conversion. |
| 1268 | ConversionSet.addConversion(ConvFun: Con, FromType, ToType: Con->getParamDecl(0)->getType()); |
| 1269 | } |
| 1270 | |
| 1271 | if (std::optional<UserDefinedConversionSelector::PreparedConversion> |
| 1272 | SelectedConversion = ConversionSet()) { |
| 1273 | QualType RecordType{RD->getTypeForDecl(), 0}; |
| 1274 | |
| 1275 | ConversionSequence Result{FromType, RecordType}; |
| 1276 | Result.AfterFirstStandard = SelectedConversion->Seq.AfterFirstStandard; |
| 1277 | |
| 1278 | ConversionSequence::UserDefinedConvertingConstructor Ctor; |
| 1279 | Ctor.Fun = cast<CXXConstructorDecl>(Val: SelectedConversion->ConversionFun); |
| 1280 | Ctor.ConstructorParameterType = Ctor.Fun->getParamDecl(0)->getType(); |
| 1281 | Ctor.UserDefinedType = RecordType; |
| 1282 | Result.setConversion(Ctor); |
| 1283 | |
| 1284 | LLVM_DEBUG(llvm::dbgs() |
| 1285 | << "<<< tryConvertingConstructors. Found result.\n" ); |
| 1286 | return Result; |
| 1287 | } |
| 1288 | |
| 1289 | LLVM_DEBUG(llvm::dbgs() << "<<< tryConvertingConstructors. No conversion.\n" ); |
| 1290 | return {}; |
| 1291 | } |
| 1292 | |
| 1293 | /// Returns whether an expression of LType can be used in an RType context, as |
| 1294 | /// per the implicit conversion rules. |
| 1295 | /// |
| 1296 | /// Note: the result of this operation, unlike that of calculateMixability, is |
| 1297 | /// **NOT** symmetric. |
| 1298 | static MixData |
| 1299 | approximateImplicitConversion(const TheCheck &Check, QualType LType, |
| 1300 | QualType RType, const ASTContext &Ctx, |
| 1301 | ImplicitConversionModellingMode ImplicitMode) { |
| 1302 | LLVM_DEBUG(llvm::dbgs() << ">>> approximateImplicitConversion for LType:\n" ; |
| 1303 | LType.dump(llvm::dbgs(), Ctx); llvm::dbgs() << "\nand RType:\n" ; |
| 1304 | RType.dump(llvm::dbgs(), Ctx); |
| 1305 | llvm::dbgs() << "\nimplicit mode: " ; switch (ImplicitMode) { |
| 1306 | case ImplicitConversionModellingMode::None: |
| 1307 | llvm::dbgs() << "None" ; |
| 1308 | break; |
| 1309 | case ImplicitConversionModellingMode::All: |
| 1310 | llvm::dbgs() << "All" ; |
| 1311 | break; |
| 1312 | case ImplicitConversionModellingMode::OneWaySingleStandardOnly: |
| 1313 | llvm::dbgs() << "OneWay, Single, STD Only" ; |
| 1314 | break; |
| 1315 | } llvm::dbgs() << '\n';); |
| 1316 | if (LType == RType) |
| 1317 | return {MixFlags::Trivial, LType}; |
| 1318 | |
| 1319 | // An implicit conversion sequence consists of the following, in order: |
| 1320 | // * Maybe standard conversion sequence. |
| 1321 | // * Maybe user-defined conversion. |
| 1322 | // * Maybe standard conversion sequence. |
| 1323 | ConversionSequence ImplicitSeq{LType, RType}; |
| 1324 | QualType WorkType = LType; |
| 1325 | |
| 1326 | std::optional<QualType> AfterFirstStdConv = |
| 1327 | approximateStandardConversionSequence(Check, LType, RType, Ctx); |
| 1328 | if (AfterFirstStdConv) { |
| 1329 | LLVM_DEBUG(llvm::dbgs() << "--- approximateImplicitConversion. Standard " |
| 1330 | "Pre-Conversion found!\n" ); |
| 1331 | ImplicitSeq.AfterFirstStandard = *AfterFirstStdConv; |
| 1332 | WorkType = ImplicitSeq.AfterFirstStandard; |
| 1333 | } |
| 1334 | |
| 1335 | if (ImplicitMode == ImplicitConversionModellingMode::OneWaySingleStandardOnly) |
| 1336 | // If the caller only requested modelling of a standard conversion, bail. |
| 1337 | return {ImplicitSeq.AfterFirstStandard.isNull() |
| 1338 | ? MixFlags::None |
| 1339 | : MixFlags::ImplicitConversion, |
| 1340 | ImplicitSeq}; |
| 1341 | |
| 1342 | if (Ctx.getLangOpts().CPlusPlus) { |
| 1343 | bool FoundConversionOperator = false, FoundConvertingCtor = false; |
| 1344 | |
| 1345 | if (const auto *LRD = WorkType->getAsCXXRecordDecl()) { |
| 1346 | std::optional<ConversionSequence> ConversionOperatorResult = |
| 1347 | tryConversionOperators(Check, LRD, RType); |
| 1348 | if (ConversionOperatorResult) { |
| 1349 | LLVM_DEBUG(llvm::dbgs() << "--- approximateImplicitConversion. Found " |
| 1350 | "conversion operator.\n" ); |
| 1351 | ImplicitSeq.update(RHS: *ConversionOperatorResult); |
| 1352 | WorkType = ImplicitSeq.getTypeAfterUserDefinedConversion(); |
| 1353 | FoundConversionOperator = true; |
| 1354 | } |
| 1355 | } |
| 1356 | |
| 1357 | if (const auto *RRD = RType->getAsCXXRecordDecl()) { |
| 1358 | // Use the original "LType" here, and not WorkType, because the |
| 1359 | // conversion to the converting constructors' parameters will be |
| 1360 | // modelled in the recursive call. |
| 1361 | std::optional<ConversionSequence> ConvCtorResult = |
| 1362 | tryConvertingConstructors(Check, FromType: LType, RD: RRD); |
| 1363 | if (ConvCtorResult) { |
| 1364 | LLVM_DEBUG(llvm::dbgs() << "--- approximateImplicitConversion. Found " |
| 1365 | "converting constructor.\n" ); |
| 1366 | ImplicitSeq.update(RHS: *ConvCtorResult); |
| 1367 | WorkType = ImplicitSeq.getTypeAfterUserDefinedConversion(); |
| 1368 | FoundConvertingCtor = true; |
| 1369 | } |
| 1370 | } |
| 1371 | |
| 1372 | if (FoundConversionOperator && FoundConvertingCtor) { |
| 1373 | // If both an operator and a ctor matches, the sequence is ambiguous. |
| 1374 | LLVM_DEBUG(llvm::dbgs() |
| 1375 | << "<<< approximateImplicitConversion. Found both " |
| 1376 | "user-defined conversion kinds in the same sequence!\n" ); |
| 1377 | return {MixFlags::None}; |
| 1378 | } |
| 1379 | } |
| 1380 | |
| 1381 | // After the potential user-defined conversion, another standard conversion |
| 1382 | // sequence might exist. |
| 1383 | LLVM_DEBUG( |
| 1384 | llvm::dbgs() |
| 1385 | << "--- approximateImplicitConversion. Try to find post-conversion.\n" ); |
| 1386 | MixData SecondStdConv = approximateImplicitConversion( |
| 1387 | Check, LType: WorkType, RType, Ctx, |
| 1388 | ImplicitMode: ImplicitConversionModellingMode::OneWaySingleStandardOnly); |
| 1389 | if (SecondStdConv.indicatesMixability()) { |
| 1390 | LLVM_DEBUG(llvm::dbgs() << "--- approximateImplicitConversion. Standard " |
| 1391 | "Post-Conversion found!\n" ); |
| 1392 | |
| 1393 | // The single-step modelling puts the modelled conversion into the "PreStd" |
| 1394 | // variable in the recursive call, but from the PoV of this function, it is |
| 1395 | // the post-conversion. |
| 1396 | ImplicitSeq.AfterSecondStandard = |
| 1397 | SecondStdConv.Conversion.AfterFirstStandard; |
| 1398 | WorkType = ImplicitSeq.AfterSecondStandard; |
| 1399 | } |
| 1400 | |
| 1401 | if (ImplicitSeq) { |
| 1402 | LLVM_DEBUG(llvm::dbgs() |
| 1403 | << "<<< approximateImplicitConversion. Found a conversion.\n" ); |
| 1404 | return {MixFlags::ImplicitConversion, ImplicitSeq}; |
| 1405 | } |
| 1406 | |
| 1407 | LLVM_DEBUG( |
| 1408 | llvm::dbgs() << "<<< approximateImplicitConversion. No match found.\n" ); |
| 1409 | return {MixFlags::None}; |
| 1410 | } |
| 1411 | |
| 1412 | static MixableParameterRange modelMixingRange( |
| 1413 | const TheCheck &Check, const FunctionDecl *FD, std::size_t StartIndex, |
| 1414 | const filter::SimilarlyUsedParameterPairSuppressor &UsageBasedSuppressor) { |
| 1415 | std::size_t NumParams = FD->getNumParams(); |
| 1416 | assert(StartIndex < NumParams && "out of bounds for start" ); |
| 1417 | const ASTContext &Ctx = FD->getASTContext(); |
| 1418 | |
| 1419 | MixableParameterRange Ret; |
| 1420 | // A parameter at index 'StartIndex' had been trivially "checked". |
| 1421 | Ret.NumParamsChecked = 1; |
| 1422 | |
| 1423 | for (std::size_t I = StartIndex + 1; I < NumParams; ++I) { |
| 1424 | const ParmVarDecl *Ith = FD->getParamDecl(i: I); |
| 1425 | StringRef ParamName = Ith->getName(); |
| 1426 | LLVM_DEBUG(llvm::dbgs() |
| 1427 | << "Check param #" << I << " '" << ParamName << "'...\n" ); |
| 1428 | if (filter::isIgnoredParameter(Check, Node: Ith)) { |
| 1429 | LLVM_DEBUG(llvm::dbgs() << "Param #" << I << " is ignored. Break!\n" ); |
| 1430 | break; |
| 1431 | } |
| 1432 | |
| 1433 | StringRef PrevParamName = FD->getParamDecl(i: I - 1)->getName(); |
| 1434 | if (!ParamName.empty() && !PrevParamName.empty() && |
| 1435 | filter::prefixSuffixCoverUnderThreshold( |
| 1436 | Threshold: Check.NamePrefixSuffixSilenceDissimilarityTreshold, Str1: PrevParamName, |
| 1437 | Str2: ParamName)) { |
| 1438 | LLVM_DEBUG(llvm::dbgs() << "Parameter '" << ParamName |
| 1439 | << "' follows a pattern with previous parameter '" |
| 1440 | << PrevParamName << "'. Break!\n" ); |
| 1441 | break; |
| 1442 | } |
| 1443 | |
| 1444 | // Now try to go forward and build the range of [Start, ..., I, I + 1, ...] |
| 1445 | // parameters that can be messed up at a call site. |
| 1446 | MixableParameterRange::MixVector MixesOfIth; |
| 1447 | for (std::size_t J = StartIndex; J < I; ++J) { |
| 1448 | const ParmVarDecl *Jth = FD->getParamDecl(i: J); |
| 1449 | LLVM_DEBUG(llvm::dbgs() |
| 1450 | << "Check mix of #" << J << " against #" << I << "...\n" ); |
| 1451 | |
| 1452 | if (isSimilarlyUsedParameter(Suppressor: UsageBasedSuppressor, Param1: Ith, Param2: Jth)) { |
| 1453 | // Consider the two similarly used parameters to not be possible in a |
| 1454 | // mix-up at the user's request, if they enabled this heuristic. |
| 1455 | LLVM_DEBUG(llvm::dbgs() << "Parameters #" << I << " and #" << J |
| 1456 | << " deemed related, ignoring...\n" ); |
| 1457 | |
| 1458 | // If the parameter #I and #J mixes, then I is mixable with something |
| 1459 | // in the current range, so the range has to be broken and I not |
| 1460 | // included. |
| 1461 | MixesOfIth.clear(); |
| 1462 | break; |
| 1463 | } |
| 1464 | |
| 1465 | Mix M{Jth, Ith, |
| 1466 | calculateMixability(Check, Jth->getType(), Ith->getType(), Ctx, |
| 1467 | Check.ModelImplicitConversions |
| 1468 | ? ImplicitConversionModellingMode::All |
| 1469 | : ImplicitConversionModellingMode::None)}; |
| 1470 | LLVM_DEBUG(llvm::dbgs() << "Mix flags (raw) : " |
| 1471 | << formatMixFlags(M.flags()) << '\n'); |
| 1472 | M.sanitize(); |
| 1473 | LLVM_DEBUG(llvm::dbgs() << "Mix flags (after sanitize): " |
| 1474 | << formatMixFlags(M.flags()) << '\n'); |
| 1475 | |
| 1476 | assert(M.flagsValid() && "All flags decayed!" ); |
| 1477 | |
| 1478 | if (M.mixable()) |
| 1479 | MixesOfIth.emplace_back(Args: std::move(M)); |
| 1480 | } |
| 1481 | |
| 1482 | if (MixesOfIth.empty()) { |
| 1483 | // If there weren't any new mixes stored for Ith, the range is |
| 1484 | // [Start, ..., I]. |
| 1485 | LLVM_DEBUG(llvm::dbgs() |
| 1486 | << "Param #" << I |
| 1487 | << " does not mix with any in the current range. Break!\n" ); |
| 1488 | break; |
| 1489 | } |
| 1490 | |
| 1491 | Ret.Mixes.insert(I: Ret.Mixes.end(), From: MixesOfIth.begin(), To: MixesOfIth.end()); |
| 1492 | ++Ret.NumParamsChecked; // Otherwise a new param was iterated. |
| 1493 | } |
| 1494 | |
| 1495 | return Ret; |
| 1496 | } |
| 1497 | |
| 1498 | } // namespace model |
| 1499 | |
| 1500 | namespace { |
| 1501 | /// Matches DeclRefExprs and their ignorable wrappers to ParmVarDecls. |
| 1502 | AST_MATCHER_FUNCTION(ast_matchers::internal::Matcher<Stmt>, paramRefExpr) { |
| 1503 | return expr(ignoringParenImpCasts(InnerMatcher: ignoringElidableConstructorCall( |
| 1504 | InnerMatcher: declRefExpr(to(InnerMatcher: parmVarDecl().bind(ID: "param" )))))); |
| 1505 | } |
| 1506 | } // namespace |
| 1507 | |
| 1508 | namespace filter { |
| 1509 | |
| 1510 | /// Returns whether the parameter's name or the parameter's type's name is |
| 1511 | /// configured by the user to be ignored from analysis and diagnostic. |
| 1512 | static bool isIgnoredParameter(const TheCheck &Check, const ParmVarDecl *Node) { |
| 1513 | LLVM_DEBUG(llvm::dbgs() << "Checking if '" << Node->getName() |
| 1514 | << "' is ignored.\n" ); |
| 1515 | |
| 1516 | if (!Node->getIdentifier()) |
| 1517 | return llvm::is_contained(Range: Check.IgnoredParameterNames, Element: "\"\"" ); |
| 1518 | |
| 1519 | StringRef NodeName = Node->getName(); |
| 1520 | if (llvm::is_contained(Range: Check.IgnoredParameterNames, Element: NodeName)) { |
| 1521 | LLVM_DEBUG(llvm::dbgs() << "\tName ignored.\n" ); |
| 1522 | return true; |
| 1523 | } |
| 1524 | |
| 1525 | StringRef NodeTypeName = [Node] { |
| 1526 | const ASTContext &Ctx = Node->getASTContext(); |
| 1527 | const SourceManager &SM = Ctx.getSourceManager(); |
| 1528 | SourceLocation B = Node->getTypeSpecStartLoc(); |
| 1529 | SourceLocation E = Node->getTypeSpecEndLoc(); |
| 1530 | LangOptions LO; |
| 1531 | |
| 1532 | LLVM_DEBUG(llvm::dbgs() << "\tType name code is '" |
| 1533 | << Lexer::getSourceText( |
| 1534 | CharSourceRange::getTokenRange(B, E), SM, LO) |
| 1535 | << "'...\n" ); |
| 1536 | if (B.isMacroID()) { |
| 1537 | LLVM_DEBUG(llvm::dbgs() << "\t\tBeginning is macro.\n" ); |
| 1538 | B = SM.getTopMacroCallerLoc(Loc: B); |
| 1539 | } |
| 1540 | if (E.isMacroID()) { |
| 1541 | LLVM_DEBUG(llvm::dbgs() << "\t\tEnding is macro.\n" ); |
| 1542 | E = Lexer::getLocForEndOfToken(Loc: SM.getTopMacroCallerLoc(Loc: E), Offset: 0, SM, LangOpts: LO); |
| 1543 | } |
| 1544 | LLVM_DEBUG(llvm::dbgs() << "\tType name code is '" |
| 1545 | << Lexer::getSourceText( |
| 1546 | CharSourceRange::getTokenRange(B, E), SM, LO) |
| 1547 | << "'...\n" ); |
| 1548 | |
| 1549 | return Lexer::getSourceText(CharSourceRange::getTokenRange(B, E), SM, LO); |
| 1550 | }(); |
| 1551 | |
| 1552 | LLVM_DEBUG(llvm::dbgs() << "\tType name is '" << NodeTypeName << "'\n" ); |
| 1553 | if (!NodeTypeName.empty()) { |
| 1554 | if (llvm::any_of(Range: Check.IgnoredParameterTypeSuffixes, |
| 1555 | P: [NodeTypeName](StringRef E) { |
| 1556 | return !E.empty() && NodeTypeName.ends_with(E); |
| 1557 | })) { |
| 1558 | LLVM_DEBUG(llvm::dbgs() << "\tType suffix ignored.\n" ); |
| 1559 | return true; |
| 1560 | } |
| 1561 | } |
| 1562 | |
| 1563 | return false; |
| 1564 | } |
| 1565 | |
| 1566 | /// This namespace contains the implementations for the suppression of |
| 1567 | /// diagnostics from similarly-used ("related") parameters. |
| 1568 | namespace relatedness_heuristic { |
| 1569 | |
| 1570 | static constexpr std::size_t SmallDataStructureSize = 4; |
| 1571 | |
| 1572 | template <typename T, std::size_t N = SmallDataStructureSize> |
| 1573 | using ParamToSmallSetMap = |
| 1574 | llvm::DenseMap<const ParmVarDecl *, llvm::SmallSet<T, N>>; |
| 1575 | |
| 1576 | /// Returns whether the sets mapped to the two elements in the map have at |
| 1577 | /// least one element in common. |
| 1578 | template <typename MapTy, typename ElemTy> |
| 1579 | static bool lazyMapOfSetsIntersectionExists(const MapTy &Map, const ElemTy &E1, |
| 1580 | const ElemTy &E2) { |
| 1581 | auto E1Iterator = Map.find(E1); |
| 1582 | auto E2Iterator = Map.find(E2); |
| 1583 | if (E1Iterator == Map.end() || E2Iterator == Map.end()) |
| 1584 | return false; |
| 1585 | |
| 1586 | for (const auto &E1SetElem : E1Iterator->second) |
| 1587 | if (E2Iterator->second.contains(E1SetElem)) |
| 1588 | return true; |
| 1589 | |
| 1590 | return false; |
| 1591 | } |
| 1592 | |
| 1593 | /// Implements the heuristic that marks two parameters related if there is |
| 1594 | /// a usage for both in the same strict expression subtree. A strict |
| 1595 | /// expression subtree is a tree which only includes Expr nodes, i.e. no |
| 1596 | /// Stmts and no Decls. |
| 1597 | class AppearsInSameExpr : public RecursiveASTVisitor<AppearsInSameExpr> { |
| 1598 | using Base = RecursiveASTVisitor<AppearsInSameExpr>; |
| 1599 | |
| 1600 | const FunctionDecl *FD; |
| 1601 | const Expr *CurrentExprOnlyTreeRoot = nullptr; |
| 1602 | llvm::DenseMap<const ParmVarDecl *, |
| 1603 | llvm::SmallPtrSet<const Expr *, SmallDataStructureSize>> |
| 1604 | ParentExprsForParamRefs; |
| 1605 | |
| 1606 | public: |
| 1607 | void setup(const FunctionDecl *FD) { |
| 1608 | this->FD = FD; |
| 1609 | TraverseFunctionDecl(const_cast<FunctionDecl *>(FD)); |
| 1610 | } |
| 1611 | |
| 1612 | bool operator()(const ParmVarDecl *Param1, const ParmVarDecl *Param2) const { |
| 1613 | return lazyMapOfSetsIntersectionExists(Map: ParentExprsForParamRefs, E1: Param1, |
| 1614 | E2: Param2); |
| 1615 | } |
| 1616 | |
| 1617 | bool TraverseDecl(Decl *D) { |
| 1618 | CurrentExprOnlyTreeRoot = nullptr; |
| 1619 | return Base::TraverseDecl(D); |
| 1620 | } |
| 1621 | |
| 1622 | bool TraverseStmt(Stmt *S, DataRecursionQueue *Queue = nullptr) { |
| 1623 | if (auto *E = dyn_cast_or_null<Expr>(Val: S)) { |
| 1624 | bool RootSetInCurrentStackFrame = false; |
| 1625 | if (!CurrentExprOnlyTreeRoot) { |
| 1626 | CurrentExprOnlyTreeRoot = E; |
| 1627 | RootSetInCurrentStackFrame = true; |
| 1628 | } |
| 1629 | |
| 1630 | bool Ret = Base::TraverseStmt(S); |
| 1631 | |
| 1632 | if (RootSetInCurrentStackFrame) |
| 1633 | CurrentExprOnlyTreeRoot = nullptr; |
| 1634 | |
| 1635 | return Ret; |
| 1636 | } |
| 1637 | |
| 1638 | // A Stmt breaks the strictly Expr subtree. |
| 1639 | CurrentExprOnlyTreeRoot = nullptr; |
| 1640 | return Base::TraverseStmt(S); |
| 1641 | } |
| 1642 | |
| 1643 | bool VisitDeclRefExpr(DeclRefExpr *DRE) { |
| 1644 | if (!CurrentExprOnlyTreeRoot) |
| 1645 | return true; |
| 1646 | |
| 1647 | if (auto *PVD = dyn_cast<ParmVarDecl>(Val: DRE->getDecl())) |
| 1648 | if (llvm::find(Range: FD->parameters(), Val: PVD)) |
| 1649 | ParentExprsForParamRefs[PVD].insert(Ptr: CurrentExprOnlyTreeRoot); |
| 1650 | |
| 1651 | return true; |
| 1652 | } |
| 1653 | }; |
| 1654 | |
| 1655 | /// Implements the heuristic that marks two parameters related if there are |
| 1656 | /// two separate calls to the same function (overload) and the parameters are |
| 1657 | /// passed to the same index in both calls, i.e f(a, b) and f(a, c) passes |
| 1658 | /// b and c to the same index (2) of f(), marking them related. |
| 1659 | class PassedToSameFunction { |
| 1660 | ParamToSmallSetMap<std::pair<const FunctionDecl *, unsigned>> TargetParams; |
| 1661 | |
| 1662 | public: |
| 1663 | void setup(const FunctionDecl *FD) { |
| 1664 | auto ParamsAsArgsInFnCalls = |
| 1665 | match(functionDecl(forEachDescendant( |
| 1666 | callExpr(forEachArgumentWithParam( |
| 1667 | ArgMatcher: paramRefExpr(), ParamMatcher: parmVarDecl().bind(ID: "passed-to" ))) |
| 1668 | .bind(ID: "call-expr" ))), |
| 1669 | *FD, FD->getASTContext()); |
| 1670 | for (const auto &Match : ParamsAsArgsInFnCalls) { |
| 1671 | const auto *PassedParamOfThisFn = Match.getNodeAs<ParmVarDecl>("param" ); |
| 1672 | const auto *CE = Match.getNodeAs<CallExpr>("call-expr" ); |
| 1673 | const auto *PassedToParam = Match.getNodeAs<ParmVarDecl>("passed-to" ); |
| 1674 | assert(PassedParamOfThisFn && CE && PassedToParam); |
| 1675 | |
| 1676 | const FunctionDecl *CalledFn = CE->getDirectCallee(); |
| 1677 | if (!CalledFn) |
| 1678 | continue; |
| 1679 | |
| 1680 | std::optional<unsigned> TargetIdx; |
| 1681 | unsigned NumFnParams = CalledFn->getNumParams(); |
| 1682 | for (unsigned Idx = 0; Idx < NumFnParams; ++Idx) |
| 1683 | if (CalledFn->getParamDecl(Idx) == PassedToParam) |
| 1684 | TargetIdx.emplace(Idx); |
| 1685 | |
| 1686 | assert(TargetIdx && "Matched, but didn't find index?" ); |
| 1687 | TargetParams[PassedParamOfThisFn].insert( |
| 1688 | {CalledFn->getCanonicalDecl(), *TargetIdx}); |
| 1689 | } |
| 1690 | } |
| 1691 | |
| 1692 | bool operator()(const ParmVarDecl *Param1, const ParmVarDecl *Param2) const { |
| 1693 | return lazyMapOfSetsIntersectionExists(Map: TargetParams, E1: Param1, E2: Param2); |
| 1694 | } |
| 1695 | }; |
| 1696 | |
| 1697 | /// Implements the heuristic that marks two parameters related if the same |
| 1698 | /// member is accessed (referred to) inside the current function's body. |
| 1699 | class AccessedSameMemberOf { |
| 1700 | ParamToSmallSetMap<const Decl *> AccessedMembers; |
| 1701 | |
| 1702 | public: |
| 1703 | void setup(const FunctionDecl *FD) { |
| 1704 | auto MembersCalledOnParams = match( |
| 1705 | functionDecl(forEachDescendant( |
| 1706 | memberExpr(hasObjectExpression(InnerMatcher: paramRefExpr())).bind(ID: "mem-expr" ))), |
| 1707 | *FD, FD->getASTContext()); |
| 1708 | |
| 1709 | for (const auto &Match : MembersCalledOnParams) { |
| 1710 | const auto *AccessedParam = Match.getNodeAs<ParmVarDecl>("param" ); |
| 1711 | const auto *ME = Match.getNodeAs<MemberExpr>("mem-expr" ); |
| 1712 | assert(AccessedParam && ME); |
| 1713 | AccessedMembers[AccessedParam].insert( |
| 1714 | ME->getMemberDecl()->getCanonicalDecl()); |
| 1715 | } |
| 1716 | } |
| 1717 | |
| 1718 | bool operator()(const ParmVarDecl *Param1, const ParmVarDecl *Param2) const { |
| 1719 | return lazyMapOfSetsIntersectionExists(Map: AccessedMembers, E1: Param1, E2: Param2); |
| 1720 | } |
| 1721 | }; |
| 1722 | |
| 1723 | /// Implements the heuristic that marks two parameters related if different |
| 1724 | /// ReturnStmts return them from the function. |
| 1725 | class Returned { |
| 1726 | llvm::SmallVector<const ParmVarDecl *, SmallDataStructureSize> ReturnedParams; |
| 1727 | |
| 1728 | public: |
| 1729 | void setup(const FunctionDecl *FD) { |
| 1730 | // TODO: Handle co_return. |
| 1731 | auto ParamReturns = match(functionDecl(forEachDescendant( |
| 1732 | returnStmt(hasReturnValue(InnerMatcher: paramRefExpr())))), |
| 1733 | *FD, FD->getASTContext()); |
| 1734 | for (const auto &Match : ParamReturns) { |
| 1735 | const auto *ReturnedParam = Match.getNodeAs<ParmVarDecl>("param" ); |
| 1736 | assert(ReturnedParam); |
| 1737 | |
| 1738 | if (find(FD->parameters(), ReturnedParam) == FD->param_end()) |
| 1739 | // Inside the subtree of a FunctionDecl there might be ReturnStmts of |
| 1740 | // a parameter that isn't the parameter of the function, e.g. in the |
| 1741 | // case of lambdas. |
| 1742 | continue; |
| 1743 | |
| 1744 | ReturnedParams.emplace_back(ReturnedParam); |
| 1745 | } |
| 1746 | } |
| 1747 | |
| 1748 | bool operator()(const ParmVarDecl *Param1, const ParmVarDecl *Param2) const { |
| 1749 | return llvm::is_contained(Range: ReturnedParams, Element: Param1) && |
| 1750 | llvm::is_contained(Range: ReturnedParams, Element: Param2); |
| 1751 | } |
| 1752 | }; |
| 1753 | |
| 1754 | } // namespace relatedness_heuristic |
| 1755 | |
| 1756 | /// Helper class that is used to detect if two parameters of the same function |
| 1757 | /// are used in a similar fashion, to suppress the result. |
| 1758 | class SimilarlyUsedParameterPairSuppressor { |
| 1759 | const bool Enabled; |
| 1760 | relatedness_heuristic::AppearsInSameExpr SameExpr; |
| 1761 | relatedness_heuristic::PassedToSameFunction PassToFun; |
| 1762 | relatedness_heuristic::AccessedSameMemberOf SameMember; |
| 1763 | relatedness_heuristic::Returned Returns; |
| 1764 | |
| 1765 | public: |
| 1766 | SimilarlyUsedParameterPairSuppressor(const FunctionDecl *FD, bool Enable) |
| 1767 | : Enabled(Enable) { |
| 1768 | if (!Enable) |
| 1769 | return; |
| 1770 | |
| 1771 | SameExpr.setup(FD); |
| 1772 | PassToFun.setup(FD); |
| 1773 | SameMember.setup(FD); |
| 1774 | Returns.setup(FD); |
| 1775 | } |
| 1776 | |
| 1777 | /// Returns whether the specified two parameters are deemed similarly used |
| 1778 | /// or related by the heuristics. |
| 1779 | bool operator()(const ParmVarDecl *Param1, const ParmVarDecl *Param2) const { |
| 1780 | if (!Enabled) |
| 1781 | return false; |
| 1782 | |
| 1783 | LLVM_DEBUG(llvm::dbgs() |
| 1784 | << "::: Matching similar usage / relatedness heuristic...\n" ); |
| 1785 | |
| 1786 | if (SameExpr(Param1, Param2)) { |
| 1787 | LLVM_DEBUG(llvm::dbgs() << "::: Used in the same expression.\n" ); |
| 1788 | return true; |
| 1789 | } |
| 1790 | |
| 1791 | if (PassToFun(Param1, Param2)) { |
| 1792 | LLVM_DEBUG(llvm::dbgs() |
| 1793 | << "::: Passed to same function in different calls.\n" ); |
| 1794 | return true; |
| 1795 | } |
| 1796 | |
| 1797 | if (SameMember(Param1, Param2)) { |
| 1798 | LLVM_DEBUG(llvm::dbgs() |
| 1799 | << "::: Same member field access or method called.\n" ); |
| 1800 | return true; |
| 1801 | } |
| 1802 | |
| 1803 | if (Returns(Param1, Param2)) { |
| 1804 | LLVM_DEBUG(llvm::dbgs() << "::: Both parameter returned.\n" ); |
| 1805 | return true; |
| 1806 | } |
| 1807 | |
| 1808 | LLVM_DEBUG(llvm::dbgs() << "::: None.\n" ); |
| 1809 | return false; |
| 1810 | } |
| 1811 | }; |
| 1812 | |
| 1813 | // (This function hoists the call to operator() of the wrapper, so we do not |
| 1814 | // need to define the previous class at the top of the file.) |
| 1815 | static inline bool |
| 1816 | isSimilarlyUsedParameter(const SimilarlyUsedParameterPairSuppressor &Suppressor, |
| 1817 | const ParmVarDecl *Param1, const ParmVarDecl *Param2) { |
| 1818 | return Suppressor(Param1, Param2); |
| 1819 | } |
| 1820 | |
| 1821 | static void padStringAtEnd(SmallVectorImpl<char> &Str, std::size_t ToLen) { |
| 1822 | while (Str.size() < ToLen) |
| 1823 | Str.emplace_back(Args: '\0'); |
| 1824 | } |
| 1825 | |
| 1826 | static void padStringAtBegin(SmallVectorImpl<char> &Str, std::size_t ToLen) { |
| 1827 | while (Str.size() < ToLen) |
| 1828 | Str.insert(I: Str.begin(), Elt: '\0'); |
| 1829 | } |
| 1830 | |
| 1831 | static bool isCommonPrefixWithoutSomeCharacters(std::size_t N, StringRef S1, |
| 1832 | StringRef S2) { |
| 1833 | assert(S1.size() >= N && S2.size() >= N); |
| 1834 | StringRef S1Prefix = S1.take_front(N: S1.size() - N), |
| 1835 | S2Prefix = S2.take_front(N: S2.size() - N); |
| 1836 | return S1Prefix == S2Prefix && !S1Prefix.empty(); |
| 1837 | } |
| 1838 | |
| 1839 | static bool isCommonSuffixWithoutSomeCharacters(std::size_t N, StringRef S1, |
| 1840 | StringRef S2) { |
| 1841 | assert(S1.size() >= N && S2.size() >= N); |
| 1842 | StringRef S1Suffix = S1.take_back(N: S1.size() - N), |
| 1843 | S2Suffix = S2.take_back(N: S2.size() - N); |
| 1844 | return S1Suffix == S2Suffix && !S1Suffix.empty(); |
| 1845 | } |
| 1846 | |
| 1847 | /// Returns whether the two strings are prefixes or suffixes of each other with |
| 1848 | /// at most Threshold characters differing on the non-common end. |
| 1849 | static bool prefixSuffixCoverUnderThreshold(std::size_t Threshold, |
| 1850 | StringRef Str1, StringRef Str2) { |
| 1851 | if (Threshold == 0) |
| 1852 | return false; |
| 1853 | |
| 1854 | // Pad the two strings to the longer length. |
| 1855 | std::size_t BiggerLength = std::max(a: Str1.size(), b: Str2.size()); |
| 1856 | |
| 1857 | if (BiggerLength <= Threshold) |
| 1858 | // If the length of the strings is still smaller than the threshold, they |
| 1859 | // would be covered by an empty prefix/suffix with the rest differing. |
| 1860 | // (E.g. "A" and "X" with Threshold = 1 would mean we think they are |
| 1861 | // similar and do not warn about them, which is a too eager assumption.) |
| 1862 | return false; |
| 1863 | |
| 1864 | SmallString<32> S1PadE{Str1}, S2PadE{Str2}; |
| 1865 | padStringAtEnd(Str&: S1PadE, ToLen: BiggerLength); |
| 1866 | padStringAtEnd(Str&: S2PadE, ToLen: BiggerLength); |
| 1867 | |
| 1868 | if (isCommonPrefixWithoutSomeCharacters( |
| 1869 | N: Threshold, S1: StringRef{S1PadE.begin(), BiggerLength}, |
| 1870 | S2: StringRef{S2PadE.begin(), BiggerLength})) |
| 1871 | return true; |
| 1872 | |
| 1873 | SmallString<32> S1PadB{Str1}, S2PadB{Str2}; |
| 1874 | padStringAtBegin(Str&: S1PadB, ToLen: BiggerLength); |
| 1875 | padStringAtBegin(Str&: S2PadB, ToLen: BiggerLength); |
| 1876 | |
| 1877 | if (isCommonSuffixWithoutSomeCharacters( |
| 1878 | N: Threshold, S1: StringRef{S1PadB.begin(), BiggerLength}, |
| 1879 | S2: StringRef{S2PadB.begin(), BiggerLength})) |
| 1880 | return true; |
| 1881 | |
| 1882 | return false; |
| 1883 | } |
| 1884 | |
| 1885 | } // namespace filter |
| 1886 | |
| 1887 | namespace { |
| 1888 | |
| 1889 | /// Matches functions that have at least the specified amount of parameters. |
| 1890 | AST_MATCHER_P(FunctionDecl, parameterCountGE, unsigned, N) { |
| 1891 | return Node.getNumParams() >= N; |
| 1892 | } |
| 1893 | |
| 1894 | /// Matches *any* overloaded unary and binary operators. |
| 1895 | AST_MATCHER(FunctionDecl, isOverloadedUnaryOrBinaryOperator) { |
| 1896 | switch (Node.getOverloadedOperator()) { |
| 1897 | case OO_None: |
| 1898 | case OO_New: |
| 1899 | case OO_Delete: |
| 1900 | case OO_Array_New: |
| 1901 | case OO_Array_Delete: |
| 1902 | case OO_Conditional: |
| 1903 | case OO_Coawait: |
| 1904 | return false; |
| 1905 | |
| 1906 | default: |
| 1907 | return Node.getNumParams() <= 2; |
| 1908 | } |
| 1909 | } |
| 1910 | |
| 1911 | } // namespace |
| 1912 | |
| 1913 | /// Returns the DefaultMinimumLength if the Value of requested minimum length |
| 1914 | /// is less than 2. Minimum lengths of 0 or 1 are not accepted. |
| 1915 | static inline unsigned clampMinimumLength(const unsigned Value) { |
| 1916 | return Value < 2 ? DefaultMinimumLength : Value; |
| 1917 | } |
| 1918 | |
| 1919 | // FIXME: Maybe unneeded, getNameForDiagnostic() is expected to change to return |
| 1920 | // a crafted location when the node itself is unnamed. (See D84658, D85033.) |
| 1921 | /// Returns the diagnostic-friendly name of the node, or empty string. |
| 1922 | static SmallString<64> getName(const NamedDecl *ND) { |
| 1923 | SmallString<64> Name; |
| 1924 | llvm::raw_svector_ostream OS{Name}; |
| 1925 | ND->getNameForDiagnostic(OS, Policy: ND->getASTContext().getPrintingPolicy(), Qualified: false); |
| 1926 | return Name; |
| 1927 | } |
| 1928 | |
| 1929 | /// Returns the diagnostic-friendly name of the node, or a constant value. |
| 1930 | static SmallString<64> getNameOrUnnamed(const NamedDecl *ND) { |
| 1931 | auto Name = getName(ND); |
| 1932 | if (Name.empty()) |
| 1933 | Name = "<unnamed>" ; |
| 1934 | return Name; |
| 1935 | } |
| 1936 | |
| 1937 | /// Returns whether a particular Mix between two parameters should have the |
| 1938 | /// types involved diagnosed to the user. This is only a flag check. |
| 1939 | static inline bool needsToPrintTypeInDiagnostic(const model::Mix &M) { |
| 1940 | using namespace model; |
| 1941 | return static_cast<bool>( |
| 1942 | M.flags() & |
| 1943 | (MixFlags::TypeAlias | MixFlags::ReferenceBind | MixFlags::Qualifiers)); |
| 1944 | } |
| 1945 | |
| 1946 | /// Returns whether a particular Mix between the two parameters should have |
| 1947 | /// implicit conversions elaborated. |
| 1948 | static inline bool needsToElaborateImplicitConversion(const model::Mix &M) { |
| 1949 | return hasFlag(Data: M.flags(), SearchedFlag: model::MixFlags::ImplicitConversion); |
| 1950 | } |
| 1951 | |
| 1952 | namespace { |
| 1953 | |
| 1954 | /// This class formats a conversion sequence into a "Ty1 -> Ty2 -> Ty3" line |
| 1955 | /// that can be used in diagnostics. |
| 1956 | struct FormattedConversionSequence { |
| 1957 | std::string DiagnosticText; |
| 1958 | |
| 1959 | /// The formatted sequence is trivial if it is "Ty1 -> Ty2", but Ty1 and |
| 1960 | /// Ty2 are the types that are shown in the code. A trivial diagnostic |
| 1961 | /// does not need to be printed. |
| 1962 | bool Trivial = true; |
| 1963 | |
| 1964 | FormattedConversionSequence(const PrintingPolicy &PP, |
| 1965 | StringRef StartTypeAsDiagnosed, |
| 1966 | const model::ConversionSequence &Conv, |
| 1967 | StringRef DestinationTypeAsDiagnosed) { |
| 1968 | llvm::raw_string_ostream OS{DiagnosticText}; |
| 1969 | |
| 1970 | // Print the type name as it is printed in other places in the diagnostic. |
| 1971 | OS << '\'' << StartTypeAsDiagnosed << '\''; |
| 1972 | std::string LastAddedType = StartTypeAsDiagnosed.str(); |
| 1973 | std::size_t NumElementsAdded = 1; |
| 1974 | |
| 1975 | // However, the parameter's defined type might not be what the implicit |
| 1976 | // conversion started with, e.g. if a typedef is found to convert. |
| 1977 | std::string SeqBeginTypeStr = Conv.Begin.getAsString(PP); |
| 1978 | std::string SeqEndTypeStr = Conv.End.getAsString(PP); |
| 1979 | if (StartTypeAsDiagnosed != SeqBeginTypeStr) { |
| 1980 | OS << " (as '" << SeqBeginTypeStr << "')" ; |
| 1981 | LastAddedType = SeqBeginTypeStr; |
| 1982 | Trivial = false; |
| 1983 | } |
| 1984 | |
| 1985 | auto AddType = [&](StringRef ToAdd) { |
| 1986 | if (LastAddedType != ToAdd && ToAdd != SeqEndTypeStr) { |
| 1987 | OS << " -> '" << ToAdd << "'" ; |
| 1988 | LastAddedType = ToAdd.str(); |
| 1989 | ++NumElementsAdded; |
| 1990 | } |
| 1991 | }; |
| 1992 | for (QualType InvolvedType : Conv.getInvolvedTypesInSequence()) |
| 1993 | // Print every type that's unique in the sequence into the diagnosis. |
| 1994 | AddType(InvolvedType.getAsString(Policy: PP)); |
| 1995 | |
| 1996 | if (LastAddedType != DestinationTypeAsDiagnosed) { |
| 1997 | OS << " -> '" << DestinationTypeAsDiagnosed << "'" ; |
| 1998 | LastAddedType = DestinationTypeAsDiagnosed.str(); |
| 1999 | ++NumElementsAdded; |
| 2000 | } |
| 2001 | |
| 2002 | // Same reasoning as with the Begin, e.g. if the converted-to type is a |
| 2003 | // typedef, it will not be the same inside the conversion sequence (where |
| 2004 | // the model already tore off typedefs) as in the code. |
| 2005 | if (DestinationTypeAsDiagnosed != SeqEndTypeStr) { |
| 2006 | OS << " (as '" << SeqEndTypeStr << "')" ; |
| 2007 | LastAddedType = SeqEndTypeStr; |
| 2008 | Trivial = false; |
| 2009 | } |
| 2010 | |
| 2011 | if (Trivial && NumElementsAdded > 2) |
| 2012 | // If the thing is still marked trivial but we have more than the |
| 2013 | // from and to types added, it should not be trivial, and elaborated |
| 2014 | // when printing the diagnostic. |
| 2015 | Trivial = false; |
| 2016 | } |
| 2017 | }; |
| 2018 | |
| 2019 | /// Retains the elements called with and returns whether the call is done with |
| 2020 | /// a new element. |
| 2021 | template <typename E, std::size_t N> class InsertOnce { |
| 2022 | llvm::SmallSet<E, N> CalledWith; |
| 2023 | |
| 2024 | public: |
| 2025 | bool operator()(E El) { return CalledWith.insert(std::move(El)).second; } |
| 2026 | |
| 2027 | bool calledWith(const E &El) const { return CalledWith.contains(El); } |
| 2028 | }; |
| 2029 | |
| 2030 | struct SwappedEqualQualTypePair { |
| 2031 | QualType LHSType, RHSType; |
| 2032 | |
| 2033 | bool operator==(const SwappedEqualQualTypePair &Other) const { |
| 2034 | return (LHSType == Other.LHSType && RHSType == Other.RHSType) || |
| 2035 | (LHSType == Other.RHSType && RHSType == Other.LHSType); |
| 2036 | } |
| 2037 | |
| 2038 | bool operator<(const SwappedEqualQualTypePair &Other) const { |
| 2039 | return LHSType < Other.LHSType && RHSType < Other.RHSType; |
| 2040 | } |
| 2041 | }; |
| 2042 | |
| 2043 | struct TypeAliasDiagnosticTuple { |
| 2044 | QualType LHSType, RHSType, CommonType; |
| 2045 | |
| 2046 | bool operator==(const TypeAliasDiagnosticTuple &Other) const { |
| 2047 | return CommonType == Other.CommonType && |
| 2048 | ((LHSType == Other.LHSType && RHSType == Other.RHSType) || |
| 2049 | (LHSType == Other.RHSType && RHSType == Other.LHSType)); |
| 2050 | } |
| 2051 | |
| 2052 | bool operator<(const TypeAliasDiagnosticTuple &Other) const { |
| 2053 | return CommonType < Other.CommonType && LHSType < Other.LHSType && |
| 2054 | RHSType < Other.RHSType; |
| 2055 | } |
| 2056 | }; |
| 2057 | |
| 2058 | /// Helper class to only emit a diagnostic related to MixFlags::TypeAlias once. |
| 2059 | class UniqueTypeAliasDiagnosticHelper |
| 2060 | : public InsertOnce<TypeAliasDiagnosticTuple, 8> { |
| 2061 | using Base = InsertOnce<TypeAliasDiagnosticTuple, 8>; |
| 2062 | |
| 2063 | public: |
| 2064 | /// Returns whether the diagnostic for LHSType and RHSType which are both |
| 2065 | /// referring to CommonType being the same has not been emitted already. |
| 2066 | bool operator()(QualType LHSType, QualType RHSType, QualType CommonType) { |
| 2067 | if (CommonType.isNull() || CommonType == LHSType || CommonType == RHSType) |
| 2068 | return Base::operator()({LHSType, RHSType, {}}); |
| 2069 | |
| 2070 | TypeAliasDiagnosticTuple ThreeTuple{LHSType, RHSType, CommonType}; |
| 2071 | if (!Base::operator()(El: ThreeTuple)) |
| 2072 | return false; |
| 2073 | |
| 2074 | bool AlreadySaidLHSAndCommonIsSame = calledWith({LHSType, CommonType, {}}); |
| 2075 | bool AlreadySaidRHSAndCommonIsSame = calledWith({RHSType, CommonType, {}}); |
| 2076 | if (AlreadySaidLHSAndCommonIsSame && AlreadySaidRHSAndCommonIsSame) { |
| 2077 | // "SomeInt == int" && "SomeOtherInt == int" => "Common(SomeInt, |
| 2078 | // SomeOtherInt) == int", no need to diagnose it. Save the 3-tuple only |
| 2079 | // for shortcut if it ever appears again. |
| 2080 | return false; |
| 2081 | } |
| 2082 | |
| 2083 | return true; |
| 2084 | } |
| 2085 | }; |
| 2086 | |
| 2087 | } // namespace |
| 2088 | |
| 2089 | EasilySwappableParametersCheck::EasilySwappableParametersCheck( |
| 2090 | StringRef Name, ClangTidyContext *Context) |
| 2091 | : ClangTidyCheck(Name, Context), |
| 2092 | MinimumLength(clampMinimumLength( |
| 2093 | Value: Options.get(LocalName: "MinimumLength" , Default: DefaultMinimumLength))), |
| 2094 | IgnoredParameterNames(optutils::parseStringList( |
| 2095 | Option: Options.get(LocalName: "IgnoredParameterNames" , Default: DefaultIgnoredParameterNames))), |
| 2096 | IgnoredParameterTypeSuffixes(optutils::parseStringList( |
| 2097 | Option: Options.get(LocalName: "IgnoredParameterTypeSuffixes" , |
| 2098 | Default: DefaultIgnoredParameterTypeSuffixes))), |
| 2099 | QualifiersMix(Options.get(LocalName: "QualifiersMix" , Default: DefaultQualifiersMix)), |
| 2100 | ModelImplicitConversions(Options.get(LocalName: "ModelImplicitConversions" , |
| 2101 | Default: DefaultModelImplicitConversions)), |
| 2102 | SuppressParametersUsedTogether( |
| 2103 | Options.get(LocalName: "SuppressParametersUsedTogether" , |
| 2104 | Default: DefaultSuppressParametersUsedTogether)), |
| 2105 | NamePrefixSuffixSilenceDissimilarityTreshold( |
| 2106 | Options.get(LocalName: "NamePrefixSuffixSilenceDissimilarityTreshold" , |
| 2107 | Default: DefaultNamePrefixSuffixSilenceDissimilarityTreshold)) {} |
| 2108 | |
| 2109 | void EasilySwappableParametersCheck::storeOptions( |
| 2110 | ClangTidyOptions::OptionMap &Opts) { |
| 2111 | Options.store(Options&: Opts, LocalName: "MinimumLength" , Value: MinimumLength); |
| 2112 | Options.store(Options&: Opts, LocalName: "IgnoredParameterNames" , |
| 2113 | Value: optutils::serializeStringList(Strings: IgnoredParameterNames)); |
| 2114 | Options.store(Options&: Opts, LocalName: "IgnoredParameterTypeSuffixes" , |
| 2115 | Value: optutils::serializeStringList(Strings: IgnoredParameterTypeSuffixes)); |
| 2116 | Options.store(Options&: Opts, LocalName: "QualifiersMix" , Value: QualifiersMix); |
| 2117 | Options.store(Options&: Opts, LocalName: "ModelImplicitConversions" , Value: ModelImplicitConversions); |
| 2118 | Options.store(Options&: Opts, LocalName: "SuppressParametersUsedTogether" , |
| 2119 | Value: SuppressParametersUsedTogether); |
| 2120 | Options.store(Options&: Opts, LocalName: "NamePrefixSuffixSilenceDissimilarityTreshold" , |
| 2121 | Value: NamePrefixSuffixSilenceDissimilarityTreshold); |
| 2122 | } |
| 2123 | |
| 2124 | void EasilySwappableParametersCheck::registerMatchers(MatchFinder *Finder) { |
| 2125 | const auto BaseConstraints = functionDecl( |
| 2126 | // Only report for definition nodes, as fixing the issues reported |
| 2127 | // requires the user to be able to change code. |
| 2128 | isDefinition(), parameterCountGE(N: MinimumLength), |
| 2129 | unless(isOverloadedUnaryOrBinaryOperator())); |
| 2130 | |
| 2131 | Finder->addMatcher( |
| 2132 | NodeMatch: functionDecl(BaseConstraints, |
| 2133 | unless(ast_matchers::isTemplateInstantiation())) |
| 2134 | .bind(ID: "func" ), |
| 2135 | Action: this); |
| 2136 | Finder->addMatcher( |
| 2137 | NodeMatch: functionDecl(BaseConstraints, isExplicitTemplateSpecialization()) |
| 2138 | .bind(ID: "func" ), |
| 2139 | Action: this); |
| 2140 | } |
| 2141 | |
| 2142 | void EasilySwappableParametersCheck::check( |
| 2143 | const MatchFinder::MatchResult &Result) { |
| 2144 | using namespace model; |
| 2145 | using namespace filter; |
| 2146 | |
| 2147 | const auto *FD = Result.Nodes.getNodeAs<FunctionDecl>(ID: "func" ); |
| 2148 | assert(FD); |
| 2149 | |
| 2150 | const PrintingPolicy &PP = FD->getASTContext().getPrintingPolicy(); |
| 2151 | std::size_t NumParams = FD->getNumParams(); |
| 2152 | std::size_t MixableRangeStartIndex = 0; |
| 2153 | |
| 2154 | // Spawn one suppressor and if the user requested, gather information from |
| 2155 | // the AST for the parameters' usages. |
| 2156 | filter::SimilarlyUsedParameterPairSuppressor UsageBasedSuppressor{ |
| 2157 | FD, SuppressParametersUsedTogether}; |
| 2158 | |
| 2159 | LLVM_DEBUG(llvm::dbgs() << "Begin analysis of " << getName(FD) << " with " |
| 2160 | << NumParams << " parameters...\n" ); |
| 2161 | while (MixableRangeStartIndex < NumParams) { |
| 2162 | if (isIgnoredParameter(Check: *this, Node: FD->getParamDecl(i: MixableRangeStartIndex))) { |
| 2163 | LLVM_DEBUG(llvm::dbgs() |
| 2164 | << "Parameter #" << MixableRangeStartIndex << " ignored.\n" ); |
| 2165 | ++MixableRangeStartIndex; |
| 2166 | continue; |
| 2167 | } |
| 2168 | |
| 2169 | MixableParameterRange R = modelMixingRange( |
| 2170 | Check: *this, FD, StartIndex: MixableRangeStartIndex, UsageBasedSuppressor); |
| 2171 | assert(R.NumParamsChecked > 0 && "Ensure forward progress!" ); |
| 2172 | MixableRangeStartIndex += R.NumParamsChecked; |
| 2173 | if (R.NumParamsChecked < MinimumLength) { |
| 2174 | LLVM_DEBUG(llvm::dbgs() << "Ignoring range of " << R.NumParamsChecked |
| 2175 | << " lower than limit.\n" ); |
| 2176 | continue; |
| 2177 | } |
| 2178 | |
| 2179 | bool NeedsAnyTypeNote = llvm::any_of(Range&: R.Mixes, P: needsToPrintTypeInDiagnostic); |
| 2180 | bool HasAnyImplicits = |
| 2181 | llvm::any_of(Range&: R.Mixes, P: needsToElaborateImplicitConversion); |
| 2182 | const ParmVarDecl *First = R.getFirstParam(), *Last = R.getLastParam(); |
| 2183 | std::string FirstParamTypeAsWritten = First->getType().getAsString(PP); |
| 2184 | { |
| 2185 | StringRef DiagText; |
| 2186 | |
| 2187 | if (HasAnyImplicits) |
| 2188 | DiagText = "%0 adjacent parameters of %1 of convertible types are " |
| 2189 | "easily swapped by mistake" ; |
| 2190 | else if (NeedsAnyTypeNote) |
| 2191 | DiagText = "%0 adjacent parameters of %1 of similar type are easily " |
| 2192 | "swapped by mistake" ; |
| 2193 | else |
| 2194 | DiagText = "%0 adjacent parameters of %1 of similar type ('%2') are " |
| 2195 | "easily swapped by mistake" ; |
| 2196 | |
| 2197 | auto Diag = diag(First->getOuterLocStart(), DiagText) |
| 2198 | << static_cast<unsigned>(R.NumParamsChecked) << FD; |
| 2199 | if (!NeedsAnyTypeNote) |
| 2200 | Diag << FirstParamTypeAsWritten; |
| 2201 | |
| 2202 | CharSourceRange HighlightRange = CharSourceRange::getTokenRange( |
| 2203 | First->getBeginLoc(), Last->getEndLoc()); |
| 2204 | Diag << HighlightRange; |
| 2205 | } |
| 2206 | |
| 2207 | // There is a chance that the previous highlight did not succeed, e.g. when |
| 2208 | // the two parameters are on different lines. For clarity, show the user |
| 2209 | // the involved variable explicitly. |
| 2210 | diag(First->getLocation(), "the first parameter in the range is '%0'" , |
| 2211 | DiagnosticIDs::Note) |
| 2212 | << getNameOrUnnamed(First) |
| 2213 | << CharSourceRange::getTokenRange(First->getLocation(), |
| 2214 | First->getLocation()); |
| 2215 | diag(Last->getLocation(), "the last parameter in the range is '%0'" , |
| 2216 | DiagnosticIDs::Note) |
| 2217 | << getNameOrUnnamed(Last) |
| 2218 | << CharSourceRange::getTokenRange(Last->getLocation(), |
| 2219 | Last->getLocation()); |
| 2220 | |
| 2221 | // Helper classes to silence elaborative diagnostic notes that would be |
| 2222 | // too verbose. |
| 2223 | UniqueTypeAliasDiagnosticHelper UniqueTypeAlias; |
| 2224 | InsertOnce<SwappedEqualQualTypePair, 8> UniqueBindPower; |
| 2225 | InsertOnce<SwappedEqualQualTypePair, 8> UniqueImplicitConversion; |
| 2226 | |
| 2227 | for (const model::Mix &M : R.Mixes) { |
| 2228 | assert(M.mixable() && "Sentinel or false mix in result." ); |
| 2229 | if (!needsToPrintTypeInDiagnostic(M) && |
| 2230 | !needsToElaborateImplicitConversion(M)) |
| 2231 | continue; |
| 2232 | |
| 2233 | // Typedefs might result in the type of the variable needing to be |
| 2234 | // emitted to a note diagnostic, so prepare it. |
| 2235 | const ParmVarDecl *LVar = M.First; |
| 2236 | const ParmVarDecl *RVar = M.Second; |
| 2237 | QualType LType = LVar->getType(); |
| 2238 | QualType RType = RVar->getType(); |
| 2239 | QualType CommonType = M.commonUnderlyingType(); |
| 2240 | std::string LTypeStr = LType.getAsString(Policy: PP); |
| 2241 | std::string RTypeStr = RType.getAsString(Policy: PP); |
| 2242 | std::string CommonTypeStr = CommonType.getAsString(Policy: PP); |
| 2243 | |
| 2244 | if (hasFlag(Data: M.flags(), SearchedFlag: MixFlags::TypeAlias) && |
| 2245 | UniqueTypeAlias(LType, RType, CommonType)) { |
| 2246 | StringRef DiagText; |
| 2247 | bool ExplicitlyPrintCommonType = false; |
| 2248 | if (LTypeStr == CommonTypeStr || RTypeStr == CommonTypeStr) { |
| 2249 | if (hasFlag(Data: M.flags(), SearchedFlag: MixFlags::Qualifiers)) |
| 2250 | DiagText = "after resolving type aliases, '%0' and '%1' share a " |
| 2251 | "common type" ; |
| 2252 | else |
| 2253 | DiagText = |
| 2254 | "after resolving type aliases, '%0' and '%1' are the same" ; |
| 2255 | } else if (!CommonType.isNull()) { |
| 2256 | DiagText = "after resolving type aliases, the common type of '%0' " |
| 2257 | "and '%1' is '%2'" ; |
| 2258 | ExplicitlyPrintCommonType = true; |
| 2259 | } |
| 2260 | |
| 2261 | auto Diag = |
| 2262 | diag(LVar->getOuterLocStart(), DiagText, DiagnosticIDs::Note) |
| 2263 | << LTypeStr << RTypeStr; |
| 2264 | if (ExplicitlyPrintCommonType) |
| 2265 | Diag << CommonTypeStr; |
| 2266 | } |
| 2267 | |
| 2268 | if ((hasFlag(Data: M.flags(), SearchedFlag: MixFlags::ReferenceBind) || |
| 2269 | hasFlag(Data: M.flags(), SearchedFlag: MixFlags::Qualifiers)) && |
| 2270 | UniqueBindPower({LType, RType})) { |
| 2271 | StringRef DiagText = "'%0' and '%1' parameters accept and bind the " |
| 2272 | "same kind of values" ; |
| 2273 | diag(RVar->getOuterLocStart(), DiagText, DiagnosticIDs::Note) |
| 2274 | << LTypeStr << RTypeStr; |
| 2275 | } |
| 2276 | |
| 2277 | if (needsToElaborateImplicitConversion(M) && |
| 2278 | UniqueImplicitConversion({LType, RType})) { |
| 2279 | const model::ConversionSequence <R = |
| 2280 | M.leftToRightConversionSequence(); |
| 2281 | const model::ConversionSequence &RTL = |
| 2282 | M.rightToLeftConversionSequence(); |
| 2283 | FormattedConversionSequence LTRFmt{PP, LTypeStr, LTR, RTypeStr}; |
| 2284 | FormattedConversionSequence RTLFmt{PP, RTypeStr, RTL, LTypeStr}; |
| 2285 | |
| 2286 | StringRef DiagText = "'%0' and '%1' may be implicitly converted" ; |
| 2287 | if (!LTRFmt.Trivial || !RTLFmt.Trivial) |
| 2288 | DiagText = "'%0' and '%1' may be implicitly converted: %2, %3" ; |
| 2289 | |
| 2290 | { |
| 2291 | auto Diag = |
| 2292 | diag(RVar->getOuterLocStart(), DiagText, DiagnosticIDs::Note) |
| 2293 | << LTypeStr << RTypeStr; |
| 2294 | |
| 2295 | if (!LTRFmt.Trivial || !RTLFmt.Trivial) |
| 2296 | Diag << LTRFmt.DiagnosticText << RTLFmt.DiagnosticText; |
| 2297 | } |
| 2298 | |
| 2299 | StringRef ConversionFunctionDiagText = |
| 2300 | "the implicit conversion involves the " |
| 2301 | "%select{|converting constructor|conversion operator}0 " |
| 2302 | "declared here" ; |
| 2303 | if (const FunctionDecl *LFD = LTR.getUserDefinedConversionFunction()) |
| 2304 | diag(LFD->getLocation(), ConversionFunctionDiagText, |
| 2305 | DiagnosticIDs::Note) |
| 2306 | << static_cast<unsigned>(LTR.UDConvKind) |
| 2307 | << LTR.getUserDefinedConversionHighlight(); |
| 2308 | if (const FunctionDecl *RFD = RTL.getUserDefinedConversionFunction()) |
| 2309 | diag(RFD->getLocation(), ConversionFunctionDiagText, |
| 2310 | DiagnosticIDs::Note) |
| 2311 | << static_cast<unsigned>(RTL.UDConvKind) |
| 2312 | << RTL.getUserDefinedConversionHighlight(); |
| 2313 | } |
| 2314 | } |
| 2315 | } |
| 2316 | } |
| 2317 | |
| 2318 | } // namespace clang::tidy::bugprone |
| 2319 | |