| File: | build/source/clang/include/clang/Basic/Diagnostic.h |
| Warning: | line 1198, column 5 Use of memory after it is freed |
Press '?' to see keyboard shortcuts
Keyboard shortcuts:
| 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" "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)LLVM_BITMASK_LARGEST_ENUMERATOR = ImplicitConversion | ||||
| 131 | }; | ||||
| 132 | LLVM_ENABLE_BITMASK_ENUMS_IN_NAMESPACE()using ::llvm::BitmaskEnumDetail::operator~; using ::llvm::BitmaskEnumDetail ::operator|; using ::llvm::BitmaskEnumDetail::operator&; using ::llvm::BitmaskEnumDetail::operator^; using ::llvm::BitmaskEnumDetail ::operator|=; using ::llvm::BitmaskEnumDetail::operator&= ; using ::llvm::BitmaskEnumDetail::operator^=; | ||||
| 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 &&(static_cast <bool> (SearchedFlag != MixFlags::Invalid && "can't be used to detect lack of all bits!") ? void (0) : __assert_fail ("SearchedFlag != MixFlags::Invalid && \"can't be used to detect lack of all bits!\"" , "clang-tools-extra/clang-tidy/bugprone/EasilySwappableParametersCheck.cpp" , 137, __extension__ __PRETTY_FUNCTION__)) | ||||
| 137 | "can't be used to detect lack of all bits!")(static_cast <bool> (SearchedFlag != MixFlags::Invalid && "can't be used to detect lack of all bits!") ? void (0) : __assert_fail ("SearchedFlag != MixFlags::Invalid && \"can't be used to detect lack of all bits!\"" , "clang-tools-extra/clang-tidy/bugprone/EasilySwappableParametersCheck.cpp" , 137, __extension__ __PRETTY_FUNCTION__)); | ||||
| 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(F, 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(F, MixFlags::Trivial)) | ||||
| 162 | Str[1] = 'T'; | ||||
| 163 | if (hasFlag(F, MixFlags::Canonical)) | ||||
| 164 | Str[2] = 'C'; | ||||
| 165 | if (hasFlag(F, MixFlags::TypeAlias)) | ||||
| 166 | Str[3] = 't'; | ||||
| 167 | if (hasFlag(F, MixFlags::ReferenceBind)) | ||||
| 168 | Str[4] = '&'; | ||||
| 169 | if (hasFlag(F, MixFlags::Qualifiers)) | ||||
| 170 | Str[5] = 'Q'; | ||||
| 171 | if (hasFlag(F, MixFlags::ImplicitConversion)) | ||||
| 172 | Str[6] = 'i'; | ||||
| 173 | |||||
| 174 | if (hasFlag(F, MixFlags::WorkaroundDisableCanonicalEquivalence)) | ||||
| 175 | Str.append("(~C)"); | ||||
| 176 | |||||
| 177 | return Str.str().str(); | ||||
| 178 | } | ||||
| 179 | |||||
| 180 | #endif // 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(QT); | ||||
| 279 | else if (Ret.back() != QT) | ||||
| 280 | Ret.emplace_back(QT); | ||||
| 281 | }; | ||||
| 282 | |||||
| 283 | EmplaceIfDifferent(AfterFirstStandard); | ||||
| 284 | switch (UDConvKind) { | ||||
| 285 | case UDCK_Ctor: | ||||
| 286 | EmplaceIfDifferent(UDConvCtor.ConstructorParameterType); | ||||
| 287 | EmplaceIfDifferent(UDConvCtor.UserDefinedType); | ||||
| 288 | break; | ||||
| 289 | case UDCK_Oper: | ||||
| 290 | EmplaceIfDifferent(UDConvOp.UserDefinedType); | ||||
| 291 | EmplaceIfDifferent(UDConvOp.ConversionOperatorResultType); | ||||
| 292 | break; | ||||
| 293 | case UDCK_None: | ||||
| 294 | break; | ||||
| 295 | } | ||||
| 296 | EmplaceIfDifferent(AfterSecondStandard); | ||||
| 297 | |||||
| 298 | return Ret; | ||||
| 299 | } | ||||
| 300 | |||||
| 301 | /// Updates the steps of the conversion sequence with the steps from 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.")::llvm::llvm_unreachable_internal("Invalid UDConv kind.", "clang-tools-extra/clang-tidy/bugprone/EasilySwappableParametersCheck.cpp" , 350); | ||||
| 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.")::llvm::llvm_unreachable_internal("Invalid UDConv kind.", "clang-tools-extra/clang-tidy/bugprone/EasilySwappableParametersCheck.cpp" , 362); | ||||
| 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.")::llvm::llvm_unreachable_internal("Invalid UDConv kind.", "clang-tools-extra/clang-tidy/bugprone/EasilySwappableParametersCheck.cpp" , 383); | ||||
| 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")(static_cast <bool> (Flags != MixFlags::Invalid && "sanitize() called on invalid bitvec") ? void (0) : __assert_fail ("Flags != MixFlags::Invalid && \"sanitize() called on invalid bitvec\"" , "clang-tools-extra/clang-tidy/bugprone/EasilySwappableParametersCheck.cpp" , 418, __extension__ __PRETTY_FUNCTION__)); | ||||
| 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(Flags, 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 <class F> MixData withCommonTypeTransformed(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<Mix>::value && | ||||
| 522 | std::is_trivially_move_constructible<Mix>::value && | ||||
| 523 | std::is_trivially_move_assignable<Mix>::value, | ||||
| 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())(static_cast <bool> (!Mixes.empty()) ? void (0) : __assert_fail ("!Mixes.empty()", "clang-tools-extra/clang-tidy/bugprone/EasilySwappableParametersCheck.cpp" , 539, __extension__ __PRETTY_FUNCTION__)); | ||||
| 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())(static_cast <bool> (!Mixes.empty()) ? void (0) : __assert_fail ("!Mixes.empty()", "clang-tools-extra/clang-tidy/bugprone/EasilySwappableParametersCheck.cpp" , 549, __extension__ __PRETTY_FUNCTION__)); | ||||
| 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>(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";do { if (::llvm::DebugFlag && ::llvm::isCurrentDebugType ("EasilySwappableParametersCheck")) { llvm::dbgs() << ">>> getNonCVRQualifiers for LType:\n" ; LType.dump(llvm::dbgs(), Ctx); llvm::dbgs() << "\nand RType:\n" ; RType.dump(llvm::dbgs(), Ctx); llvm::dbgs() << '\n';; } } while (false) | ||||
| 605 | LType.dump(llvm::dbgs(), Ctx); llvm::dbgs() << "\nand RType:\n";do { if (::llvm::DebugFlag && ::llvm::isCurrentDebugType ("EasilySwappableParametersCheck")) { llvm::dbgs() << ">>> getNonCVRQualifiers for LType:\n" ; LType.dump(llvm::dbgs(), Ctx); llvm::dbgs() << "\nand RType:\n" ; RType.dump(llvm::dbgs(), Ctx); llvm::dbgs() << '\n';; } } while (false) | ||||
| 606 | RType.dump(llvm::dbgs(), Ctx); llvm::dbgs() << '\n';)do { if (::llvm::DebugFlag && ::llvm::isCurrentDebugType ("EasilySwappableParametersCheck")) { llvm::dbgs() << ">>> getNonCVRQualifiers for LType:\n" ; LType.dump(llvm::dbgs(), Ctx); llvm::dbgs() << "\nand RType:\n" ; RType.dump(llvm::dbgs(), Ctx); llvm::dbgs() << '\n';; } } while (false); | ||||
| 607 | Qualifiers LQual = LType.getLocalQualifiers(), | ||||
| 608 | RQual = RType.getLocalQualifiers(); | ||||
| 609 | |||||
| 610 | // Strip potential CVR. That is handled by the check option QualifiersMix. | ||||
| 611 | LQual.removeCVRQualifiers(); | ||||
| 612 | RQual.removeCVRQualifiers(); | ||||
| 613 | |||||
| 614 | NonCVRQualifiersResult Ret; | ||||
| 615 | Ret.CommonQualifiers = Qualifiers::removeCommonQualifiers(LQual, RQual); | ||||
| 616 | |||||
| 617 | LLVM_DEBUG(llvm::dbgs() << "--- hasNonCVRMixabilityBreakingQualifiers. "do { if (::llvm::DebugFlag && ::llvm::isCurrentDebugType ("EasilySwappableParametersCheck")) { llvm::dbgs() << "--- hasNonCVRMixabilityBreakingQualifiers. " "Removed common qualifiers: "; Ret.CommonQualifiers.print(llvm ::dbgs(), Ctx.getPrintingPolicy()); llvm::dbgs() << "\n\tremaining on LType: " ; LQual.print(llvm::dbgs(), Ctx.getPrintingPolicy()); llvm::dbgs () << "\n\tremaining on RType: "; RQual.print(llvm::dbgs (), Ctx.getPrintingPolicy()); llvm::dbgs() << '\n';; } } while (false) | ||||
| 618 | "Removed common qualifiers: ";do { if (::llvm::DebugFlag && ::llvm::isCurrentDebugType ("EasilySwappableParametersCheck")) { llvm::dbgs() << "--- hasNonCVRMixabilityBreakingQualifiers. " "Removed common qualifiers: "; Ret.CommonQualifiers.print(llvm ::dbgs(), Ctx.getPrintingPolicy()); llvm::dbgs() << "\n\tremaining on LType: " ; LQual.print(llvm::dbgs(), Ctx.getPrintingPolicy()); llvm::dbgs () << "\n\tremaining on RType: "; RQual.print(llvm::dbgs (), Ctx.getPrintingPolicy()); llvm::dbgs() << '\n';; } } while (false) | ||||
| 619 | Ret.CommonQualifiers.print(llvm::dbgs(), Ctx.getPrintingPolicy());do { if (::llvm::DebugFlag && ::llvm::isCurrentDebugType ("EasilySwappableParametersCheck")) { llvm::dbgs() << "--- hasNonCVRMixabilityBreakingQualifiers. " "Removed common qualifiers: "; Ret.CommonQualifiers.print(llvm ::dbgs(), Ctx.getPrintingPolicy()); llvm::dbgs() << "\n\tremaining on LType: " ; LQual.print(llvm::dbgs(), Ctx.getPrintingPolicy()); llvm::dbgs () << "\n\tremaining on RType: "; RQual.print(llvm::dbgs (), Ctx.getPrintingPolicy()); llvm::dbgs() << '\n';; } } while (false) | ||||
| 620 | llvm::dbgs() << "\n\tremaining on LType: ";do { if (::llvm::DebugFlag && ::llvm::isCurrentDebugType ("EasilySwappableParametersCheck")) { llvm::dbgs() << "--- hasNonCVRMixabilityBreakingQualifiers. " "Removed common qualifiers: "; Ret.CommonQualifiers.print(llvm ::dbgs(), Ctx.getPrintingPolicy()); llvm::dbgs() << "\n\tremaining on LType: " ; LQual.print(llvm::dbgs(), Ctx.getPrintingPolicy()); llvm::dbgs () << "\n\tremaining on RType: "; RQual.print(llvm::dbgs (), Ctx.getPrintingPolicy()); llvm::dbgs() << '\n';; } } while (false) | ||||
| 621 | LQual.print(llvm::dbgs(), Ctx.getPrintingPolicy());do { if (::llvm::DebugFlag && ::llvm::isCurrentDebugType ("EasilySwappableParametersCheck")) { llvm::dbgs() << "--- hasNonCVRMixabilityBreakingQualifiers. " "Removed common qualifiers: "; Ret.CommonQualifiers.print(llvm ::dbgs(), Ctx.getPrintingPolicy()); llvm::dbgs() << "\n\tremaining on LType: " ; LQual.print(llvm::dbgs(), Ctx.getPrintingPolicy()); llvm::dbgs () << "\n\tremaining on RType: "; RQual.print(llvm::dbgs (), Ctx.getPrintingPolicy()); llvm::dbgs() << '\n';; } } while (false) | ||||
| 622 | llvm::dbgs() << "\n\tremaining on RType: ";do { if (::llvm::DebugFlag && ::llvm::isCurrentDebugType ("EasilySwappableParametersCheck")) { llvm::dbgs() << "--- hasNonCVRMixabilityBreakingQualifiers. " "Removed common qualifiers: "; Ret.CommonQualifiers.print(llvm ::dbgs(), Ctx.getPrintingPolicy()); llvm::dbgs() << "\n\tremaining on LType: " ; LQual.print(llvm::dbgs(), Ctx.getPrintingPolicy()); llvm::dbgs () << "\n\tremaining on RType: "; RQual.print(llvm::dbgs (), Ctx.getPrintingPolicy()); llvm::dbgs() << '\n';; } } while (false) | ||||
| 623 | RQual.print(llvm::dbgs(), Ctx.getPrintingPolicy());do { if (::llvm::DebugFlag && ::llvm::isCurrentDebugType ("EasilySwappableParametersCheck")) { llvm::dbgs() << "--- hasNonCVRMixabilityBreakingQualifiers. " "Removed common qualifiers: "; Ret.CommonQualifiers.print(llvm ::dbgs(), Ctx.getPrintingPolicy()); llvm::dbgs() << "\n\tremaining on LType: " ; LQual.print(llvm::dbgs(), Ctx.getPrintingPolicy()); llvm::dbgs () << "\n\tremaining on RType: "; RQual.print(llvm::dbgs (), Ctx.getPrintingPolicy()); llvm::dbgs() << '\n';; } } while (false) | ||||
| 624 | llvm::dbgs() << '\n';)do { if (::llvm::DebugFlag && ::llvm::isCurrentDebugType ("EasilySwappableParametersCheck")) { llvm::dbgs() << "--- hasNonCVRMixabilityBreakingQualifiers. " "Removed common qualifiers: "; Ret.CommonQualifiers.print(llvm ::dbgs(), Ctx.getPrintingPolicy()); llvm::dbgs() << "\n\tremaining on LType: " ; LQual.print(llvm::dbgs(), Ctx.getPrintingPolicy()); llvm::dbgs () << "\n\tremaining on RType: "; RQual.print(llvm::dbgs (), Ctx.getPrintingPolicy()); llvm::dbgs() << '\n';; } } while (false); | ||||
| 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";do { if (::llvm::DebugFlag && ::llvm::isCurrentDebugType ("EasilySwappableParametersCheck")) { llvm::dbgs() << ">>> calculateMixability for LType:\n" ; LType.dump(llvm::dbgs(), Ctx); llvm::dbgs() << "\nand RType:\n" ; RType.dump(llvm::dbgs(), Ctx); llvm::dbgs() << '\n';; } } while (false) | ||||
| 648 | LType.dump(llvm::dbgs(), Ctx); llvm::dbgs() << "\nand RType:\n";do { if (::llvm::DebugFlag && ::llvm::isCurrentDebugType ("EasilySwappableParametersCheck")) { llvm::dbgs() << ">>> calculateMixability for LType:\n" ; LType.dump(llvm::dbgs(), Ctx); llvm::dbgs() << "\nand RType:\n" ; RType.dump(llvm::dbgs(), Ctx); llvm::dbgs() << '\n';; } } while (false) | ||||
| 649 | RType.dump(llvm::dbgs(), Ctx); llvm::dbgs() << '\n';)do { if (::llvm::DebugFlag && ::llvm::isCurrentDebugType ("EasilySwappableParametersCheck")) { llvm::dbgs() << ">>> calculateMixability for LType:\n" ; LType.dump(llvm::dbgs(), Ctx); llvm::dbgs() << "\nand RType:\n" ; RType.dump(llvm::dbgs(), Ctx); llvm::dbgs() << '\n';; } } while (false); | ||||
| 650 | if (LType == RType) { | ||||
| 651 | LLVM_DEBUG(llvm::dbgs() << "<<< calculateMixability. Trivial equality.\n")do { if (::llvm::DebugFlag && ::llvm::isCurrentDebugType ("EasilySwappableParametersCheck")) { llvm::dbgs() << "<<< calculateMixability. Trivial equality.\n" ; } } while (false); | ||||
| 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(LType.getTypePtr())) { | ||||
| 659 | LLVM_DEBUG(llvm::dbgs()do { if (::llvm::DebugFlag && ::llvm::isCurrentDebugType ("EasilySwappableParametersCheck")) { llvm::dbgs() << "--- calculateMixability. LHS is useless sugar.\n" ; } } while (false) | ||||
| 660 | << "--- calculateMixability. LHS is useless sugar.\n")do { if (::llvm::DebugFlag && ::llvm::isCurrentDebugType ("EasilySwappableParametersCheck")) { llvm::dbgs() << "--- calculateMixability. LHS is useless sugar.\n" ; } } while (false); | ||||
| 661 | return calculateMixability(Check, LType.getSingleStepDesugaredType(Ctx), | ||||
| 662 | RType, Ctx, ImplicitMode); | ||||
| 663 | } | ||||
| 664 | if (isUselessSugar(RType.getTypePtr())) { | ||||
| 665 | LLVM_DEBUG(llvm::dbgs()do { if (::llvm::DebugFlag && ::llvm::isCurrentDebugType ("EasilySwappableParametersCheck")) { llvm::dbgs() << "--- calculateMixability. RHS is useless sugar.\n" ; } } while (false) | ||||
| 666 | << "--- calculateMixability. RHS is useless sugar.\n")do { if (::llvm::DebugFlag && ::llvm::isCurrentDebugType ("EasilySwappableParametersCheck")) { llvm::dbgs() << "--- calculateMixability. RHS is useless sugar.\n" ; } } while (false); | ||||
| 667 | return calculateMixability( | ||||
| 668 | Check, LType, RType.getSingleStepDesugaredType(Ctx), Ctx, ImplicitMode); | ||||
| 669 | } | ||||
| 670 | |||||
| 671 | const auto *LLRef = LType->getAs<LValueReferenceType>(); | ||||
| 672 | const auto *RLRef = RType->getAs<LValueReferenceType>(); | ||||
| 673 | if (LLRef && RLRef) { | ||||
| 674 | LLVM_DEBUG(llvm::dbgs() << "--- calculateMixability. LHS and RHS are &.\n")do { if (::llvm::DebugFlag && ::llvm::isCurrentDebugType ("EasilySwappableParametersCheck")) { llvm::dbgs() << "--- calculateMixability. LHS and RHS are &.\n" ; } } while (false); | ||||
| 675 | |||||
| 676 | return calculateMixability(Check, LLRef->getPointeeType(), | ||||
| 677 | RLRef->getPointeeType(), Ctx, ImplicitMode) | ||||
| 678 | .withCommonTypeTransformed( | ||||
| 679 | [&Ctx](QualType QT) { return Ctx.getLValueReferenceType(QT); }); | ||||
| 680 | } | ||||
| 681 | // At a particular call site, what could be passed to a 'T' or 'const T' 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")do { if (::llvm::DebugFlag && ::llvm::isCurrentDebugType ("EasilySwappableParametersCheck")) { llvm::dbgs() << "--- calculateMixability. LHS is &.\n" ; } } while (false); | ||||
| 686 | return isLRefEquallyBindingToType(Check, LLRef, RType, Ctx, false, | ||||
| 687 | ImplicitMode) | | ||||
| 688 | MixFlags::ReferenceBind; | ||||
| 689 | } | ||||
| 690 | if (RLRef) { | ||||
| 691 | LLVM_DEBUG(llvm::dbgs() << "--- calculateMixability. RHS is &.\n")do { if (::llvm::DebugFlag && ::llvm::isCurrentDebugType ("EasilySwappableParametersCheck")) { llvm::dbgs() << "--- calculateMixability. RHS is &.\n" ; } } while (false); | ||||
| 692 | return isLRefEquallyBindingToType(Check, RLRef, LType, Ctx, true, | ||||
| 693 | ImplicitMode) | | ||||
| 694 | MixFlags::ReferenceBind; | ||||
| 695 | } | ||||
| 696 | |||||
| 697 | if (LType->getAs<TypedefType>()) { | ||||
| 698 | LLVM_DEBUG(llvm::dbgs() << "--- calculateMixability. LHS is typedef.\n")do { if (::llvm::DebugFlag && ::llvm::isCurrentDebugType ("EasilySwappableParametersCheck")) { llvm::dbgs() << "--- calculateMixability. LHS is typedef.\n" ; } } while (false); | ||||
| 699 | return calculateMixability(Check, LType.getSingleStepDesugaredType(Ctx), | ||||
| 700 | RType, Ctx, ImplicitMode) | | ||||
| 701 | MixFlags::TypeAlias; | ||||
| 702 | } | ||||
| 703 | if (RType->getAs<TypedefType>()) { | ||||
| 704 | LLVM_DEBUG(llvm::dbgs() << "--- calculateMixability. RHS is typedef.\n")do { if (::llvm::DebugFlag && ::llvm::isCurrentDebugType ("EasilySwappableParametersCheck")) { llvm::dbgs() << "--- calculateMixability. RHS is typedef.\n" ; } } while (false); | ||||
| 705 | return calculateMixability(Check, LType, | ||||
| 706 | RType.getSingleStepDesugaredType(Ctx), Ctx, | ||||
| 707 | ImplicitMode) | | ||||
| 708 | MixFlags::TypeAlias; | ||||
| 709 | } | ||||
| 710 | |||||
| 711 | // A parameter of type 'cvr1 T' and another of potentially 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()) {do { if (::llvm::DebugFlag && ::llvm::isCurrentDebugType ("EasilySwappableParametersCheck")) { if (LType.getLocalCVRQualifiers ()) { llvm::dbgs() << "--- calculateMixability. LHS has CVR-Qualifiers: " ; Qualifiers::fromCVRMask(LType.getLocalCVRQualifiers()) .print (llvm::dbgs(), Ctx.getPrintingPolicy()); llvm::dbgs() << '\n'; }; } } while (false) | ||||
| 718 | llvm::dbgs() << "--- calculateMixability. LHS has CVR-Qualifiers: ";do { if (::llvm::DebugFlag && ::llvm::isCurrentDebugType ("EasilySwappableParametersCheck")) { if (LType.getLocalCVRQualifiers ()) { llvm::dbgs() << "--- calculateMixability. LHS has CVR-Qualifiers: " ; Qualifiers::fromCVRMask(LType.getLocalCVRQualifiers()) .print (llvm::dbgs(), Ctx.getPrintingPolicy()); llvm::dbgs() << '\n'; }; } } while (false) | ||||
| 719 | Qualifiers::fromCVRMask(LType.getLocalCVRQualifiers())do { if (::llvm::DebugFlag && ::llvm::isCurrentDebugType ("EasilySwappableParametersCheck")) { if (LType.getLocalCVRQualifiers ()) { llvm::dbgs() << "--- calculateMixability. LHS has CVR-Qualifiers: " ; Qualifiers::fromCVRMask(LType.getLocalCVRQualifiers()) .print (llvm::dbgs(), Ctx.getPrintingPolicy()); llvm::dbgs() << '\n'; }; } } while (false) | ||||
| 720 | .print(llvm::dbgs(), Ctx.getPrintingPolicy());do { if (::llvm::DebugFlag && ::llvm::isCurrentDebugType ("EasilySwappableParametersCheck")) { if (LType.getLocalCVRQualifiers ()) { llvm::dbgs() << "--- calculateMixability. LHS has CVR-Qualifiers: " ; Qualifiers::fromCVRMask(LType.getLocalCVRQualifiers()) .print (llvm::dbgs(), Ctx.getPrintingPolicy()); llvm::dbgs() << '\n'; }; } } while (false) | ||||
| 721 | llvm::dbgs() << '\n';do { if (::llvm::DebugFlag && ::llvm::isCurrentDebugType ("EasilySwappableParametersCheck")) { if (LType.getLocalCVRQualifiers ()) { llvm::dbgs() << "--- calculateMixability. LHS has CVR-Qualifiers: " ; Qualifiers::fromCVRMask(LType.getLocalCVRQualifiers()) .print (llvm::dbgs(), Ctx.getPrintingPolicy()); llvm::dbgs() << '\n'; }; } } while (false) | ||||
| 722 | })do { if (::llvm::DebugFlag && ::llvm::isCurrentDebugType ("EasilySwappableParametersCheck")) { if (LType.getLocalCVRQualifiers ()) { llvm::dbgs() << "--- calculateMixability. LHS has CVR-Qualifiers: " ; Qualifiers::fromCVRMask(LType.getLocalCVRQualifiers()) .print (llvm::dbgs(), Ctx.getPrintingPolicy()); llvm::dbgs() << '\n'; }; } } while (false); | ||||
| 723 | LLVM_DEBUG(if (RType.getLocalCVRQualifiers()) {do { if (::llvm::DebugFlag && ::llvm::isCurrentDebugType ("EasilySwappableParametersCheck")) { if (RType.getLocalCVRQualifiers ()) { llvm::dbgs() << "--- calculateMixability. RHS has CVR-Qualifiers: " ; Qualifiers::fromCVRMask(RType.getLocalCVRQualifiers()) .print (llvm::dbgs(), Ctx.getPrintingPolicy()); llvm::dbgs() << '\n'; }; } } while (false) | ||||
| 724 | llvm::dbgs() << "--- calculateMixability. RHS has CVR-Qualifiers: ";do { if (::llvm::DebugFlag && ::llvm::isCurrentDebugType ("EasilySwappableParametersCheck")) { if (RType.getLocalCVRQualifiers ()) { llvm::dbgs() << "--- calculateMixability. RHS has CVR-Qualifiers: " ; Qualifiers::fromCVRMask(RType.getLocalCVRQualifiers()) .print (llvm::dbgs(), Ctx.getPrintingPolicy()); llvm::dbgs() << '\n'; }; } } while (false) | ||||
| 725 | Qualifiers::fromCVRMask(RType.getLocalCVRQualifiers())do { if (::llvm::DebugFlag && ::llvm::isCurrentDebugType ("EasilySwappableParametersCheck")) { if (RType.getLocalCVRQualifiers ()) { llvm::dbgs() << "--- calculateMixability. RHS has CVR-Qualifiers: " ; Qualifiers::fromCVRMask(RType.getLocalCVRQualifiers()) .print (llvm::dbgs(), Ctx.getPrintingPolicy()); llvm::dbgs() << '\n'; }; } } while (false) | ||||
| 726 | .print(llvm::dbgs(), Ctx.getPrintingPolicy());do { if (::llvm::DebugFlag && ::llvm::isCurrentDebugType ("EasilySwappableParametersCheck")) { if (RType.getLocalCVRQualifiers ()) { llvm::dbgs() << "--- calculateMixability. RHS has CVR-Qualifiers: " ; Qualifiers::fromCVRMask(RType.getLocalCVRQualifiers()) .print (llvm::dbgs(), Ctx.getPrintingPolicy()); llvm::dbgs() << '\n'; }; } } while (false) | ||||
| 727 | llvm::dbgs() << '\n';do { if (::llvm::DebugFlag && ::llvm::isCurrentDebugType ("EasilySwappableParametersCheck")) { if (RType.getLocalCVRQualifiers ()) { llvm::dbgs() << "--- calculateMixability. RHS has CVR-Qualifiers: " ; Qualifiers::fromCVRMask(RType.getLocalCVRQualifiers()) .print (llvm::dbgs(), Ctx.getPrintingPolicy()); llvm::dbgs() << '\n'; }; } } while (false) | ||||
| 728 | })do { if (::llvm::DebugFlag && ::llvm::isCurrentDebugType ("EasilySwappableParametersCheck")) { if (RType.getLocalCVRQualifiers ()) { llvm::dbgs() << "--- calculateMixability. RHS has CVR-Qualifiers: " ; Qualifiers::fromCVRMask(RType.getLocalCVRQualifiers()) .print (llvm::dbgs(), Ctx.getPrintingPolicy()); llvm::dbgs() << '\n'; }; } } while (false); | ||||
| 729 | |||||
| 730 | if (!Check.QualifiersMix) { | ||||
| 731 | LLVM_DEBUG(llvm::dbgs()do { if (::llvm::DebugFlag && ::llvm::isCurrentDebugType ("EasilySwappableParametersCheck")) { llvm::dbgs() << "<<< calculateMixability. QualifiersMix turned off - not " "mixable.\n"; } } while (false) | ||||
| 732 | << "<<< calculateMixability. QualifiersMix turned off - not "do { if (::llvm::DebugFlag && ::llvm::isCurrentDebugType ("EasilySwappableParametersCheck")) { llvm::dbgs() << "<<< calculateMixability. QualifiersMix turned off - not " "mixable.\n"; } } while (false) | ||||
| 733 | "mixable.\n")do { if (::llvm::DebugFlag && ::llvm::isCurrentDebugType ("EasilySwappableParametersCheck")) { llvm::dbgs() << "<<< calculateMixability. QualifiersMix turned off - not " "mixable.\n"; } } while (false); | ||||
| 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()) {do { if (::llvm::DebugFlag && ::llvm::isCurrentDebugType ("EasilySwappableParametersCheck")) { if (LType.getLocalCVRQualifiers ()) { llvm::dbgs() << "--- calculateMixability. LHS and RHS have same CVR-Qualifiers: " ; Qualifiers::fromCVRMask(LType.getLocalCVRQualifiers()) .print (llvm::dbgs(), Ctx.getPrintingPolicy()); llvm::dbgs() << '\n'; }; } } while (false) | ||||
| 744 | llvm::dbgs()do { if (::llvm::DebugFlag && ::llvm::isCurrentDebugType ("EasilySwappableParametersCheck")) { if (LType.getLocalCVRQualifiers ()) { llvm::dbgs() << "--- calculateMixability. LHS and RHS have same CVR-Qualifiers: " ; Qualifiers::fromCVRMask(LType.getLocalCVRQualifiers()) .print (llvm::dbgs(), Ctx.getPrintingPolicy()); llvm::dbgs() << '\n'; }; } } while (false) | ||||
| 745 | << "--- calculateMixability. LHS and RHS have same CVR-Qualifiers: ";do { if (::llvm::DebugFlag && ::llvm::isCurrentDebugType ("EasilySwappableParametersCheck")) { if (LType.getLocalCVRQualifiers ()) { llvm::dbgs() << "--- calculateMixability. LHS and RHS have same CVR-Qualifiers: " ; Qualifiers::fromCVRMask(LType.getLocalCVRQualifiers()) .print (llvm::dbgs(), Ctx.getPrintingPolicy()); llvm::dbgs() << '\n'; }; } } while (false) | ||||
| 746 | Qualifiers::fromCVRMask(LType.getLocalCVRQualifiers())do { if (::llvm::DebugFlag && ::llvm::isCurrentDebugType ("EasilySwappableParametersCheck")) { if (LType.getLocalCVRQualifiers ()) { llvm::dbgs() << "--- calculateMixability. LHS and RHS have same CVR-Qualifiers: " ; Qualifiers::fromCVRMask(LType.getLocalCVRQualifiers()) .print (llvm::dbgs(), Ctx.getPrintingPolicy()); llvm::dbgs() << '\n'; }; } } while (false) | ||||
| 747 | .print(llvm::dbgs(), Ctx.getPrintingPolicy());do { if (::llvm::DebugFlag && ::llvm::isCurrentDebugType ("EasilySwappableParametersCheck")) { if (LType.getLocalCVRQualifiers ()) { llvm::dbgs() << "--- calculateMixability. LHS and RHS have same CVR-Qualifiers: " ; Qualifiers::fromCVRMask(LType.getLocalCVRQualifiers()) .print (llvm::dbgs(), Ctx.getPrintingPolicy()); llvm::dbgs() << '\n'; }; } } while (false) | ||||
| 748 | llvm::dbgs() << '\n';do { if (::llvm::DebugFlag && ::llvm::isCurrentDebugType ("EasilySwappableParametersCheck")) { if (LType.getLocalCVRQualifiers ()) { llvm::dbgs() << "--- calculateMixability. LHS and RHS have same CVR-Qualifiers: " ; Qualifiers::fromCVRMask(LType.getLocalCVRQualifiers()) .print (llvm::dbgs(), Ctx.getPrintingPolicy()); llvm::dbgs() << '\n'; }; } } while (false) | ||||
| 749 | })do { if (::llvm::DebugFlag && ::llvm::isCurrentDebugType ("EasilySwappableParametersCheck")) { if (LType.getLocalCVRQualifiers ()) { llvm::dbgs() << "--- calculateMixability. LHS and RHS have same CVR-Qualifiers: " ; Qualifiers::fromCVRMask(LType.getLocalCVRQualifiers()) .print (llvm::dbgs(), Ctx.getPrintingPolicy()); llvm::dbgs() << '\n'; }; } } while (false); | ||||
| 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 "do { if (::llvm::DebugFlag && ::llvm::isCurrentDebugType ("EasilySwappableParametersCheck")) { llvm::dbgs() << "<<< calculateMixability. Additional " "non-equal incompatible qualifiers.\n"; } } while (false) | ||||
| 760 | "non-equal incompatible qualifiers.\n")do { if (::llvm::DebugFlag && ::llvm::isCurrentDebugType ("EasilySwappableParametersCheck")) { llvm::dbgs() << "<<< calculateMixability. Additional " "non-equal incompatible qualifiers.\n"; } } while (false); | ||||
| 761 | return {MixFlags::None}; | ||||
| 762 | } | ||||
| 763 | |||||
| 764 | MixData UnqualifiedMixability = | ||||
| 765 | calculateMixability(Check, LType.getLocalUnqualifiedType(), | ||||
| 766 | RType.getLocalUnqualifiedType(), Ctx, ImplicitMode) | ||||
| 767 | .withCommonTypeTransformed([&AdditionalQuals, &Ctx](QualType QT) { | ||||
| 768 | // Once the mixability was deduced, apply the qualifiers common | ||||
| 769 | // to the two type back onto the diagnostic printout. | ||||
| 770 | return Ctx.getQualifiedType(QT, AdditionalQuals.CommonQualifiers); | ||||
| 771 | }); | ||||
| 772 | |||||
| 773 | if (!OriginallySameQualifiers) | ||||
| 774 | // User-enabled qualifier change modelled for the mix. | ||||
| 775 | return UnqualifiedMixability | MixFlags::Qualifiers; | ||||
| 776 | |||||
| 777 | // Apply the same qualifier back into the found common type if they were | ||||
| 778 | // the same. | ||||
| 779 | return UnqualifiedMixability.withCommonTypeTransformed( | ||||
| 780 | [&Ctx, LType](QualType QT) { | ||||
| 781 | return Ctx.getQualifiedType(QT, LType.getLocalQualifiers()); | ||||
| 782 | }); | ||||
| 783 | } | ||||
| 784 | |||||
| 785 | // Certain constructs match on the last catch-all getCanonicalType() equality, | ||||
| 786 | // which is perhaps something not what we want. If this variable is true, | ||||
| 787 | // the canonical type equality will be ignored. | ||||
| 788 | bool RecursiveReturnDiscardingCanonicalType = false; | ||||
| 789 | |||||
| 790 | if (LType->isPointerType() && RType->isPointerType()) { | ||||
| 791 | // If both types are pointers, and pointed to the exact same type, | ||||
| 792 | // LType == RType took care of that. Try to see if the pointee type has | ||||
| 793 | // some other match. However, this must not consider implicit conversions. | ||||
| 794 | LLVM_DEBUG(llvm::dbgs()do { if (::llvm::DebugFlag && ::llvm::isCurrentDebugType ("EasilySwappableParametersCheck")) { llvm::dbgs() << "--- calculateMixability. LHS and RHS are Ptrs.\n" ; } } while (false) | ||||
| 795 | << "--- calculateMixability. LHS and RHS are Ptrs.\n")do { if (::llvm::DebugFlag && ::llvm::isCurrentDebugType ("EasilySwappableParametersCheck")) { llvm::dbgs() << "--- calculateMixability. LHS and RHS are Ptrs.\n" ; } } while (false); | ||||
| 796 | MixData MixOfPointee = | ||||
| 797 | calculateMixability(Check, LType->getPointeeType(), | ||||
| 798 | RType->getPointeeType(), Ctx, | ||||
| 799 | ImplicitConversionModellingMode::None) | ||||
| 800 | .withCommonTypeTransformed( | ||||
| 801 | [&Ctx](QualType QT) { return Ctx.getPointerType(QT); }); | ||||
| 802 | if (hasFlag(MixOfPointee.Flags, | ||||
| 803 | MixFlags::WorkaroundDisableCanonicalEquivalence)) | ||||
| 804 | RecursiveReturnDiscardingCanonicalType = true; | ||||
| 805 | |||||
| 806 | MixOfPointee.sanitize(); | ||||
| 807 | if (MixOfPointee.indicatesMixability()) { | ||||
| 808 | LLVM_DEBUG(llvm::dbgs()do { if (::llvm::DebugFlag && ::llvm::isCurrentDebugType ("EasilySwappableParametersCheck")) { llvm::dbgs() << "<<< calculateMixability. Pointees are mixable.\n" ; } } while (false) | ||||
| 809 | << "<<< calculateMixability. Pointees are mixable.\n")do { if (::llvm::DebugFlag && ::llvm::isCurrentDebugType ("EasilySwappableParametersCheck")) { llvm::dbgs() << "<<< calculateMixability. Pointees are mixable.\n" ; } } while (false); | ||||
| 810 | return MixOfPointee; | ||||
| 811 | } | ||||
| 812 | } | ||||
| 813 | |||||
| 814 | if (ImplicitMode > ImplicitConversionModellingMode::None) { | ||||
| 815 | LLVM_DEBUG(llvm::dbgs() << "--- calculateMixability. Start implicit...\n")do { if (::llvm::DebugFlag && ::llvm::isCurrentDebugType ("EasilySwappableParametersCheck")) { llvm::dbgs() << "--- calculateMixability. Start implicit...\n" ; } } while (false); | ||||
| 816 | MixData MixLTR = | ||||
| 817 | approximateImplicitConversion(Check, LType, RType, Ctx, ImplicitMode); | ||||
| 818 | LLVM_DEBUG(do { if (::llvm::DebugFlag && ::llvm::isCurrentDebugType ("EasilySwappableParametersCheck")) { if (hasFlag(MixLTR.Flags , MixFlags::ImplicitConversion)) llvm::dbgs() << "--- calculateMixability. Implicit Left -> Right found.\n" ;; } } while (false) | ||||
| 819 | if (hasFlag(MixLTR.Flags, MixFlags::ImplicitConversion)) llvm::dbgs()do { if (::llvm::DebugFlag && ::llvm::isCurrentDebugType ("EasilySwappableParametersCheck")) { if (hasFlag(MixLTR.Flags , MixFlags::ImplicitConversion)) llvm::dbgs() << "--- calculateMixability. Implicit Left -> Right found.\n" ;; } } while (false) | ||||
| 820 | << "--- calculateMixability. Implicit Left -> Right found.\n";)do { if (::llvm::DebugFlag && ::llvm::isCurrentDebugType ("EasilySwappableParametersCheck")) { if (hasFlag(MixLTR.Flags , MixFlags::ImplicitConversion)) llvm::dbgs() << "--- calculateMixability. Implicit Left -> Right found.\n" ;; } } while (false); | ||||
| 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 "do { if (::llvm::DebugFlag && ::llvm::isCurrentDebugType ("EasilySwappableParametersCheck")) { llvm::dbgs() << "<<< calculateMixability. Implicit " "conversion, one-way, standard-only.\n"; } } while (false) | ||||
| 830 | "conversion, one-way, standard-only.\n")do { if (::llvm::DebugFlag && ::llvm::isCurrentDebugType ("EasilySwappableParametersCheck")) { llvm::dbgs() << "<<< calculateMixability. Implicit " "conversion, one-way, standard-only.\n"; } } while (false); | ||||
| 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, RType, LType, Ctx, ImplicitMode); | ||||
| 838 | LLVM_DEBUG(do { if (::llvm::DebugFlag && ::llvm::isCurrentDebugType ("EasilySwappableParametersCheck")) { if (hasFlag(MixRTL.Flags , MixFlags::ImplicitConversion)) llvm::dbgs() << "--- calculateMixability. Implicit Right -> Left found.\n" ;; } } while (false) | ||||
| 839 | if (hasFlag(MixRTL.Flags, MixFlags::ImplicitConversion)) llvm::dbgs()do { if (::llvm::DebugFlag && ::llvm::isCurrentDebugType ("EasilySwappableParametersCheck")) { if (hasFlag(MixRTL.Flags , MixFlags::ImplicitConversion)) llvm::dbgs() << "--- calculateMixability. Implicit Right -> Left found.\n" ;; } } while (false) | ||||
| 840 | << "--- calculateMixability. Implicit Right -> Left found.\n";)do { if (::llvm::DebugFlag && ::llvm::isCurrentDebugType ("EasilySwappableParametersCheck")) { if (hasFlag(MixRTL.Flags , MixFlags::ImplicitConversion)) llvm::dbgs() << "--- calculateMixability. Implicit Right -> Left found.\n" ;; } } while (false); | ||||
| 841 | |||||
| 842 | if (MixLTR.Conversion && MixRTL.Conversion) { | ||||
| 843 | LLVM_DEBUG(do { if (::llvm::DebugFlag && ::llvm::isCurrentDebugType ("EasilySwappableParametersCheck")) { llvm::dbgs() << "<<< calculateMixability. Implicit conversion, bidirectional.\n" ; } } while (false) | ||||
| 844 | llvm::dbgs()do { if (::llvm::DebugFlag && ::llvm::isCurrentDebugType ("EasilySwappableParametersCheck")) { llvm::dbgs() << "<<< calculateMixability. Implicit conversion, bidirectional.\n" ; } } while (false) | ||||
| 845 | << "<<< calculateMixability. Implicit conversion, bidirectional.\n")do { if (::llvm::DebugFlag && ::llvm::isCurrentDebugType ("EasilySwappableParametersCheck")) { llvm::dbgs() << "<<< calculateMixability. Implicit conversion, bidirectional.\n" ; } } while (false); | ||||
| 846 | return {MixFlags::ImplicitConversion, MixLTR.Conversion, | ||||
| 847 | MixRTL.Conversion}; | ||||
| 848 | } | ||||
| 849 | } | ||||
| 850 | |||||
| 851 | if (RecursiveReturnDiscardingCanonicalType) | ||||
| 852 | LLVM_DEBUG(llvm::dbgs() << "--- calculateMixability. Before CanonicalType, "do { if (::llvm::DebugFlag && ::llvm::isCurrentDebugType ("EasilySwappableParametersCheck")) { llvm::dbgs() << "--- calculateMixability. Before CanonicalType, " "Discard was enabled.\n"; } } while (false) | ||||
| 853 | "Discard was enabled.\n")do { if (::llvm::DebugFlag && ::llvm::isCurrentDebugType ("EasilySwappableParametersCheck")) { llvm::dbgs() << "--- calculateMixability. Before CanonicalType, " "Discard was enabled.\n"; } } while (false); | ||||
| 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()do { if (::llvm::DebugFlag && ::llvm::isCurrentDebugType ("EasilySwappableParametersCheck")) { llvm::dbgs() << "--- calculateMixability. Discarding potential canonical " "equivalence on FunctionProtoTypes.\n"; } } while (false) | ||||
| 862 | << "--- calculateMixability. Discarding potential canonical "do { if (::llvm::DebugFlag && ::llvm::isCurrentDebugType ("EasilySwappableParametersCheck")) { llvm::dbgs() << "--- calculateMixability. Discarding potential canonical " "equivalence on FunctionProtoTypes.\n"; } } while (false) | ||||
| 863 | "equivalence on FunctionProtoTypes.\n")do { if (::llvm::DebugFlag && ::llvm::isCurrentDebugType ("EasilySwappableParametersCheck")) { llvm::dbgs() << "--- calculateMixability. Discarding potential canonical " "equivalence on FunctionProtoTypes.\n"; } } while (false); | ||||
| 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()do { if (::llvm::DebugFlag && ::llvm::isCurrentDebugType ("EasilySwappableParametersCheck")) { llvm::dbgs() << "<<< calculateMixability. Same CanonicalType.\n" ; } } while (false) | ||||
| 874 | << "<<< calculateMixability. Same CanonicalType.\n")do { if (::llvm::DebugFlag && ::llvm::isCurrentDebugType ("EasilySwappableParametersCheck")) { llvm::dbgs() << "<<< calculateMixability. Same CanonicalType.\n" ; } } while (false); | ||||
| 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()do { if (::llvm::DebugFlag && ::llvm::isCurrentDebugType ("EasilySwappableParametersCheck")) { if (MixToReturn.Flags == MixFlags::None) llvm::dbgs() << "<<< calculateMixability. No match found.\n" ; } } while (false) | ||||
| 882 | << "<<< calculateMixability. No match found.\n")do { if (::llvm::DebugFlag && ::llvm::isCurrentDebugType ("EasilySwappableParametersCheck")) { if (MixToReturn.Flags == MixFlags::None) llvm::dbgs() << "<<< calculateMixability. No match found.\n" ; } } while (false); | ||||
| 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";do { if (::llvm::DebugFlag && ::llvm::isCurrentDebugType ("EasilySwappableParametersCheck")) { llvm::dbgs() << ">>> isLRefEquallyBindingToType for LRef:\n" ; LRef->dump(llvm::dbgs(), Ctx); llvm::dbgs() << "\nand Type:\n" ; Ty.dump(llvm::dbgs(), Ctx); llvm::dbgs() << '\n';; } } while (false) | ||||
| 897 | LRef->dump(llvm::dbgs(), Ctx); llvm::dbgs() << "\nand Type:\n";do { if (::llvm::DebugFlag && ::llvm::isCurrentDebugType ("EasilySwappableParametersCheck")) { llvm::dbgs() << ">>> isLRefEquallyBindingToType for LRef:\n" ; LRef->dump(llvm::dbgs(), Ctx); llvm::dbgs() << "\nand Type:\n" ; Ty.dump(llvm::dbgs(), Ctx); llvm::dbgs() << '\n';; } } while (false) | ||||
| 898 | Ty.dump(llvm::dbgs(), Ctx); llvm::dbgs() << '\n';)do { if (::llvm::DebugFlag && ::llvm::isCurrentDebugType ("EasilySwappableParametersCheck")) { llvm::dbgs() << ">>> isLRefEquallyBindingToType for LRef:\n" ; LRef->dump(llvm::dbgs(), Ctx); llvm::dbgs() << "\nand Type:\n" ; Ty.dump(llvm::dbgs(), Ctx); llvm::dbgs() << '\n';; } } while (false); | ||||
| 899 | |||||
| 900 | QualType ReferredType = LRef->getPointeeType(); | ||||
| 901 | if (!ReferredType.isLocalConstQualified() && | ||||
| 902 | ReferredType->getAs<TypedefType>()) { | ||||
| 903 | LLVM_DEBUG(do { if (::llvm::DebugFlag && ::llvm::isCurrentDebugType ("EasilySwappableParametersCheck")) { llvm::dbgs() << "--- isLRefEquallyBindingToType. Non-const LRef to Typedef.\n" ; } } while (false) | ||||
| 904 | llvm::dbgs()do { if (::llvm::DebugFlag && ::llvm::isCurrentDebugType ("EasilySwappableParametersCheck")) { llvm::dbgs() << "--- isLRefEquallyBindingToType. Non-const LRef to Typedef.\n" ; } } while (false) | ||||
| 905 | << "--- isLRefEquallyBindingToType. Non-const LRef to Typedef.\n")do { if (::llvm::DebugFlag && ::llvm::isCurrentDebugType ("EasilySwappableParametersCheck")) { llvm::dbgs() << "--- isLRefEquallyBindingToType. Non-const LRef to Typedef.\n" ; } } while (false); | ||||
| 906 | ReferredType = ReferredType.getDesugaredType(Ctx); | ||||
| 907 | if (!ReferredType.isLocalConstQualified()) { | ||||
| 908 | LLVM_DEBUG(llvm::dbgs()do { if (::llvm::DebugFlag && ::llvm::isCurrentDebugType ("EasilySwappableParametersCheck")) { llvm::dbgs() << "<<< isLRefEquallyBindingToType. Typedef is not const.\n" ; } } while (false) | ||||
| 909 | << "<<< isLRefEquallyBindingToType. Typedef is not const.\n")do { if (::llvm::DebugFlag && ::llvm::isCurrentDebugType ("EasilySwappableParametersCheck")) { llvm::dbgs() << "<<< isLRefEquallyBindingToType. Typedef is not const.\n" ; } } while (false); | ||||
| 910 | return {MixFlags::None}; | ||||
| 911 | } | ||||
| 912 | |||||
| 913 | LLVM_DEBUG(llvm::dbgs() << "--- isLRefEquallyBindingToType. Typedef is "do { if (::llvm::DebugFlag && ::llvm::isCurrentDebugType ("EasilySwappableParametersCheck")) { llvm::dbgs() << "--- isLRefEquallyBindingToType. Typedef is " "const, considering as const LRef.\n"; } } while (false) | ||||
| 914 | "const, considering as const LRef.\n")do { if (::llvm::DebugFlag && ::llvm::isCurrentDebugType ("EasilySwappableParametersCheck")) { llvm::dbgs() << "--- isLRefEquallyBindingToType. Typedef is " "const, considering as const LRef.\n"; } } while (false); | ||||
| 915 | } else if (!ReferredType.isLocalConstQualified()) { | ||||
| 916 | LLVM_DEBUG(llvm::dbgs()do { if (::llvm::DebugFlag && ::llvm::isCurrentDebugType ("EasilySwappableParametersCheck")) { llvm::dbgs() << "<<< isLRefEquallyBindingToType. Not const LRef.\n" ; } } while (false) | ||||
| 917 | << "<<< isLRefEquallyBindingToType. Not const LRef.\n")do { if (::llvm::DebugFlag && ::llvm::isCurrentDebugType ("EasilySwappableParametersCheck")) { llvm::dbgs() << "<<< isLRefEquallyBindingToType. Not const LRef.\n" ; } } while (false); | ||||
| 918 | return {MixFlags::None}; | ||||
| 919 | }; | ||||
| 920 | |||||
| 921 | assert(ReferredType.isLocalConstQualified() &&(static_cast <bool> (ReferredType.isLocalConstQualified () && "Reaching this point means we are sure LRef is effectively a const&." ) ? void (0) : __assert_fail ("ReferredType.isLocalConstQualified() && \"Reaching this point means we are sure LRef is effectively a const&.\"" , "clang-tools-extra/clang-tidy/bugprone/EasilySwappableParametersCheck.cpp" , 922, __extension__ __PRETTY_FUNCTION__)) | ||||
| 922 | "Reaching this point means we are sure LRef is effectively a const&.")(static_cast <bool> (ReferredType.isLocalConstQualified () && "Reaching this point means we are sure LRef is effectively a const&." ) ? void (0) : __assert_fail ("ReferredType.isLocalConstQualified() && \"Reaching this point means we are sure LRef is effectively a const&.\"" , "clang-tools-extra/clang-tidy/bugprone/EasilySwappableParametersCheck.cpp" , 922, __extension__ __PRETTY_FUNCTION__)); | ||||
| 923 | |||||
| 924 | if (ReferredType == Ty) { | ||||
| 925 | LLVM_DEBUG(do { if (::llvm::DebugFlag && ::llvm::isCurrentDebugType ("EasilySwappableParametersCheck")) { llvm::dbgs() << "<<< isLRefEquallyBindingToType. Type of referred matches.\n" ; } } while (false) | ||||
| 926 | llvm::dbgs()do { if (::llvm::DebugFlag && ::llvm::isCurrentDebugType ("EasilySwappableParametersCheck")) { llvm::dbgs() << "<<< isLRefEquallyBindingToType. Type of referred matches.\n" ; } } while (false) | ||||
| 927 | << "<<< isLRefEquallyBindingToType. Type of referred matches.\n")do { if (::llvm::DebugFlag && ::llvm::isCurrentDebugType ("EasilySwappableParametersCheck")) { llvm::dbgs() << "<<< isLRefEquallyBindingToType. Type of referred matches.\n" ; } } while (false); | ||||
| 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 "do { if (::llvm::DebugFlag && ::llvm::isCurrentDebugType ("EasilySwappableParametersCheck")) { llvm::dbgs() << "<<< isLRefEquallyBindingToType. Type of " "referred matches to non-const qualified.\n"; } } while (false ) | ||||
| 935 | "referred matches to non-const qualified.\n")do { if (::llvm::DebugFlag && ::llvm::isCurrentDebugType ("EasilySwappableParametersCheck")) { llvm::dbgs() << "<<< isLRefEquallyBindingToType. Type of " "referred matches to non-const qualified.\n"; } } while (false ); | ||||
| 936 | return {MixFlags::Trivial, NonConstReferredType}; | ||||
| 937 | } | ||||
| 938 | |||||
| 939 | LLVM_DEBUG(do { if (::llvm::DebugFlag && ::llvm::isCurrentDebugType ("EasilySwappableParametersCheck")) { llvm::dbgs() << "--- isLRefEquallyBindingToType. Checking mix for underlying type.\n" ; } } while (false) | ||||
| 940 | llvm::dbgs()do { if (::llvm::DebugFlag && ::llvm::isCurrentDebugType ("EasilySwappableParametersCheck")) { llvm::dbgs() << "--- isLRefEquallyBindingToType. Checking mix for underlying type.\n" ; } } while (false) | ||||
| 941 | << "--- isLRefEquallyBindingToType. Checking mix for underlying type.\n")do { if (::llvm::DebugFlag && ::llvm::isCurrentDebugType ("EasilySwappableParametersCheck")) { llvm::dbgs() << "--- isLRefEquallyBindingToType. Checking mix for underlying type.\n" ; } } while (false); | ||||
| 942 | return IsRefRHS ? calculateMixability(Check, Ty, NonConstReferredType, Ctx, | ||||
| 943 | ImplicitMode) | ||||
| 944 | : calculateMixability(Check, NonConstReferredType, 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";do { if (::llvm::DebugFlag && ::llvm::isCurrentDebugType ("EasilySwappableParametersCheck")) { llvm::dbgs() << ">>> approximateStdConv for LType:\n" ; From.dump(llvm::dbgs(), Ctx); llvm::dbgs() << "\nand RType:\n" ; To.dump(llvm::dbgs(), Ctx); llvm::dbgs() << '\n';; } } while (false) | ||||
| 958 | From.dump(llvm::dbgs(), Ctx); llvm::dbgs() << "\nand RType:\n";do { if (::llvm::DebugFlag && ::llvm::isCurrentDebugType ("EasilySwappableParametersCheck")) { llvm::dbgs() << ">>> approximateStdConv for LType:\n" ; From.dump(llvm::dbgs(), Ctx); llvm::dbgs() << "\nand RType:\n" ; To.dump(llvm::dbgs(), Ctx); llvm::dbgs() << '\n';; } } while (false) | ||||
| 959 | To.dump(llvm::dbgs(), Ctx); llvm::dbgs() << '\n';)do { if (::llvm::DebugFlag && ::llvm::isCurrentDebugType ("EasilySwappableParametersCheck")) { llvm::dbgs() << ">>> approximateStdConv for LType:\n" ; From.dump(llvm::dbgs(), Ctx); llvm::dbgs() << "\nand RType:\n" ; To.dump(llvm::dbgs(), Ctx); llvm::dbgs() << '\n';; } } while (false); | ||||
| 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 QualifiersToApply = From.split().Quals.getAsOpaqueValue(); | ||||
| 971 | |||||
| 972 | // LValue->RValue is irrelevant for the check, because it is a thing to be | ||||
| 973 | // done at a call site, and will be performed if need be performed. | ||||
| 974 | |||||
| 975 | // Array->Pointer decay is handled by the main method in desugaring | ||||
| 976 | // the parameter's DecayedType as "useless sugar". | ||||
| 977 | |||||
| 978 | // Function->Pointer conversions are also irrelevant, because a | ||||
| 979 | // "FunctionType" cannot be the type of a parameter variable, so this | ||||
| 980 | // conversion is only meaningful at call sites. | ||||
| 981 | |||||
| 982 | // Numeric promotions and conversions. | ||||
| 983 | const auto *FromBuiltin = WorkType->getAs<BuiltinType>(); | ||||
| 984 | const auto *ToBuiltin = To->getAs<BuiltinType>(); | ||||
| 985 | bool FromNumeric = FromBuiltin && (FromBuiltin->isIntegerType() || | ||||
| 986 | FromBuiltin->isFloatingType()); | ||||
| 987 | bool ToNumeric = | ||||
| 988 | ToBuiltin && (ToBuiltin->isIntegerType() || ToBuiltin->isFloatingType()); | ||||
| 989 | if (FromNumeric && ToNumeric) { | ||||
| 990 | // If both are integral types, the numeric conversion is performed. | ||||
| 991 | // Reapply the qualifiers of the original type, however, so | ||||
| 992 | // "const int -> double" in this case moves over to | ||||
| 993 | // "const double -> double". | ||||
| 994 | LLVM_DEBUG(llvm::dbgs()do { if (::llvm::DebugFlag && ::llvm::isCurrentDebugType ("EasilySwappableParametersCheck")) { llvm::dbgs() << "--- approximateStdConv. Conversion between numerics.\n" ; } } while (false) | ||||
| 995 | << "--- approximateStdConv. Conversion between numerics.\n")do { if (::llvm::DebugFlag && ::llvm::isCurrentDebugType ("EasilySwappableParametersCheck")) { llvm::dbgs() << "--- approximateStdConv. Conversion between numerics.\n" ; } } while (false); | ||||
| 996 | WorkType = QualType{ToBuiltin, QualifiersToApply}; | ||||
| 997 | } | ||||
| 998 | |||||
| 999 | const auto *FromEnum = WorkType->getAs<EnumType>(); | ||||
| 1000 | const auto *ToEnum = To->getAs<EnumType>(); | ||||
| 1001 | if (FromEnum && ToNumeric && FromEnum->isUnscopedEnumerationType()) { | ||||
| 1002 | // Unscoped enumerations (or enumerations in C) convert to numerics. | ||||
| 1003 | LLVM_DEBUG(llvm::dbgs()do { if (::llvm::DebugFlag && ::llvm::isCurrentDebugType ("EasilySwappableParametersCheck")) { llvm::dbgs() << "--- approximateStdConv. Unscoped enum to numeric.\n" ; } } while (false) | ||||
| 1004 | << "--- approximateStdConv. Unscoped enum to numeric.\n")do { if (::llvm::DebugFlag && ::llvm::isCurrentDebugType ("EasilySwappableParametersCheck")) { llvm::dbgs() << "--- approximateStdConv. Unscoped enum to numeric.\n" ; } } while (false); | ||||
| 1005 | WorkType = QualType{ToBuiltin, QualifiersToApply}; | ||||
| 1006 | } else if (FromNumeric && ToEnum && ToEnum->isUnscopedEnumerationType()) { | ||||
| 1007 | // Numeric types convert to enumerations only in C. | ||||
| 1008 | if (Ctx.getLangOpts().CPlusPlus) { | ||||
| 1009 | LLVM_DEBUG(llvm::dbgs() << "<<< approximateStdConv. Numeric to unscoped "do { if (::llvm::DebugFlag && ::llvm::isCurrentDebugType ("EasilySwappableParametersCheck")) { llvm::dbgs() << "<<< approximateStdConv. Numeric to unscoped " "enum, not possible in C++!\n"; } } while (false) | ||||
| 1010 | "enum, not possible in C++!\n")do { if (::llvm::DebugFlag && ::llvm::isCurrentDebugType ("EasilySwappableParametersCheck")) { llvm::dbgs() << "<<< approximateStdConv. Numeric to unscoped " "enum, not possible in C++!\n"; } } while (false); | ||||
| 1011 | return {}; | ||||
| 1012 | } | ||||
| 1013 | |||||
| 1014 | LLVM_DEBUG(llvm::dbgs()do { if (::llvm::DebugFlag && ::llvm::isCurrentDebugType ("EasilySwappableParametersCheck")) { llvm::dbgs() << "--- approximateStdConv. Numeric to unscoped enum.\n" ; } } while (false) | ||||
| 1015 | << "--- approximateStdConv. Numeric to unscoped enum.\n")do { if (::llvm::DebugFlag && ::llvm::isCurrentDebugType ("EasilySwappableParametersCheck")) { llvm::dbgs() << "--- approximateStdConv. Numeric to unscoped enum.\n" ; } } while (false); | ||||
| 1016 | WorkType = QualType{ToEnum, QualifiersToApply}; | ||||
| 1017 | } | ||||
| 1018 | |||||
| 1019 | // Check for pointer conversions. | ||||
| 1020 | const auto *FromPtr = WorkType->getAs<PointerType>(); | ||||
| 1021 | const auto *ToPtr = To->getAs<PointerType>(); | ||||
| 1022 | if (FromPtr && ToPtr) { | ||||
| 1023 | if (ToPtr->isVoidPointerType()) { | ||||
| 1024 | LLVM_DEBUG(llvm::dbgs() << "--- approximateStdConv. To void pointer.\n")do { if (::llvm::DebugFlag && ::llvm::isCurrentDebugType ("EasilySwappableParametersCheck")) { llvm::dbgs() << "--- approximateStdConv. To void pointer.\n" ; } } while (false); | ||||
| 1025 | WorkType = QualType{ToPtr, QualifiersToApply}; | ||||
| 1026 | } | ||||
| 1027 | |||||
| 1028 | const auto *FromRecordPtr = FromPtr->getPointeeCXXRecordDecl(); | ||||
| 1029 | const auto *ToRecordPtr = ToPtr->getPointeeCXXRecordDecl(); | ||||
| 1030 | if (isDerivedToBase(FromRecordPtr, ToRecordPtr)) { | ||||
| 1031 | LLVM_DEBUG(llvm::dbgs() << "--- approximateStdConv. Derived* to Base*\n")do { if (::llvm::DebugFlag && ::llvm::isCurrentDebugType ("EasilySwappableParametersCheck")) { llvm::dbgs() << "--- approximateStdConv. Derived* to Base*\n" ; } } while (false); | ||||
| 1032 | WorkType = QualType{ToPtr, QualifiersToApply}; | ||||
| 1033 | } | ||||
| 1034 | } | ||||
| 1035 | |||||
| 1036 | // Model the slicing Derived-to-Base too, as "BaseT temporary = derived;" | ||||
| 1037 | // can also be compiled. | ||||
| 1038 | const auto *FromRecord = WorkType->getAsCXXRecordDecl(); | ||||
| 1039 | const auto *ToRecord = To->getAsCXXRecordDecl(); | ||||
| 1040 | if (isDerivedToBase(FromRecord, ToRecord)) { | ||||
| 1041 | LLVM_DEBUG(llvm::dbgs() << "--- approximateStdConv. Derived To Base.\n")do { if (::llvm::DebugFlag && ::llvm::isCurrentDebugType ("EasilySwappableParametersCheck")) { llvm::dbgs() << "--- approximateStdConv. Derived To Base.\n" ; } } while (false); | ||||
| 1042 | WorkType = QualType{ToRecord->getTypeForDecl(), QualifiersToApply}; | ||||
| 1043 | } | ||||
| 1044 | |||||
| 1045 | if (Ctx.getLangOpts().CPlusPlus17 && FromPtr && ToPtr) { | ||||
| 1046 | // Function pointer conversion: A noexcept function pointer can be passed | ||||
| 1047 | // to a non-noexcept one. | ||||
| 1048 | const auto *FromFunctionPtr = | ||||
| 1049 | FromPtr->getPointeeType()->getAs<FunctionProtoType>(); | ||||
| 1050 | const auto *ToFunctionPtr = | ||||
| 1051 | ToPtr->getPointeeType()->getAs<FunctionProtoType>(); | ||||
| 1052 | if (FromFunctionPtr && ToFunctionPtr && | ||||
| 1053 | FromFunctionPtr->hasNoexceptExceptionSpec() && | ||||
| 1054 | !ToFunctionPtr->hasNoexceptExceptionSpec()) { | ||||
| 1055 | LLVM_DEBUG(llvm::dbgs() << "--- approximateStdConv. noexcept function "do { if (::llvm::DebugFlag && ::llvm::isCurrentDebugType ("EasilySwappableParametersCheck")) { llvm::dbgs() << "--- approximateStdConv. noexcept function " "pointer to non-noexcept.\n"; } } while (false) | ||||
| 1056 | "pointer to non-noexcept.\n")do { if (::llvm::DebugFlag && ::llvm::isCurrentDebugType ("EasilySwappableParametersCheck")) { llvm::dbgs() << "--- approximateStdConv. noexcept function " "pointer to non-noexcept.\n"; } } while (false); | ||||
| 1057 | WorkType = QualType{ToPtr, QualifiersToApply}; | ||||
| 1058 | } | ||||
| 1059 | } | ||||
| 1060 | |||||
| 1061 | // Qualifier adjustments are modelled according to the user's request in | ||||
| 1062 | // the QualifiersMix check config. | ||||
| 1063 | LLVM_DEBUG(llvm::dbgs()do { if (::llvm::DebugFlag && ::llvm::isCurrentDebugType ("EasilySwappableParametersCheck")) { llvm::dbgs() << "--- approximateStdConv. Trying qualifier adjustment...\n" ; } } while (false) | ||||
| 1064 | << "--- approximateStdConv. Trying qualifier adjustment...\n")do { if (::llvm::DebugFlag && ::llvm::isCurrentDebugType ("EasilySwappableParametersCheck")) { llvm::dbgs() << "--- approximateStdConv. Trying qualifier adjustment...\n" ; } } while (false); | ||||
| 1065 | MixData QualConv = calculateMixability(Check, WorkType, To, Ctx, | ||||
| 1066 | ImplicitConversionModellingMode::None); | ||||
| 1067 | QualConv.sanitize(); | ||||
| 1068 | if (hasFlag(QualConv.Flags, MixFlags::Qualifiers)) { | ||||
| 1069 | LLVM_DEBUG(llvm::dbgs()do { if (::llvm::DebugFlag && ::llvm::isCurrentDebugType ("EasilySwappableParametersCheck")) { llvm::dbgs() << "<<< approximateStdConv. Qualifiers adjusted.\n" ; } } while (false) | ||||
| 1070 | << "<<< approximateStdConv. Qualifiers adjusted.\n")do { if (::llvm::DebugFlag && ::llvm::isCurrentDebugType ("EasilySwappableParametersCheck")) { llvm::dbgs() << "<<< approximateStdConv. Qualifiers adjusted.\n" ; } } while (false); | ||||
| 1071 | WorkType = To; | ||||
| 1072 | } | ||||
| 1073 | |||||
| 1074 | if (WorkType == To) { | ||||
| 1075 | LLVM_DEBUG(llvm::dbgs() << "<<< approximateStdConv. Reached 'To' type.\n")do { if (::llvm::DebugFlag && ::llvm::isCurrentDebugType ("EasilySwappableParametersCheck")) { llvm::dbgs() << "<<< approximateStdConv. Reached 'To' type.\n" ; } } while (false); | ||||
| 1076 | return {WorkType}; | ||||
| 1077 | } | ||||
| 1078 | |||||
| 1079 | LLVM_DEBUG(llvm::dbgs() << "<<< approximateStdConv. Did not reach 'To'.\n")do { if (::llvm::DebugFlag && ::llvm::isCurrentDebugType ("EasilySwappableParametersCheck")) { llvm::dbgs() << "<<< approximateStdConv. Did not reach 'To'.\n" ; } } while (false); | ||||
| 1080 | return {}; | ||||
| 1081 | } | ||||
| 1082 | |||||
| 1083 | namespace { | ||||
| 1084 | |||||
| 1085 | /// Helper class for storing possible user-defined conversion calls that | ||||
| 1086 | /// *could* take place in an implicit conversion, and selecting the one that | ||||
| 1087 | /// most likely *does*, if any. | ||||
| 1088 | class UserDefinedConversionSelector { | ||||
| 1089 | public: | ||||
| 1090 | /// The conversion associated with a conversion function, together with the | ||||
| 1091 | /// mixability flags of the conversion function's parameter or return type | ||||
| 1092 | /// to the rest of the sequence the selector is used in, and the sequence | ||||
| 1093 | /// that applied through the conversion itself. | ||||
| 1094 | struct PreparedConversion { | ||||
| 1095 | const CXXMethodDecl *ConversionFun; | ||||
| 1096 | MixFlags Flags; | ||||
| 1097 | ConversionSequence Seq; | ||||
| 1098 | |||||
| 1099 | PreparedConversion(const CXXMethodDecl *CMD, MixFlags F, | ||||
| 1100 | ConversionSequence S) | ||||
| 1101 | : ConversionFun(CMD), Flags(F), Seq(S) {} | ||||
| 1102 | }; | ||||
| 1103 | |||||
| 1104 | UserDefinedConversionSelector(const TheCheck &Check) : Check(Check) {} | ||||
| 1105 | |||||
| 1106 | /// Adds the conversion between the two types for the given function into | ||||
| 1107 | /// the possible implicit conversion set. FromType and ToType is either: | ||||
| 1108 | /// * the result of a standard sequence and a converting ctor parameter | ||||
| 1109 | /// * the return type of a conversion operator and the expected target of | ||||
| 1110 | /// an implicit conversion. | ||||
| 1111 | void addConversion(const CXXMethodDecl *ConvFun, QualType FromType, | ||||
| 1112 | QualType ToType) { | ||||
| 1113 | // Try to go from the FromType to the ToType with only a single implicit | ||||
| 1114 | // conversion, to see if the conversion function is applicable. | ||||
| 1115 | MixData Mix = calculateMixability( | ||||
| 1116 | Check, FromType, ToType, ConvFun->getASTContext(), | ||||
| 1117 | ImplicitConversionModellingMode::OneWaySingleStandardOnly); | ||||
| 1118 | Mix.sanitize(); | ||||
| 1119 | if (!Mix.indicatesMixability()) | ||||
| 1120 | return; | ||||
| 1121 | |||||
| 1122 | LLVM_DEBUG(llvm::dbgs() << "--- tryConversion. Found viable with flags: "do { if (::llvm::DebugFlag && ::llvm::isCurrentDebugType ("EasilySwappableParametersCheck")) { llvm::dbgs() << "--- tryConversion. Found viable with flags: " << formatMixFlags(Mix.Flags) << '\n'; } } while ( false) | ||||
| 1123 | << formatMixFlags(Mix.Flags) << '\n')do { if (::llvm::DebugFlag && ::llvm::isCurrentDebugType ("EasilySwappableParametersCheck")) { llvm::dbgs() << "--- tryConversion. Found viable with flags: " << formatMixFlags(Mix.Flags) << '\n'; } } while ( false); | ||||
| 1124 | FlaggedConversions.emplace_back(ConvFun, Mix.Flags, Mix.Conversion); | ||||
| 1125 | } | ||||
| 1126 | |||||
| 1127 | /// Selects the best conversion function that is applicable from the | ||||
| 1128 | /// prepared set of potential conversion functions taken. | ||||
| 1129 | std::optional<PreparedConversion> operator()() const { | ||||
| 1130 | if (FlaggedConversions.empty()) { | ||||
| 1131 | LLVM_DEBUG(llvm::dbgs() << "--- selectUserDefinedConv. Empty.\n")do { if (::llvm::DebugFlag && ::llvm::isCurrentDebugType ("EasilySwappableParametersCheck")) { llvm::dbgs() << "--- selectUserDefinedConv. Empty.\n" ; } } while (false); | ||||
| 1132 | return {}; | ||||
| 1133 | } | ||||
| 1134 | if (FlaggedConversions.size() == 1) { | ||||
| 1135 | LLVM_DEBUG(llvm::dbgs() << "--- selectUserDefinedConv. Single.\n")do { if (::llvm::DebugFlag && ::llvm::isCurrentDebugType ("EasilySwappableParametersCheck")) { llvm::dbgs() << "--- selectUserDefinedConv. Single.\n" ; } } while (false); | ||||
| 1136 | return FlaggedConversions.front(); | ||||
| 1137 | } | ||||
| 1138 | |||||
| 1139 | std::optional<PreparedConversion> BestConversion; | ||||
| 1140 | unsigned short HowManyGoodConversions = 0; | ||||
| 1141 | for (const auto &Prepared : FlaggedConversions) { | ||||
| 1142 | LLVM_DEBUG(llvm::dbgs() << "--- selectUserDefinedConv. Candidate flags: "do { if (::llvm::DebugFlag && ::llvm::isCurrentDebugType ("EasilySwappableParametersCheck")) { llvm::dbgs() << "--- selectUserDefinedConv. Candidate flags: " << formatMixFlags(Prepared.Flags) << '\n'; } } while (false) | ||||
| 1143 | << formatMixFlags(Prepared.Flags) << '\n')do { if (::llvm::DebugFlag && ::llvm::isCurrentDebugType ("EasilySwappableParametersCheck")) { llvm::dbgs() << "--- selectUserDefinedConv. Candidate flags: " << formatMixFlags(Prepared.Flags) << '\n'; } } while (false); | ||||
| 1144 | if (!BestConversion) { | ||||
| 1145 | BestConversion = Prepared; | ||||
| 1146 | ++HowManyGoodConversions; | ||||
| 1147 | continue; | ||||
| 1148 | } | ||||
| 1149 | |||||
| 1150 | bool BestConversionHasImplicit = | ||||
| 1151 | hasFlag(BestConversion->Flags, MixFlags::ImplicitConversion); | ||||
| 1152 | bool ThisConversionHasImplicit = | ||||
| 1153 | hasFlag(Prepared.Flags, MixFlags::ImplicitConversion); | ||||
| 1154 | if (!BestConversionHasImplicit && ThisConversionHasImplicit) | ||||
| 1155 | // This is a worse conversion, because a better one was found earlier. | ||||
| 1156 | continue; | ||||
| 1157 | |||||
| 1158 | if (BestConversionHasImplicit && !ThisConversionHasImplicit) { | ||||
| 1159 | // If the so far best selected conversion needs a previous implicit | ||||
| 1160 | // conversion to match the user-defined converting function, but this | ||||
| 1161 | // conversion does not, this is a better conversion, and we can throw | ||||
| 1162 | // away the previously selected conversion(s). | ||||
| 1163 | BestConversion = Prepared; | ||||
| 1164 | HowManyGoodConversions = 1; | ||||
| 1165 | continue; | ||||
| 1166 | } | ||||
| 1167 | |||||
| 1168 | if (BestConversionHasImplicit == ThisConversionHasImplicit) | ||||
| 1169 | // The current conversion is the same in term of goodness than the | ||||
| 1170 | // already selected one. | ||||
| 1171 | ++HowManyGoodConversions; | ||||
| 1172 | } | ||||
| 1173 | |||||
| 1174 | if (HowManyGoodConversions == 1) { | ||||
| 1175 | LLVM_DEBUG(llvm::dbgs()do { if (::llvm::DebugFlag && ::llvm::isCurrentDebugType ("EasilySwappableParametersCheck")) { llvm::dbgs() << "--- selectUserDefinedConv. Unique result. Flags: " << formatMixFlags(BestConversion->Flags) << '\n' ; } } while (false) | ||||
| 1176 | << "--- selectUserDefinedConv. Unique result. Flags: "do { if (::llvm::DebugFlag && ::llvm::isCurrentDebugType ("EasilySwappableParametersCheck")) { llvm::dbgs() << "--- selectUserDefinedConv. Unique result. Flags: " << formatMixFlags(BestConversion->Flags) << '\n' ; } } while (false) | ||||
| 1177 | << formatMixFlags(BestConversion->Flags) << '\n')do { if (::llvm::DebugFlag && ::llvm::isCurrentDebugType ("EasilySwappableParametersCheck")) { llvm::dbgs() << "--- selectUserDefinedConv. Unique result. Flags: " << formatMixFlags(BestConversion->Flags) << '\n' ; } } while (false); | ||||
| 1178 | return BestConversion; | ||||
| 1179 | } | ||||
| 1180 | |||||
| 1181 | LLVM_DEBUG(llvm::dbgs()do { if (::llvm::DebugFlag && ::llvm::isCurrentDebugType ("EasilySwappableParametersCheck")) { llvm::dbgs() << "--- selectUserDefinedConv. No, or ambiguous.\n" ; } } while (false) | ||||
| 1182 | << "--- selectUserDefinedConv. No, or ambiguous.\n")do { if (::llvm::DebugFlag && ::llvm::isCurrentDebugType ("EasilySwappableParametersCheck")) { llvm::dbgs() << "--- selectUserDefinedConv. No, or ambiguous.\n" ; } } while (false); | ||||
| 1183 | return {}; | ||||
| 1184 | } | ||||
| 1185 | |||||
| 1186 | private: | ||||
| 1187 | llvm::SmallVector<PreparedConversion, 2> FlaggedConversions; | ||||
| 1188 | const TheCheck &Check; | ||||
| 1189 | }; | ||||
| 1190 | |||||
| 1191 | } // namespace | ||||
| 1192 | |||||
| 1193 | static std::optional<ConversionSequence> | ||||
| 1194 | tryConversionOperators(const TheCheck &Check, const CXXRecordDecl *RD, | ||||
| 1195 | QualType ToType) { | ||||
| 1196 | if (!RD || !RD->isCompleteDefinition()) | ||||
| 1197 | return {}; | ||||
| 1198 | RD = RD->getDefinition(); | ||||
| 1199 | |||||
| 1200 | LLVM_DEBUG(llvm::dbgs() << ">>> tryConversionOperators: " << RD->getName()do { if (::llvm::DebugFlag && ::llvm::isCurrentDebugType ("EasilySwappableParametersCheck")) { llvm::dbgs() << ">>> tryConversionOperators: " << RD->getName() << " to:\n"; ToType.dump(llvm ::dbgs(), RD->getASTContext()); llvm::dbgs() << '\n' ;; } } while (false) | ||||
| 1201 | << " to:\n";do { if (::llvm::DebugFlag && ::llvm::isCurrentDebugType ("EasilySwappableParametersCheck")) { llvm::dbgs() << ">>> tryConversionOperators: " << RD->getName() << " to:\n"; ToType.dump(llvm ::dbgs(), RD->getASTContext()); llvm::dbgs() << '\n' ;; } } while (false) | ||||
| 1202 | ToType.dump(llvm::dbgs(), RD->getASTContext());do { if (::llvm::DebugFlag && ::llvm::isCurrentDebugType ("EasilySwappableParametersCheck")) { llvm::dbgs() << ">>> tryConversionOperators: " << RD->getName() << " to:\n"; ToType.dump(llvm ::dbgs(), RD->getASTContext()); llvm::dbgs() << '\n' ;; } } while (false) | ||||
| 1203 | llvm::dbgs() << '\n';)do { if (::llvm::DebugFlag && ::llvm::isCurrentDebugType ("EasilySwappableParametersCheck")) { llvm::dbgs() << ">>> tryConversionOperators: " << RD->getName() << " to:\n"; ToType.dump(llvm ::dbgs(), RD->getASTContext()); llvm::dbgs() << '\n' ;; } } while (false); | ||||
| 1204 | |||||
| 1205 | UserDefinedConversionSelector ConversionSet{Check}; | ||||
| 1206 | |||||
| 1207 | for (const NamedDecl *Method : RD->getVisibleConversionFunctions()) { | ||||
| 1208 | const auto *Con = dyn_cast<CXXConversionDecl>(Method); | ||||
| 1209 | if (!Con || Con->isExplicit()) | ||||
| 1210 | continue; | ||||
| 1211 | LLVM_DEBUG(llvm::dbgs() << "--- tryConversionOperators. Trying:\n";do { if (::llvm::DebugFlag && ::llvm::isCurrentDebugType ("EasilySwappableParametersCheck")) { llvm::dbgs() << "--- tryConversionOperators. Trying:\n" ; Con->dump(llvm::dbgs()); llvm::dbgs() << '\n';; } } while (false) | ||||
| 1212 | Con->dump(llvm::dbgs()); llvm::dbgs() << '\n';)do { if (::llvm::DebugFlag && ::llvm::isCurrentDebugType ("EasilySwappableParametersCheck")) { llvm::dbgs() << "--- tryConversionOperators. Trying:\n" ; Con->dump(llvm::dbgs()); llvm::dbgs() << '\n';; } } while (false); | ||||
| 1213 | |||||
| 1214 | // Try to go from the result of conversion operator to the expected type, | ||||
| 1215 | // without calculating another user-defined conversion. | ||||
| 1216 | ConversionSet.addConversion(Con, Con->getConversionType(), ToType); | ||||
| 1217 | } | ||||
| 1218 | |||||
| 1219 | if (std::optional<UserDefinedConversionSelector::PreparedConversion> | ||||
| 1220 | SelectedConversion = ConversionSet()) { | ||||
| 1221 | QualType RecordType{RD->getTypeForDecl(), 0}; | ||||
| 1222 | |||||
| 1223 | ConversionSequence Result{RecordType, ToType}; | ||||
| 1224 | // The conversion from the operator call's return type to ToType was | ||||
| 1225 | // modelled as a "pre-conversion" in the operator call, but it is the | ||||
| 1226 | // "post-conversion" from the point of view of the original conversion | ||||
| 1227 | // we are modelling. | ||||
| 1228 | Result.AfterSecondStandard = SelectedConversion->Seq.AfterFirstStandard; | ||||
| 1229 | |||||
| 1230 | ConversionSequence::UserDefinedConversionOperator ConvOp; | ||||
| 1231 | ConvOp.Fun = cast<CXXConversionDecl>(SelectedConversion->ConversionFun); | ||||
| 1232 | ConvOp.UserDefinedType = RecordType; | ||||
| 1233 | ConvOp.ConversionOperatorResultType = ConvOp.Fun->getConversionType(); | ||||
| 1234 | Result.setConversion(ConvOp); | ||||
| 1235 | |||||
| 1236 | LLVM_DEBUG(llvm::dbgs() << "<<< tryConversionOperators. Found result.\n")do { if (::llvm::DebugFlag && ::llvm::isCurrentDebugType ("EasilySwappableParametersCheck")) { llvm::dbgs() << "<<< tryConversionOperators. Found result.\n" ; } } while (false); | ||||
| 1237 | return Result; | ||||
| 1238 | } | ||||
| 1239 | |||||
| 1240 | LLVM_DEBUG(llvm::dbgs() << "<<< tryConversionOperators. No conversion.\n")do { if (::llvm::DebugFlag && ::llvm::isCurrentDebugType ("EasilySwappableParametersCheck")) { llvm::dbgs() << "<<< tryConversionOperators. No conversion.\n" ; } } while (false); | ||||
| 1241 | return {}; | ||||
| 1242 | } | ||||
| 1243 | |||||
| 1244 | static std::optional<ConversionSequence> | ||||
| 1245 | tryConvertingConstructors(const TheCheck &Check, QualType FromType, | ||||
| 1246 | const CXXRecordDecl *RD) { | ||||
| 1247 | if (!RD || !RD->isCompleteDefinition()) | ||||
| 1248 | return {}; | ||||
| 1249 | RD = RD->getDefinition(); | ||||
| 1250 | |||||
| 1251 | LLVM_DEBUG(llvm::dbgs() << ">>> tryConveringConstructors: " << RD->getName()do { if (::llvm::DebugFlag && ::llvm::isCurrentDebugType ("EasilySwappableParametersCheck")) { llvm::dbgs() << ">>> tryConveringConstructors: " << RD->getName() << " from:\n"; FromType.dump (llvm::dbgs(), RD->getASTContext()); llvm::dbgs() << '\n';; } } while (false) | ||||
| 1252 | << " from:\n";do { if (::llvm::DebugFlag && ::llvm::isCurrentDebugType ("EasilySwappableParametersCheck")) { llvm::dbgs() << ">>> tryConveringConstructors: " << RD->getName() << " from:\n"; FromType.dump (llvm::dbgs(), RD->getASTContext()); llvm::dbgs() << '\n';; } } while (false) | ||||
| 1253 | FromType.dump(llvm::dbgs(), RD->getASTContext());do { if (::llvm::DebugFlag && ::llvm::isCurrentDebugType ("EasilySwappableParametersCheck")) { llvm::dbgs() << ">>> tryConveringConstructors: " << RD->getName() << " from:\n"; FromType.dump (llvm::dbgs(), RD->getASTContext()); llvm::dbgs() << '\n';; } } while (false) | ||||
| 1254 | llvm::dbgs() << '\n';)do { if (::llvm::DebugFlag && ::llvm::isCurrentDebugType ("EasilySwappableParametersCheck")) { llvm::dbgs() << ">>> tryConveringConstructors: " << RD->getName() << " from:\n"; FromType.dump (llvm::dbgs(), RD->getASTContext()); llvm::dbgs() << '\n';; } } while (false); | ||||
| 1255 | |||||
| 1256 | UserDefinedConversionSelector ConversionSet{Check}; | ||||
| 1257 | |||||
| 1258 | for (const CXXConstructorDecl *Con : RD->ctors()) { | ||||
| 1259 | if (Con->isCopyOrMoveConstructor() || | ||||
| 1260 | !Con->isConvertingConstructor(/* AllowExplicit =*/false)) | ||||
| 1261 | continue; | ||||
| 1262 | LLVM_DEBUG(llvm::dbgs() << "--- tryConvertingConstructors. Trying:\n";do { if (::llvm::DebugFlag && ::llvm::isCurrentDebugType ("EasilySwappableParametersCheck")) { llvm::dbgs() << "--- tryConvertingConstructors. Trying:\n" ; Con->dump(llvm::dbgs()); llvm::dbgs() << '\n';; } } while (false) | ||||
| 1263 | Con->dump(llvm::dbgs()); llvm::dbgs() << '\n';)do { if (::llvm::DebugFlag && ::llvm::isCurrentDebugType ("EasilySwappableParametersCheck")) { llvm::dbgs() << "--- tryConvertingConstructors. Trying:\n" ; Con->dump(llvm::dbgs()); llvm::dbgs() << '\n';; } } while (false); | ||||
| 1264 | |||||
| 1265 | // Try to go from the original FromType to the converting constructor's | ||||
| 1266 | // parameter type without another user-defined conversion. | ||||
| 1267 | ConversionSet.addConversion(Con, FromType, Con->getParamDecl(0)->getType()); | ||||
| 1268 | } | ||||
| 1269 | |||||
| 1270 | if (std::optional<UserDefinedConversionSelector::PreparedConversion> | ||||
| 1271 | SelectedConversion = ConversionSet()) { | ||||
| 1272 | QualType RecordType{RD->getTypeForDecl(), 0}; | ||||
| 1273 | |||||
| 1274 | ConversionSequence Result{FromType, RecordType}; | ||||
| 1275 | Result.AfterFirstStandard = SelectedConversion->Seq.AfterFirstStandard; | ||||
| 1276 | |||||
| 1277 | ConversionSequence::UserDefinedConvertingConstructor Ctor; | ||||
| 1278 | Ctor.Fun = cast<CXXConstructorDecl>(SelectedConversion->ConversionFun); | ||||
| 1279 | Ctor.ConstructorParameterType = Ctor.Fun->getParamDecl(0)->getType(); | ||||
| 1280 | Ctor.UserDefinedType = RecordType; | ||||
| 1281 | Result.setConversion(Ctor); | ||||
| 1282 | |||||
| 1283 | LLVM_DEBUG(llvm::dbgs()do { if (::llvm::DebugFlag && ::llvm::isCurrentDebugType ("EasilySwappableParametersCheck")) { llvm::dbgs() << "<<< tryConvertingConstructors. Found result.\n" ; } } while (false) | ||||
| 1284 | << "<<< tryConvertingConstructors. Found result.\n")do { if (::llvm::DebugFlag && ::llvm::isCurrentDebugType ("EasilySwappableParametersCheck")) { llvm::dbgs() << "<<< tryConvertingConstructors. Found result.\n" ; } } while (false); | ||||
| 1285 | return Result; | ||||
| 1286 | } | ||||
| 1287 | |||||
| 1288 | LLVM_DEBUG(llvm::dbgs() << "<<< tryConvertingConstructors. No conversion.\n")do { if (::llvm::DebugFlag && ::llvm::isCurrentDebugType ("EasilySwappableParametersCheck")) { llvm::dbgs() << "<<< tryConvertingConstructors. No conversion.\n" ; } } while (false); | ||||
| 1289 | return {}; | ||||
| 1290 | } | ||||
| 1291 | |||||
| 1292 | /// Returns whether an expression of LType can be used in an RType context, as | ||||
| 1293 | /// per the implicit conversion rules. | ||||
| 1294 | /// | ||||
| 1295 | /// Note: the result of this operation, unlike that of calculateMixability, is | ||||
| 1296 | /// **NOT** symmetric. | ||||
| 1297 | static MixData | ||||
| 1298 | approximateImplicitConversion(const TheCheck &Check, QualType LType, | ||||
| 1299 | QualType RType, const ASTContext &Ctx, | ||||
| 1300 | ImplicitConversionModellingMode ImplicitMode) { | ||||
| 1301 | LLVM_DEBUG(llvm::dbgs() << ">>> approximateImplicitConversion for LType:\n";do { if (::llvm::DebugFlag && ::llvm::isCurrentDebugType ("EasilySwappableParametersCheck")) { llvm::dbgs() << ">>> approximateImplicitConversion for LType:\n" ; LType.dump(llvm::dbgs(), Ctx); llvm::dbgs() << "\nand RType:\n" ; RType.dump(llvm::dbgs(), Ctx); llvm::dbgs() << "\nimplicit mode: " ; switch (ImplicitMode) { case ImplicitConversionModellingMode ::None: llvm::dbgs() << "None"; break; case ImplicitConversionModellingMode ::All: llvm::dbgs() << "All"; break; case ImplicitConversionModellingMode ::OneWaySingleStandardOnly: llvm::dbgs() << "OneWay, Single, STD Only" ; break; } llvm::dbgs() << '\n';; } } while (false) | ||||
| 1302 | LType.dump(llvm::dbgs(), Ctx); llvm::dbgs() << "\nand RType:\n";do { if (::llvm::DebugFlag && ::llvm::isCurrentDebugType ("EasilySwappableParametersCheck")) { llvm::dbgs() << ">>> approximateImplicitConversion for LType:\n" ; LType.dump(llvm::dbgs(), Ctx); llvm::dbgs() << "\nand RType:\n" ; RType.dump(llvm::dbgs(), Ctx); llvm::dbgs() << "\nimplicit mode: " ; switch (ImplicitMode) { case ImplicitConversionModellingMode ::None: llvm::dbgs() << "None"; break; case ImplicitConversionModellingMode ::All: llvm::dbgs() << "All"; break; case ImplicitConversionModellingMode ::OneWaySingleStandardOnly: llvm::dbgs() << "OneWay, Single, STD Only" ; break; } llvm::dbgs() << '\n';; } } while (false) | ||||
| 1303 | RType.dump(llvm::dbgs(), Ctx);do { if (::llvm::DebugFlag && ::llvm::isCurrentDebugType ("EasilySwappableParametersCheck")) { llvm::dbgs() << ">>> approximateImplicitConversion for LType:\n" ; LType.dump(llvm::dbgs(), Ctx); llvm::dbgs() << "\nand RType:\n" ; RType.dump(llvm::dbgs(), Ctx); llvm::dbgs() << "\nimplicit mode: " ; switch (ImplicitMode) { case ImplicitConversionModellingMode ::None: llvm::dbgs() << "None"; break; case ImplicitConversionModellingMode ::All: llvm::dbgs() << "All"; break; case ImplicitConversionModellingMode ::OneWaySingleStandardOnly: llvm::dbgs() << "OneWay, Single, STD Only" ; break; } llvm::dbgs() << '\n';; } } while (false) | ||||
| 1304 | llvm::dbgs() << "\nimplicit mode: "; switch (ImplicitMode) {do { if (::llvm::DebugFlag && ::llvm::isCurrentDebugType ("EasilySwappableParametersCheck")) { llvm::dbgs() << ">>> approximateImplicitConversion for LType:\n" ; LType.dump(llvm::dbgs(), Ctx); llvm::dbgs() << "\nand RType:\n" ; RType.dump(llvm::dbgs(), Ctx); llvm::dbgs() << "\nimplicit mode: " ; switch (ImplicitMode) { case ImplicitConversionModellingMode ::None: llvm::dbgs() << "None"; break; case ImplicitConversionModellingMode ::All: llvm::dbgs() << "All"; break; case ImplicitConversionModellingMode ::OneWaySingleStandardOnly: llvm::dbgs() << "OneWay, Single, STD Only" ; break; } llvm::dbgs() << '\n';; } } while (false) | ||||
| 1305 | case ImplicitConversionModellingMode::None:do { if (::llvm::DebugFlag && ::llvm::isCurrentDebugType ("EasilySwappableParametersCheck")) { llvm::dbgs() << ">>> approximateImplicitConversion for LType:\n" ; LType.dump(llvm::dbgs(), Ctx); llvm::dbgs() << "\nand RType:\n" ; RType.dump(llvm::dbgs(), Ctx); llvm::dbgs() << "\nimplicit mode: " ; switch (ImplicitMode) { case ImplicitConversionModellingMode ::None: llvm::dbgs() << "None"; break; case ImplicitConversionModellingMode ::All: llvm::dbgs() << "All"; break; case ImplicitConversionModellingMode ::OneWaySingleStandardOnly: llvm::dbgs() << "OneWay, Single, STD Only" ; break; } llvm::dbgs() << '\n';; } } while (false) | ||||
| 1306 | llvm::dbgs() << "None";do { if (::llvm::DebugFlag && ::llvm::isCurrentDebugType ("EasilySwappableParametersCheck")) { llvm::dbgs() << ">>> approximateImplicitConversion for LType:\n" ; LType.dump(llvm::dbgs(), Ctx); llvm::dbgs() << "\nand RType:\n" ; RType.dump(llvm::dbgs(), Ctx); llvm::dbgs() << "\nimplicit mode: " ; switch (ImplicitMode) { case ImplicitConversionModellingMode ::None: llvm::dbgs() << "None"; break; case ImplicitConversionModellingMode ::All: llvm::dbgs() << "All"; break; case ImplicitConversionModellingMode ::OneWaySingleStandardOnly: llvm::dbgs() << "OneWay, Single, STD Only" ; break; } llvm::dbgs() << '\n';; } } while (false) | ||||
| 1307 | break;do { if (::llvm::DebugFlag && ::llvm::isCurrentDebugType ("EasilySwappableParametersCheck")) { llvm::dbgs() << ">>> approximateImplicitConversion for LType:\n" ; LType.dump(llvm::dbgs(), Ctx); llvm::dbgs() << "\nand RType:\n" ; RType.dump(llvm::dbgs(), Ctx); llvm::dbgs() << "\nimplicit mode: " ; switch (ImplicitMode) { case ImplicitConversionModellingMode ::None: llvm::dbgs() << "None"; break; case ImplicitConversionModellingMode ::All: llvm::dbgs() << "All"; break; case ImplicitConversionModellingMode ::OneWaySingleStandardOnly: llvm::dbgs() << "OneWay, Single, STD Only" ; break; } llvm::dbgs() << '\n';; } } while (false) | ||||
| 1308 | case ImplicitConversionModellingMode::All:do { if (::llvm::DebugFlag && ::llvm::isCurrentDebugType ("EasilySwappableParametersCheck")) { llvm::dbgs() << ">>> approximateImplicitConversion for LType:\n" ; LType.dump(llvm::dbgs(), Ctx); llvm::dbgs() << "\nand RType:\n" ; RType.dump(llvm::dbgs(), Ctx); llvm::dbgs() << "\nimplicit mode: " ; switch (ImplicitMode) { case ImplicitConversionModellingMode ::None: llvm::dbgs() << "None"; break; case ImplicitConversionModellingMode ::All: llvm::dbgs() << "All"; break; case ImplicitConversionModellingMode ::OneWaySingleStandardOnly: llvm::dbgs() << "OneWay, Single, STD Only" ; break; } llvm::dbgs() << '\n';; } } while (false) | ||||
| 1309 | llvm::dbgs() << "All";do { if (::llvm::DebugFlag && ::llvm::isCurrentDebugType ("EasilySwappableParametersCheck")) { llvm::dbgs() << ">>> approximateImplicitConversion for LType:\n" ; LType.dump(llvm::dbgs(), Ctx); llvm::dbgs() << "\nand RType:\n" ; RType.dump(llvm::dbgs(), Ctx); llvm::dbgs() << "\nimplicit mode: " ; switch (ImplicitMode) { case ImplicitConversionModellingMode ::None: llvm::dbgs() << "None"; break; case ImplicitConversionModellingMode ::All: llvm::dbgs() << "All"; break; case ImplicitConversionModellingMode ::OneWaySingleStandardOnly: llvm::dbgs() << "OneWay, Single, STD Only" ; break; } llvm::dbgs() << '\n';; } } while (false) | ||||
| 1310 | break;do { if (::llvm::DebugFlag && ::llvm::isCurrentDebugType ("EasilySwappableParametersCheck")) { llvm::dbgs() << ">>> approximateImplicitConversion for LType:\n" ; LType.dump(llvm::dbgs(), Ctx); llvm::dbgs() << "\nand RType:\n" ; RType.dump(llvm::dbgs(), Ctx); llvm::dbgs() << "\nimplicit mode: " ; switch (ImplicitMode) { case ImplicitConversionModellingMode ::None: llvm::dbgs() << "None"; break; case ImplicitConversionModellingMode ::All: llvm::dbgs() << "All"; break; case ImplicitConversionModellingMode ::OneWaySingleStandardOnly: llvm::dbgs() << "OneWay, Single, STD Only" ; break; } llvm::dbgs() << '\n';; } } while (false) | ||||
| 1311 | case ImplicitConversionModellingMode::OneWaySingleStandardOnly:do { if (::llvm::DebugFlag && ::llvm::isCurrentDebugType ("EasilySwappableParametersCheck")) { llvm::dbgs() << ">>> approximateImplicitConversion for LType:\n" ; LType.dump(llvm::dbgs(), Ctx); llvm::dbgs() << "\nand RType:\n" ; RType.dump(llvm::dbgs(), Ctx); llvm::dbgs() << "\nimplicit mode: " ; switch (ImplicitMode) { case ImplicitConversionModellingMode ::None: llvm::dbgs() << "None"; break; case ImplicitConversionModellingMode ::All: llvm::dbgs() << "All"; break; case ImplicitConversionModellingMode ::OneWaySingleStandardOnly: llvm::dbgs() << "OneWay, Single, STD Only" ; break; } llvm::dbgs() << '\n';; } } while (false) | ||||
| 1312 | llvm::dbgs() << "OneWay, Single, STD Only";do { if (::llvm::DebugFlag && ::llvm::isCurrentDebugType ("EasilySwappableParametersCheck")) { llvm::dbgs() << ">>> approximateImplicitConversion for LType:\n" ; LType.dump(llvm::dbgs(), Ctx); llvm::dbgs() << "\nand RType:\n" ; RType.dump(llvm::dbgs(), Ctx); llvm::dbgs() << "\nimplicit mode: " ; switch (ImplicitMode) { case ImplicitConversionModellingMode ::None: llvm::dbgs() << "None"; break; case ImplicitConversionModellingMode ::All: llvm::dbgs() << "All"; break; case ImplicitConversionModellingMode ::OneWaySingleStandardOnly: llvm::dbgs() << "OneWay, Single, STD Only" ; break; } llvm::dbgs() << '\n';; } } while (false) | ||||
| 1313 | break;do { if (::llvm::DebugFlag && ::llvm::isCurrentDebugType ("EasilySwappableParametersCheck")) { llvm::dbgs() << ">>> approximateImplicitConversion for LType:\n" ; LType.dump(llvm::dbgs(), Ctx); llvm::dbgs() << "\nand RType:\n" ; RType.dump(llvm::dbgs(), Ctx); llvm::dbgs() << "\nimplicit mode: " ; switch (ImplicitMode) { case ImplicitConversionModellingMode ::None: llvm::dbgs() << "None"; break; case ImplicitConversionModellingMode ::All: llvm::dbgs() << "All"; break; case ImplicitConversionModellingMode ::OneWaySingleStandardOnly: llvm::dbgs() << "OneWay, Single, STD Only" ; break; } llvm::dbgs() << '\n';; } } while (false) | ||||
| 1314 | } llvm::dbgs() << '\n';)do { if (::llvm::DebugFlag && ::llvm::isCurrentDebugType ("EasilySwappableParametersCheck")) { llvm::dbgs() << ">>> approximateImplicitConversion for LType:\n" ; LType.dump(llvm::dbgs(), Ctx); llvm::dbgs() << "\nand RType:\n" ; RType.dump(llvm::dbgs(), Ctx); llvm::dbgs() << "\nimplicit mode: " ; switch (ImplicitMode) { case ImplicitConversionModellingMode ::None: llvm::dbgs() << "None"; break; case ImplicitConversionModellingMode ::All: llvm::dbgs() << "All"; break; case ImplicitConversionModellingMode ::OneWaySingleStandardOnly: llvm::dbgs() << "OneWay, Single, STD Only" ; break; } llvm::dbgs() << '\n';; } } while (false); | ||||
| 1315 | if (LType == RType) | ||||
| 1316 | return {MixFlags::Trivial, LType}; | ||||
| 1317 | |||||
| 1318 | // An implicit conversion sequence consists of the following, in order: | ||||
| 1319 | // * Maybe standard conversion sequence. | ||||
| 1320 | // * Maybe user-defined conversion. | ||||
| 1321 | // * Maybe standard conversion sequence. | ||||
| 1322 | ConversionSequence ImplicitSeq{LType, RType}; | ||||
| 1323 | QualType WorkType = LType; | ||||
| 1324 | |||||
| 1325 | std::optional<QualType> AfterFirstStdConv = | ||||
| 1326 | approximateStandardConversionSequence(Check, LType, RType, Ctx); | ||||
| 1327 | if (AfterFirstStdConv) { | ||||
| 1328 | LLVM_DEBUG(llvm::dbgs() << "--- approximateImplicitConversion. Standard "do { if (::llvm::DebugFlag && ::llvm::isCurrentDebugType ("EasilySwappableParametersCheck")) { llvm::dbgs() << "--- approximateImplicitConversion. Standard " "Pre-Conversion found!\n"; } } while (false) | ||||
| 1329 | "Pre-Conversion found!\n")do { if (::llvm::DebugFlag && ::llvm::isCurrentDebugType ("EasilySwappableParametersCheck")) { llvm::dbgs() << "--- approximateImplicitConversion. Standard " "Pre-Conversion found!\n"; } } while (false); | ||||
| 1330 | ImplicitSeq.AfterFirstStandard = *AfterFirstStdConv; | ||||
| 1331 | WorkType = ImplicitSeq.AfterFirstStandard; | ||||
| 1332 | } | ||||
| 1333 | |||||
| 1334 | if (ImplicitMode == ImplicitConversionModellingMode::OneWaySingleStandardOnly) | ||||
| 1335 | // If the caller only requested modelling of a standard conversion, bail. | ||||
| 1336 | return {ImplicitSeq.AfterFirstStandard.isNull() | ||||
| 1337 | ? MixFlags::None | ||||
| 1338 | : MixFlags::ImplicitConversion, | ||||
| 1339 | ImplicitSeq}; | ||||
| 1340 | |||||
| 1341 | if (Ctx.getLangOpts().CPlusPlus) { | ||||
| 1342 | bool FoundConversionOperator = false, FoundConvertingCtor = false; | ||||
| 1343 | |||||
| 1344 | if (const auto *LRD = WorkType->getAsCXXRecordDecl()) { | ||||
| 1345 | std::optional<ConversionSequence> ConversionOperatorResult = | ||||
| 1346 | tryConversionOperators(Check, LRD, RType); | ||||
| 1347 | if (ConversionOperatorResult) { | ||||
| 1348 | LLVM_DEBUG(llvm::dbgs() << "--- approximateImplicitConversion. Found "do { if (::llvm::DebugFlag && ::llvm::isCurrentDebugType ("EasilySwappableParametersCheck")) { llvm::dbgs() << "--- approximateImplicitConversion. Found " "conversion operator.\n"; } } while (false) | ||||
| 1349 | "conversion operator.\n")do { if (::llvm::DebugFlag && ::llvm::isCurrentDebugType ("EasilySwappableParametersCheck")) { llvm::dbgs() << "--- approximateImplicitConversion. Found " "conversion operator.\n"; } } while (false); | ||||
| 1350 | ImplicitSeq.update(*ConversionOperatorResult); | ||||
| 1351 | WorkType = ImplicitSeq.getTypeAfterUserDefinedConversion(); | ||||
| 1352 | FoundConversionOperator = true; | ||||
| 1353 | } | ||||
| 1354 | } | ||||
| 1355 | |||||
| 1356 | if (const auto *RRD = RType->getAsCXXRecordDecl()) { | ||||
| 1357 | // Use the original "LType" here, and not WorkType, because the | ||||
| 1358 | // conversion to the converting constructors' parameters will be | ||||
| 1359 | // modelled in the recursive call. | ||||
| 1360 | std::optional<ConversionSequence> ConvCtorResult = | ||||
| 1361 | tryConvertingConstructors(Check, LType, RRD); | ||||
| 1362 | if (ConvCtorResult) { | ||||
| 1363 | LLVM_DEBUG(llvm::dbgs() << "--- approximateImplicitConversion. Found "do { if (::llvm::DebugFlag && ::llvm::isCurrentDebugType ("EasilySwappableParametersCheck")) { llvm::dbgs() << "--- approximateImplicitConversion. Found " "converting constructor.\n"; } } while (false) | ||||
| 1364 | "converting constructor.\n")do { if (::llvm::DebugFlag && ::llvm::isCurrentDebugType ("EasilySwappableParametersCheck")) { llvm::dbgs() << "--- approximateImplicitConversion. Found " "converting constructor.\n"; } } while (false); | ||||
| 1365 | ImplicitSeq.update(*ConvCtorResult); | ||||
| 1366 | WorkType = ImplicitSeq.getTypeAfterUserDefinedConversion(); | ||||
| 1367 | FoundConvertingCtor = true; | ||||
| 1368 | } | ||||
| 1369 | } | ||||
| 1370 | |||||
| 1371 | if (FoundConversionOperator && FoundConvertingCtor) { | ||||
| 1372 | // If both an operator and a ctor matches, the sequence is ambiguous. | ||||
| 1373 | LLVM_DEBUG(llvm::dbgs()do { if (::llvm::DebugFlag && ::llvm::isCurrentDebugType ("EasilySwappableParametersCheck")) { llvm::dbgs() << "<<< approximateImplicitConversion. Found both " "user-defined conversion kinds in the same sequence!\n"; } } while (false) | ||||
| 1374 | << "<<< approximateImplicitConversion. Found both "do { if (::llvm::DebugFlag && ::llvm::isCurrentDebugType ("EasilySwappableParametersCheck")) { llvm::dbgs() << "<<< approximateImplicitConversion. Found both " "user-defined conversion kinds in the same sequence!\n"; } } while (false) | ||||
| 1375 | "user-defined conversion kinds in the same sequence!\n")do { if (::llvm::DebugFlag && ::llvm::isCurrentDebugType ("EasilySwappableParametersCheck")) { llvm::dbgs() << "<<< approximateImplicitConversion. Found both " "user-defined conversion kinds in the same sequence!\n"; } } while (false); | ||||
| 1376 | return {MixFlags::None}; | ||||
| 1377 | } | ||||
| 1378 | } | ||||
| 1379 | |||||
| 1380 | // After the potential user-defined conversion, another standard conversion | ||||
| 1381 | // sequence might exist. | ||||
| 1382 | LLVM_DEBUG(do { if (::llvm::DebugFlag && ::llvm::isCurrentDebugType ("EasilySwappableParametersCheck")) { llvm::dbgs() << "--- approximateImplicitConversion. Try to find post-conversion.\n" ; } } while (false) | ||||
| 1383 | llvm::dbgs()do { if (::llvm::DebugFlag && ::llvm::isCurrentDebugType ("EasilySwappableParametersCheck")) { llvm::dbgs() << "--- approximateImplicitConversion. Try to find post-conversion.\n" ; } } while (false) | ||||
| 1384 | << "--- approximateImplicitConversion. Try to find post-conversion.\n")do { if (::llvm::DebugFlag && ::llvm::isCurrentDebugType ("EasilySwappableParametersCheck")) { llvm::dbgs() << "--- approximateImplicitConversion. Try to find post-conversion.\n" ; } } while (false); | ||||
| 1385 | MixData SecondStdConv = approximateImplicitConversion( | ||||
| 1386 | Check, WorkType, RType, Ctx, | ||||
| 1387 | ImplicitConversionModellingMode::OneWaySingleStandardOnly); | ||||
| 1388 | if (SecondStdConv.indicatesMixability()) { | ||||
| 1389 | LLVM_DEBUG(llvm::dbgs() << "--- approximateImplicitConversion. Standard "do { if (::llvm::DebugFlag && ::llvm::isCurrentDebugType ("EasilySwappableParametersCheck")) { llvm::dbgs() << "--- approximateImplicitConversion. Standard " "Post-Conversion found!\n"; } } while (false) | ||||
| 1390 | "Post-Conversion found!\n")do { if (::llvm::DebugFlag && ::llvm::isCurrentDebugType ("EasilySwappableParametersCheck")) { llvm::dbgs() << "--- approximateImplicitConversion. Standard " "Post-Conversion found!\n"; } } while (false); | ||||
| 1391 | |||||
| 1392 | // The single-step modelling puts the modelled conversion into the "PreStd" | ||||
| 1393 | // variable in the recursive call, but from the PoV of this function, it is | ||||
| 1394 | // the post-conversion. | ||||
| 1395 | ImplicitSeq.AfterSecondStandard = | ||||
| 1396 | SecondStdConv.Conversion.AfterFirstStandard; | ||||
| 1397 | WorkType = ImplicitSeq.AfterSecondStandard; | ||||
| 1398 | } | ||||
| 1399 | |||||
| 1400 | if (ImplicitSeq) { | ||||
| 1401 | LLVM_DEBUG(llvm::dbgs()do { if (::llvm::DebugFlag && ::llvm::isCurrentDebugType ("EasilySwappableParametersCheck")) { llvm::dbgs() << "<<< approximateImplicitConversion. Found a conversion.\n" ; } } while (false) | ||||
| 1402 | << "<<< approximateImplicitConversion. Found a conversion.\n")do { if (::llvm::DebugFlag && ::llvm::isCurrentDebugType ("EasilySwappableParametersCheck")) { llvm::dbgs() << "<<< approximateImplicitConversion. Found a conversion.\n" ; } } while (false); | ||||
| 1403 | return {MixFlags::ImplicitConversion, ImplicitSeq}; | ||||
| 1404 | } | ||||
| 1405 | |||||
| 1406 | LLVM_DEBUG(do { if (::llvm::DebugFlag && ::llvm::isCurrentDebugType ("EasilySwappableParametersCheck")) { llvm::dbgs() << "<<< approximateImplicitConversion. No match found.\n" ; } } while (false) | ||||
| 1407 | llvm::dbgs() << "<<< approximateImplicitConversion. No match found.\n")do { if (::llvm::DebugFlag && ::llvm::isCurrentDebugType ("EasilySwappableParametersCheck")) { llvm::dbgs() << "<<< approximateImplicitConversion. No match found.\n" ; } } while (false); | ||||
| 1408 | return {MixFlags::None}; | ||||
| 1409 | } | ||||
| 1410 | |||||
| 1411 | static MixableParameterRange modelMixingRange( | ||||
| 1412 | const TheCheck &Check, const FunctionDecl *FD, std::size_t StartIndex, | ||||
| 1413 | const filter::SimilarlyUsedParameterPairSuppressor &UsageBasedSuppressor) { | ||||
| 1414 | std::size_t NumParams = FD->getNumParams(); | ||||
| 1415 | assert(StartIndex < NumParams && "out of bounds for start")(static_cast <bool> (StartIndex < NumParams && "out of bounds for start") ? void (0) : __assert_fail ("StartIndex < NumParams && \"out of bounds for start\"" , "clang-tools-extra/clang-tidy/bugprone/EasilySwappableParametersCheck.cpp" , 1415, __extension__ __PRETTY_FUNCTION__)); | ||||
| 1416 | const ASTContext &Ctx = FD->getASTContext(); | ||||
| 1417 | |||||
| 1418 | MixableParameterRange Ret; | ||||
| 1419 | // A parameter at index 'StartIndex' had been trivially "checked". | ||||
| 1420 | Ret.NumParamsChecked = 1; | ||||
| 1421 | |||||
| 1422 | for (std::size_t I = StartIndex + 1; I < NumParams; ++I) { | ||||
| 1423 | const ParmVarDecl *Ith = FD->getParamDecl(I); | ||||
| 1424 | StringRef ParamName = Ith->getName(); | ||||
| 1425 | LLVM_DEBUG(llvm::dbgs()do { if (::llvm::DebugFlag && ::llvm::isCurrentDebugType ("EasilySwappableParametersCheck")) { llvm::dbgs() << "Check param #" << I << " '" << ParamName << "'...\n" ; } } while (false) | ||||
| 1426 | << "Check param #" << I << " '" << ParamName << "'...\n")do { if (::llvm::DebugFlag && ::llvm::isCurrentDebugType ("EasilySwappableParametersCheck")) { llvm::dbgs() << "Check param #" << I << " '" << ParamName << "'...\n" ; } } while (false); | ||||
| 1427 | if (filter::isIgnoredParameter(Check, Ith)) { | ||||
| 1428 | LLVM_DEBUG(llvm::dbgs() << "Param #" << I << " is ignored. Break!\n")do { if (::llvm::DebugFlag && ::llvm::isCurrentDebugType ("EasilySwappableParametersCheck")) { llvm::dbgs() << "Param #" << I << " is ignored. Break!\n"; } } while (false ); | ||||
| 1429 | break; | ||||
| 1430 | } | ||||
| 1431 | |||||
| 1432 | StringRef PrevParamName = FD->getParamDecl(I - 1)->getName(); | ||||
| 1433 | if (!ParamName.empty() && !PrevParamName.empty() && | ||||
| 1434 | filter::prefixSuffixCoverUnderThreshold( | ||||
| 1435 | Check.NamePrefixSuffixSilenceDissimilarityTreshold, PrevParamName, | ||||
| 1436 | ParamName)) { | ||||
| 1437 | LLVM_DEBUG(llvm::dbgs() << "Parameter '" << ParamNamedo { if (::llvm::DebugFlag && ::llvm::isCurrentDebugType ("EasilySwappableParametersCheck")) { llvm::dbgs() << "Parameter '" << ParamName << "' follows a pattern with previous parameter '" << PrevParamName << "'. Break!\n"; } } while (false ) | ||||
| 1438 | << "' follows a pattern with previous parameter '"do { if (::llvm::DebugFlag && ::llvm::isCurrentDebugType ("EasilySwappableParametersCheck")) { llvm::dbgs() << "Parameter '" << ParamName << "' follows a pattern with previous parameter '" << PrevParamName << "'. Break!\n"; } } while (false ) | ||||
| 1439 | << PrevParamName << "'. Break!\n")do { if (::llvm::DebugFlag && ::llvm::isCurrentDebugType ("EasilySwappableParametersCheck")) { llvm::dbgs() << "Parameter '" << ParamName << "' follows a pattern with previous parameter '" << PrevParamName << "'. Break!\n"; } } while (false ); | ||||
| 1440 | break; | ||||
| 1441 | } | ||||
| 1442 | |||||
| 1443 | // Now try to go forward and build the range of [Start, ..., I, I + 1, ...] | ||||
| 1444 | // parameters that can be messed up at a call site. | ||||
| 1445 | MixableParameterRange::MixVector MixesOfIth; | ||||
| 1446 | for (std::size_t J = StartIndex; J < I; ++J) { | ||||
| 1447 | const ParmVarDecl *Jth = FD->getParamDecl(J); | ||||
| 1448 | LLVM_DEBUG(llvm::dbgs()do { if (::llvm::DebugFlag && ::llvm::isCurrentDebugType ("EasilySwappableParametersCheck")) { llvm::dbgs() << "Check mix of #" << J << " against #" << I << "...\n" ; } } while (false) | ||||
| 1449 | << "Check mix of #" << J << " against #" << I << "...\n")do { if (::llvm::DebugFlag && ::llvm::isCurrentDebugType ("EasilySwappableParametersCheck")) { llvm::dbgs() << "Check mix of #" << J << " against #" << I << "...\n" ; } } while (false); | ||||
| 1450 | |||||
| 1451 | if (isSimilarlyUsedParameter(UsageBasedSuppressor, Ith, Jth)) { | ||||
| 1452 | // Consider the two similarly used parameters to not be possible in a | ||||
| 1453 | // mix-up at the user's request, if they enabled this heuristic. | ||||
| 1454 | LLVM_DEBUG(llvm::dbgs() << "Parameters #" << I << " and #" << Jdo { if (::llvm::DebugFlag && ::llvm::isCurrentDebugType ("EasilySwappableParametersCheck")) { llvm::dbgs() << "Parameters #" << I << " and #" << J << " deemed related, ignoring...\n" ; } } while (false) | ||||
| 1455 | << " deemed related, ignoring...\n")do { if (::llvm::DebugFlag && ::llvm::isCurrentDebugType ("EasilySwappableParametersCheck")) { llvm::dbgs() << "Parameters #" << I << " and #" << J << " deemed related, ignoring...\n" ; } } while (false); | ||||
| 1456 | |||||
| 1457 | // If the parameter #I and #J mixes, then I is mixable with something | ||||
| 1458 | // in the current range, so the range has to be broken and I not | ||||
| 1459 | // included. | ||||
| 1460 | MixesOfIth.clear(); | ||||
| 1461 | break; | ||||
| 1462 | } | ||||
| 1463 | |||||
| 1464 | Mix M{Jth, Ith, | ||||
| 1465 | calculateMixability(Check, Jth->getType(), Ith->getType(), Ctx, | ||||
| 1466 | Check.ModelImplicitConversions | ||||
| 1467 | ? ImplicitConversionModellingMode::All | ||||
| 1468 | : ImplicitConversionModellingMode::None)}; | ||||
| 1469 | LLVM_DEBUG(llvm::dbgs() << "Mix flags (raw) : "do { if (::llvm::DebugFlag && ::llvm::isCurrentDebugType ("EasilySwappableParametersCheck")) { llvm::dbgs() << "Mix flags (raw) : " << formatMixFlags(M.flags()) << '\n'; } } while ( false) | ||||
| 1470 | << formatMixFlags(M.flags()) << '\n')do { if (::llvm::DebugFlag && ::llvm::isCurrentDebugType ("EasilySwappableParametersCheck")) { llvm::dbgs() << "Mix flags (raw) : " << formatMixFlags(M.flags()) << '\n'; } } while ( false); | ||||
| 1471 | M.sanitize(); | ||||
| 1472 | LLVM_DEBUG(llvm::dbgs() << "Mix flags (after sanitize): "do { if (::llvm::DebugFlag && ::llvm::isCurrentDebugType ("EasilySwappableParametersCheck")) { llvm::dbgs() << "Mix flags (after sanitize): " << formatMixFlags(M.flags()) << '\n'; } } while ( false) | ||||
| 1473 | << formatMixFlags(M.flags()) << '\n')do { if (::llvm::DebugFlag && ::llvm::isCurrentDebugType ("EasilySwappableParametersCheck")) { llvm::dbgs() << "Mix flags (after sanitize): " << formatMixFlags(M.flags()) << '\n'; } } while ( false); | ||||
| 1474 | |||||
| 1475 | assert(M.flagsValid() && "All flags decayed!")(static_cast <bool> (M.flagsValid() && "All flags decayed!" ) ? void (0) : __assert_fail ("M.flagsValid() && \"All flags decayed!\"" , "clang-tools-extra/clang-tidy/bugprone/EasilySwappableParametersCheck.cpp" , 1475, __extension__ __PRETTY_FUNCTION__)); | ||||
| 1476 | |||||
| 1477 | if (M.mixable()) | ||||
| 1478 | MixesOfIth.emplace_back(std::move(M)); | ||||
| 1479 | } | ||||
| 1480 | |||||
| 1481 | if (MixesOfIth.empty()) { | ||||
| 1482 | // If there weren't any new mixes stored for Ith, the range is | ||||
| 1483 | // [Start, ..., I]. | ||||
| 1484 | LLVM_DEBUG(llvm::dbgs()do { if (::llvm::DebugFlag && ::llvm::isCurrentDebugType ("EasilySwappableParametersCheck")) { llvm::dbgs() << "Param #" << I << " does not mix with any in the current range. Break!\n" ; } } while (false) | ||||
| 1485 | << "Param #" << Ido { if (::llvm::DebugFlag && ::llvm::isCurrentDebugType ("EasilySwappableParametersCheck")) { llvm::dbgs() << "Param #" << I << " does not mix with any in the current range. Break!\n" ; } } while (false) | ||||
| 1486 | << " does not mix with any in the current range. Break!\n")do { if (::llvm::DebugFlag && ::llvm::isCurrentDebugType ("EasilySwappableParametersCheck")) { llvm::dbgs() << "Param #" << I << " does not mix with any in the current range. Break!\n" ; } } while (false); | ||||
| 1487 | break; | ||||
| 1488 | } | ||||
| 1489 | |||||
| 1490 | Ret.Mixes.insert(Ret.Mixes.end(), MixesOfIth.begin(), MixesOfIth.end()); | ||||
| 1491 | ++Ret.NumParamsChecked; // Otherwise a new param was iterated. | ||||
| 1492 | } | ||||
| 1493 | |||||
| 1494 | return Ret; | ||||
| 1495 | } | ||||
| 1496 | |||||
| 1497 | } // namespace model | ||||
| 1498 | |||||
| 1499 | /// Matches DeclRefExprs and their ignorable wrappers to ParmVarDecls. | ||||
| 1500 | AST_MATCHER_FUNCTION(ast_matchers::internal::Matcher<Stmt>, paramRefExpr)inline ast_matchers::internal::Matcher<Stmt> paramRefExpr_getInstance (); inline ast_matchers::internal::Matcher<Stmt> paramRefExpr () { return ::clang::ast_matchers::internal::MemoizedMatcher< ast_matchers::internal::Matcher<Stmt>, paramRefExpr_getInstance >::getInstance(); } inline ast_matchers::internal::Matcher <Stmt> paramRefExpr_getInstance() { | ||||
| 1501 | return expr(ignoringParenImpCasts(ignoringElidableConstructorCall( | ||||
| 1502 | declRefExpr(to(parmVarDecl().bind("param")))))); | ||||
| 1503 | } | ||||
| 1504 | |||||
| 1505 | namespace filter { | ||||
| 1506 | |||||
| 1507 | /// Returns whether the parameter's name or the parameter's type's name is | ||||
| 1508 | /// configured by the user to be ignored from analysis and diagnostic. | ||||
| 1509 | static bool isIgnoredParameter(const TheCheck &Check, const ParmVarDecl *Node) { | ||||
| 1510 | LLVM_DEBUG(llvm::dbgs() << "Checking if '" << Node->getName()do { if (::llvm::DebugFlag && ::llvm::isCurrentDebugType ("EasilySwappableParametersCheck")) { llvm::dbgs() << "Checking if '" << Node->getName() << "' is ignored.\n"; } } while (false) | ||||
| 1511 | << "' is ignored.\n")do { if (::llvm::DebugFlag && ::llvm::isCurrentDebugType ("EasilySwappableParametersCheck")) { llvm::dbgs() << "Checking if '" << Node->getName() << "' is ignored.\n"; } } while (false); | ||||
| 1512 | |||||
| 1513 | if (!Node->getIdentifier()) | ||||
| 1514 | return llvm::is_contained(Check.IgnoredParameterNames, "\"\""); | ||||
| 1515 | |||||
| 1516 | StringRef NodeName = Node->getName(); | ||||
| 1517 | if (llvm::is_contained(Check.IgnoredParameterNames, NodeName)) { | ||||
| 1518 | LLVM_DEBUG(llvm::dbgs() << "\tName ignored.\n")do { if (::llvm::DebugFlag && ::llvm::isCurrentDebugType ("EasilySwappableParametersCheck")) { llvm::dbgs() << "\tName ignored.\n" ; } } while (false); | ||||
| 1519 | return true; | ||||
| 1520 | } | ||||
| 1521 | |||||
| 1522 | StringRef NodeTypeName = [Node] { | ||||
| 1523 | const ASTContext &Ctx = Node->getASTContext(); | ||||
| 1524 | const SourceManager &SM = Ctx.getSourceManager(); | ||||
| 1525 | SourceLocation B = Node->getTypeSpecStartLoc(); | ||||
| 1526 | SourceLocation E = Node->getTypeSpecEndLoc(); | ||||
| 1527 | LangOptions LO; | ||||
| 1528 | |||||
| 1529 | LLVM_DEBUG(llvm::dbgs() << "\tType name code is '"do { if (::llvm::DebugFlag && ::llvm::isCurrentDebugType ("EasilySwappableParametersCheck")) { llvm::dbgs() << "\tType name code is '" << Lexer::getSourceText( CharSourceRange::getTokenRange (B, E), SM, LO) << "'...\n"; } } while (false) | ||||
| 1530 | << Lexer::getSourceText(do { if (::llvm::DebugFlag && ::llvm::isCurrentDebugType ("EasilySwappableParametersCheck")) { llvm::dbgs() << "\tType name code is '" << Lexer::getSourceText( CharSourceRange::getTokenRange (B, E), SM, LO) << "'...\n"; } } while (false) | ||||
| 1531 | CharSourceRange::getTokenRange(B, E), SM, LO)do { if (::llvm::DebugFlag && ::llvm::isCurrentDebugType ("EasilySwappableParametersCheck")) { llvm::dbgs() << "\tType name code is '" << Lexer::getSourceText( CharSourceRange::getTokenRange (B, E), SM, LO) << "'...\n"; } } while (false) | ||||
| 1532 | << "'...\n")do { if (::llvm::DebugFlag && ::llvm::isCurrentDebugType ("EasilySwappableParametersCheck")) { llvm::dbgs() << "\tType name code is '" << Lexer::getSourceText( CharSourceRange::getTokenRange (B, E), SM, LO) << "'...\n"; } } while (false); | ||||
| 1533 | if (B.isMacroID()) { | ||||
| 1534 | LLVM_DEBUG(llvm::dbgs() << "\t\tBeginning is macro.\n")do { if (::llvm::DebugFlag && ::llvm::isCurrentDebugType ("EasilySwappableParametersCheck")) { llvm::dbgs() << "\t\tBeginning is macro.\n" ; } } while (false); | ||||
| 1535 | B = SM.getTopMacroCallerLoc(B); | ||||
| 1536 | } | ||||
| 1537 | if (E.isMacroID()) { | ||||
| 1538 | LLVM_DEBUG(llvm::dbgs() << "\t\tEnding is macro.\n")do { if (::llvm::DebugFlag && ::llvm::isCurrentDebugType ("EasilySwappableParametersCheck")) { llvm::dbgs() << "\t\tEnding is macro.\n" ; } } while (false); | ||||
| 1539 | E = Lexer::getLocForEndOfToken(SM.getTopMacroCallerLoc(E), 0, SM, LO); | ||||
| 1540 | } | ||||
| 1541 | LLVM_DEBUG(llvm::dbgs() << "\tType name code is '"do { if (::llvm::DebugFlag && ::llvm::isCurrentDebugType ("EasilySwappableParametersCheck")) { llvm::dbgs() << "\tType name code is '" << Lexer::getSourceText( CharSourceRange::getTokenRange (B, E), SM, LO) << "'...\n"; } } while (false) | ||||
| 1542 | << Lexer::getSourceText(do { if (::llvm::DebugFlag && ::llvm::isCurrentDebugType ("EasilySwappableParametersCheck")) { llvm::dbgs() << "\tType name code is '" << Lexer::getSourceText( CharSourceRange::getTokenRange (B, E), SM, LO) << "'...\n"; } } while (false) | ||||
| 1543 | CharSourceRange::getTokenRange(B, E), SM, LO)do { if (::llvm::DebugFlag && ::llvm::isCurrentDebugType ("EasilySwappableParametersCheck")) { llvm::dbgs() << "\tType name code is '" << Lexer::getSourceText( CharSourceRange::getTokenRange (B, E), SM, LO) << "'...\n"; } } while (false) | ||||
| 1544 | << "'...\n")do { if (::llvm::DebugFlag && ::llvm::isCurrentDebugType ("EasilySwappableParametersCheck")) { llvm::dbgs() << "\tType name code is '" << Lexer::getSourceText( CharSourceRange::getTokenRange (B, E), SM, LO) << "'...\n"; } } while (false); | ||||
| 1545 | |||||
| 1546 | return Lexer::getSourceText(CharSourceRange::getTokenRange(B, E), SM, LO); | ||||
| 1547 | }(); | ||||
| 1548 | |||||
| 1549 | LLVM_DEBUG(llvm::dbgs() << "\tType name is '" << NodeTypeName << "'\n")do { if (::llvm::DebugFlag && ::llvm::isCurrentDebugType ("EasilySwappableParametersCheck")) { llvm::dbgs() << "\tType name is '" << NodeTypeName << "'\n"; } } while (false); | ||||
| 1550 | if (!NodeTypeName.empty()) { | ||||
| 1551 | if (llvm::any_of(Check.IgnoredParameterTypeSuffixes, | ||||
| 1552 | [NodeTypeName](StringRef E) { | ||||
| 1553 | return !E.empty() && NodeTypeName.endswith(E); | ||||
| 1554 | })) { | ||||
| 1555 | LLVM_DEBUG(llvm::dbgs() << "\tType suffix ignored.\n")do { if (::llvm::DebugFlag && ::llvm::isCurrentDebugType ("EasilySwappableParametersCheck")) { llvm::dbgs() << "\tType suffix ignored.\n" ; } } while (false); | ||||
| 1556 | return true; | ||||
| 1557 | } | ||||
| 1558 | } | ||||
| 1559 | |||||
| 1560 | return false; | ||||
| 1561 | } | ||||
| 1562 | |||||
| 1563 | /// This namespace contains the implementations for the suppression of | ||||
| 1564 | /// diagnostics from similarly-used ("related") parameters. | ||||
| 1565 | namespace relatedness_heuristic { | ||||
| 1566 | |||||
| 1567 | static constexpr std::size_t SmallDataStructureSize = 4; | ||||
| 1568 | |||||
| 1569 | template <typename T, std::size_t N = SmallDataStructureSize> | ||||
| 1570 | using ParamToSmallSetMap = | ||||
| 1571 | llvm::DenseMap<const ParmVarDecl *, llvm::SmallSet<T, N>>; | ||||
| 1572 | |||||
| 1573 | /// Returns whether the sets mapped to the two elements in the map have at | ||||
| 1574 | /// least one element in common. | ||||
| 1575 | template <typename MapTy, typename ElemTy> | ||||
| 1576 | bool lazyMapOfSetsIntersectionExists(const MapTy &Map, const ElemTy &E1, | ||||
| 1577 | const ElemTy &E2) { | ||||
| 1578 | auto E1Iterator = Map.find(E1); | ||||
| 1579 | auto E2Iterator = Map.find(E2); | ||||
| 1580 | if (E1Iterator == Map.end() || E2Iterator == Map.end()) | ||||
| 1581 | return false; | ||||
| 1582 | |||||
| 1583 | for (const auto &E1SetElem : E1Iterator->second) | ||||
| 1584 | if (E2Iterator->second.contains(E1SetElem)) | ||||
| 1585 | return true; | ||||
| 1586 | |||||
| 1587 | return false; | ||||
| 1588 | } | ||||
| 1589 | |||||
| 1590 | /// Implements the heuristic that marks two parameters related if there is | ||||
| 1591 | /// a usage for both in the same strict expression subtree. A strict | ||||
| 1592 | /// expression subtree is a tree which only includes Expr nodes, i.e. no | ||||
| 1593 | /// Stmts and no Decls. | ||||
| 1594 | class AppearsInSameExpr : public RecursiveASTVisitor<AppearsInSameExpr> { | ||||
| 1595 | using Base = RecursiveASTVisitor<AppearsInSameExpr>; | ||||
| 1596 | |||||
| 1597 | const FunctionDecl *FD; | ||||
| 1598 | const Expr *CurrentExprOnlyTreeRoot = nullptr; | ||||
| 1599 | llvm::DenseMap<const ParmVarDecl *, | ||||
| 1600 | llvm::SmallPtrSet<const Expr *, SmallDataStructureSize>> | ||||
| 1601 | ParentExprsForParamRefs; | ||||
| 1602 | |||||
| 1603 | public: | ||||
| 1604 | void setup(const FunctionDecl *FD) { | ||||
| 1605 | this->FD = FD; | ||||
| 1606 | TraverseFunctionDecl(const_cast<FunctionDecl *>(FD)); | ||||
| 1607 | } | ||||
| 1608 | |||||
| 1609 | bool operator()(const ParmVarDecl *Param1, const ParmVarDecl *Param2) const { | ||||
| 1610 | return lazyMapOfSetsIntersectionExists(ParentExprsForParamRefs, Param1, | ||||
| 1611 | Param2); | ||||
| 1612 | } | ||||
| 1613 | |||||
| 1614 | bool TraverseDecl(Decl *D) { | ||||
| 1615 | CurrentExprOnlyTreeRoot = nullptr; | ||||
| 1616 | return Base::TraverseDecl(D); | ||||
| 1617 | } | ||||
| 1618 | |||||
| 1619 | bool TraverseStmt(Stmt *S, DataRecursionQueue *Queue = nullptr) { | ||||
| 1620 | if (auto *E = dyn_cast_or_null<Expr>(S)) { | ||||
| 1621 | bool RootSetInCurrentStackFrame = false; | ||||
| 1622 | if (!CurrentExprOnlyTreeRoot) { | ||||
| 1623 | CurrentExprOnlyTreeRoot = E; | ||||
| 1624 | RootSetInCurrentStackFrame = true; | ||||
| 1625 | } | ||||
| 1626 | |||||
| 1627 | bool Ret = Base::TraverseStmt(S); | ||||
| 1628 | |||||
| 1629 | if (RootSetInCurrentStackFrame) | ||||
| 1630 | CurrentExprOnlyTreeRoot = nullptr; | ||||
| 1631 | |||||
| 1632 | return Ret; | ||||
| 1633 | } | ||||
| 1634 | |||||
| 1635 | // A Stmt breaks the strictly Expr subtree. | ||||
| 1636 | CurrentExprOnlyTreeRoot = nullptr; | ||||
| 1637 | return Base::TraverseStmt(S); | ||||
| 1638 | } | ||||
| 1639 | |||||
| 1640 | bool VisitDeclRefExpr(DeclRefExpr *DRE) { | ||||
| 1641 | if (!CurrentExprOnlyTreeRoot) | ||||
| 1642 | return true; | ||||
| 1643 | |||||
| 1644 | if (auto *PVD = dyn_cast<ParmVarDecl>(DRE->getDecl())) | ||||
| 1645 | if (llvm::find(FD->parameters(), PVD)) | ||||
| 1646 | ParentExprsForParamRefs[PVD].insert(CurrentExprOnlyTreeRoot); | ||||
| 1647 | |||||
| 1648 | return true; | ||||
| 1649 | } | ||||
| 1650 | }; | ||||
| 1651 | |||||
| 1652 | /// Implements the heuristic that marks two parameters related if there are | ||||
| 1653 | /// two separate calls to the same function (overload) and the parameters are | ||||
| 1654 | /// passed to the same index in both calls, i.e f(a, b) and f(a, c) passes | ||||
| 1655 | /// b and c to the same index (2) of f(), marking them related. | ||||
| 1656 | class PassedToSameFunction { | ||||
| 1657 | ParamToSmallSetMap<std::pair<const FunctionDecl *, unsigned>> TargetParams; | ||||
| 1658 | |||||
| 1659 | public: | ||||
| 1660 | void setup(const FunctionDecl *FD) { | ||||
| 1661 | auto ParamsAsArgsInFnCalls = | ||||
| 1662 | match(functionDecl(forEachDescendant( | ||||
| 1663 | callExpr(forEachArgumentWithParam( | ||||
| 1664 | paramRefExpr(), parmVarDecl().bind("passed-to"))) | ||||
| 1665 | .bind("call-expr"))), | ||||
| 1666 | *FD, FD->getASTContext()); | ||||
| 1667 | for (const auto &Match : ParamsAsArgsInFnCalls) { | ||||
| 1668 | const auto *PassedParamOfThisFn = Match.getNodeAs<ParmVarDecl>("param"); | ||||
| 1669 | const auto *CE = Match.getNodeAs<CallExpr>("call-expr"); | ||||
| 1670 | const auto *PassedToParam = Match.getNodeAs<ParmVarDecl>("passed-to"); | ||||
| 1671 | assert(PassedParamOfThisFn && CE && PassedToParam)(static_cast <bool> (PassedParamOfThisFn && CE && PassedToParam) ? void (0) : __assert_fail ("PassedParamOfThisFn && CE && PassedToParam" , "clang-tools-extra/clang-tidy/bugprone/EasilySwappableParametersCheck.cpp" , 1671, __extension__ __PRETTY_FUNCTION__)); | ||||
| 1672 | |||||
| 1673 | const FunctionDecl *CalledFn = CE->getDirectCallee(); | ||||
| 1674 | if (!CalledFn) | ||||
| 1675 | continue; | ||||
| 1676 | |||||
| 1677 | std::optional<unsigned> TargetIdx; | ||||
| 1678 | unsigned NumFnParams = CalledFn->getNumParams(); | ||||
| 1679 | for (unsigned Idx = 0; Idx < NumFnParams; ++Idx) | ||||
| 1680 | if (CalledFn->getParamDecl(Idx) == PassedToParam) | ||||
| 1681 | TargetIdx.emplace(Idx); | ||||
| 1682 | |||||
| 1683 | assert(TargetIdx && "Matched, but didn't find index?")(static_cast <bool> (TargetIdx && "Matched, but didn't find index?" ) ? void (0) : __assert_fail ("TargetIdx && \"Matched, but didn't find index?\"" , "clang-tools-extra/clang-tidy/bugprone/EasilySwappableParametersCheck.cpp" , 1683, __extension__ __PRETTY_FUNCTION__)); | ||||
| 1684 | TargetParams[PassedParamOfThisFn].insert( | ||||
| 1685 | {CalledFn->getCanonicalDecl(), *TargetIdx}); | ||||
| 1686 | } | ||||
| 1687 | } | ||||
| 1688 | |||||
| 1689 | bool operator()(const ParmVarDecl *Param1, const ParmVarDecl *Param2) const { | ||||
| 1690 | return lazyMapOfSetsIntersectionExists(TargetParams, Param1, Param2); | ||||
| 1691 | } | ||||
| 1692 | }; | ||||
| 1693 | |||||
| 1694 | /// Implements the heuristic that marks two parameters related if the same | ||||
| 1695 | /// member is accessed (referred to) inside the current function's body. | ||||
| 1696 | class AccessedSameMemberOf { | ||||
| 1697 | ParamToSmallSetMap<const Decl *> AccessedMembers; | ||||
| 1698 | |||||
| 1699 | public: | ||||
| 1700 | void setup(const FunctionDecl *FD) { | ||||
| 1701 | auto MembersCalledOnParams = match( | ||||
| 1702 | functionDecl(forEachDescendant( | ||||
| 1703 | memberExpr(hasObjectExpression(paramRefExpr())).bind("mem-expr"))), | ||||
| 1704 | *FD, FD->getASTContext()); | ||||
| 1705 | |||||
| 1706 | for (const auto &Match : MembersCalledOnParams) { | ||||
| 1707 | const auto *AccessedParam = Match.getNodeAs<ParmVarDecl>("param"); | ||||
| 1708 | const auto *ME = Match.getNodeAs<MemberExpr>("mem-expr"); | ||||
| 1709 | assert(AccessedParam && ME)(static_cast <bool> (AccessedParam && ME) ? void (0) : __assert_fail ("AccessedParam && ME", "clang-tools-extra/clang-tidy/bugprone/EasilySwappableParametersCheck.cpp" , 1709, __extension__ __PRETTY_FUNCTION__)); | ||||
| 1710 | AccessedMembers[AccessedParam].insert( | ||||
| 1711 | ME->getMemberDecl()->getCanonicalDecl()); | ||||
| 1712 | } | ||||
| 1713 | } | ||||
| 1714 | |||||
| 1715 | bool operator()(const ParmVarDecl *Param1, const ParmVarDecl *Param2) const { | ||||
| 1716 | return lazyMapOfSetsIntersectionExists(AccessedMembers, Param1, Param2); | ||||
| 1717 | } | ||||
| 1718 | }; | ||||
| 1719 | |||||
| 1720 | /// Implements the heuristic that marks two parameters related if different | ||||
| 1721 | /// ReturnStmts return them from the function. | ||||
| 1722 | class Returned { | ||||
| 1723 | llvm::SmallVector<const ParmVarDecl *, SmallDataStructureSize> ReturnedParams; | ||||
| 1724 | |||||
| 1725 | public: | ||||
| 1726 | void setup(const FunctionDecl *FD) { | ||||
| 1727 | // TODO: Handle co_return. | ||||
| 1728 | auto ParamReturns = match(functionDecl(forEachDescendant( | ||||
| 1729 | returnStmt(hasReturnValue(paramRefExpr())))), | ||||
| 1730 | *FD, FD->getASTContext()); | ||||
| 1731 | for (const auto &Match : ParamReturns) { | ||||
| 1732 | const auto *ReturnedParam = Match.getNodeAs<ParmVarDecl>("param"); | ||||
| 1733 | assert(ReturnedParam)(static_cast <bool> (ReturnedParam) ? void (0) : __assert_fail ("ReturnedParam", "clang-tools-extra/clang-tidy/bugprone/EasilySwappableParametersCheck.cpp" , 1733, __extension__ __PRETTY_FUNCTION__)); | ||||
| 1734 | |||||
| 1735 | if (find(FD->parameters(), ReturnedParam) == FD->param_end()) | ||||
| 1736 | // Inside the subtree of a FunctionDecl there might be ReturnStmts of | ||||
| 1737 | // a parameter that isn't the parameter of the function, e.g. in the | ||||
| 1738 | // case of lambdas. | ||||
| 1739 | continue; | ||||
| 1740 | |||||
| 1741 | ReturnedParams.emplace_back(ReturnedParam); | ||||
| 1742 | } | ||||
| 1743 | } | ||||
| 1744 | |||||
| 1745 | bool operator()(const ParmVarDecl *Param1, const ParmVarDecl *Param2) const { | ||||
| 1746 | return llvm::is_contained(ReturnedParams, Param1) && | ||||
| 1747 | llvm::is_contained(ReturnedParams, Param2); | ||||
| 1748 | } | ||||
| 1749 | }; | ||||
| 1750 | |||||
| 1751 | } // namespace relatedness_heuristic | ||||
| 1752 | |||||
| 1753 | /// Helper class that is used to detect if two parameters of the same function | ||||
| 1754 | /// are used in a similar fashion, to suppress the result. | ||||
| 1755 | class SimilarlyUsedParameterPairSuppressor { | ||||
| 1756 | const bool Enabled; | ||||
| 1757 | relatedness_heuristic::AppearsInSameExpr SameExpr; | ||||
| 1758 | relatedness_heuristic::PassedToSameFunction PassToFun; | ||||
| 1759 | relatedness_heuristic::AccessedSameMemberOf SameMember; | ||||
| 1760 | relatedness_heuristic::Returned Returns; | ||||
| 1761 | |||||
| 1762 | public: | ||||
| 1763 | SimilarlyUsedParameterPairSuppressor(const FunctionDecl *FD, bool Enable) | ||||
| 1764 | : Enabled(Enable) { | ||||
| 1765 | if (!Enable) | ||||
| 1766 | return; | ||||
| 1767 | |||||
| 1768 | SameExpr.setup(FD); | ||||
| 1769 | PassToFun.setup(FD); | ||||
| 1770 | SameMember.setup(FD); | ||||
| 1771 | Returns.setup(FD); | ||||
| 1772 | } | ||||
| 1773 | |||||
| 1774 | /// Returns whether the specified two parameters are deemed similarly used | ||||
| 1775 | /// or related by the heuristics. | ||||
| 1776 | bool operator()(const ParmVarDecl *Param1, const ParmVarDecl *Param2) const { | ||||
| 1777 | if (!Enabled) | ||||
| 1778 | return false; | ||||
| 1779 | |||||
| 1780 | LLVM_DEBUG(llvm::dbgs()do { if (::llvm::DebugFlag && ::llvm::isCurrentDebugType ("EasilySwappableParametersCheck")) { llvm::dbgs() << "::: Matching similar usage / relatedness heuristic...\n" ; } } while (false) | ||||
| 1781 | << "::: Matching similar usage / relatedness heuristic...\n")do { if (::llvm::DebugFlag && ::llvm::isCurrentDebugType ("EasilySwappableParametersCheck")) { llvm::dbgs() << "::: Matching similar usage / relatedness heuristic...\n" ; } } while (false); | ||||
| 1782 | |||||
| 1783 | if (SameExpr(Param1, Param2)) { | ||||
| 1784 | LLVM_DEBUG(llvm::dbgs() << "::: Used in the same expression.\n")do { if (::llvm::DebugFlag && ::llvm::isCurrentDebugType ("EasilySwappableParametersCheck")) { llvm::dbgs() << "::: Used in the same expression.\n" ; } } while (false); | ||||
| 1785 | return true; | ||||
| 1786 | } | ||||
| 1787 | |||||
| 1788 | if (PassToFun(Param1, Param2)) { | ||||
| 1789 | LLVM_DEBUG(llvm::dbgs()do { if (::llvm::DebugFlag && ::llvm::isCurrentDebugType ("EasilySwappableParametersCheck")) { llvm::dbgs() << "::: Passed to same function in different calls.\n" ; } } while (false) | ||||
| 1790 | << "::: Passed to same function in different calls.\n")do { if (::llvm::DebugFlag && ::llvm::isCurrentDebugType ("EasilySwappableParametersCheck")) { llvm::dbgs() << "::: Passed to same function in different calls.\n" ; } } while (false); | ||||
| 1791 | return true; | ||||
| 1792 | } | ||||
| 1793 | |||||
| 1794 | if (SameMember(Param1, Param2)) { | ||||
| 1795 | LLVM_DEBUG(llvm::dbgs()do { if (::llvm::DebugFlag && ::llvm::isCurrentDebugType ("EasilySwappableParametersCheck")) { llvm::dbgs() << "::: Same member field access or method called.\n" ; } } while (false) | ||||
| 1796 | << "::: Same member field access or method called.\n")do { if (::llvm::DebugFlag && ::llvm::isCurrentDebugType ("EasilySwappableParametersCheck")) { llvm::dbgs() << "::: Same member field access or method called.\n" ; } } while (false); | ||||
| 1797 | return true; | ||||
| 1798 | } | ||||
| 1799 | |||||
| 1800 | if (Returns(Param1, Param2)) { | ||||
| 1801 | LLVM_DEBUG(llvm::dbgs() << "::: Both parameter returned.\n")do { if (::llvm::DebugFlag && ::llvm::isCurrentDebugType ("EasilySwappableParametersCheck")) { llvm::dbgs() << "::: Both parameter returned.\n" ; } } while (false); | ||||
| 1802 | return true; | ||||
| 1803 | } | ||||
| 1804 | |||||
| 1805 | LLVM_DEBUG(llvm::dbgs() << "::: None.\n")do { if (::llvm::DebugFlag && ::llvm::isCurrentDebugType ("EasilySwappableParametersCheck")) { llvm::dbgs() << "::: None.\n" ; } } while (false); | ||||
| 1806 | return false; | ||||
| 1807 | } | ||||
| 1808 | }; | ||||
| 1809 | |||||
| 1810 | // (This function hoists the call to operator() of the wrapper, so we do not | ||||
| 1811 | // need to define the previous class at the top of the file.) | ||||
| 1812 | static inline bool | ||||
| 1813 | isSimilarlyUsedParameter(const SimilarlyUsedParameterPairSuppressor &Suppressor, | ||||
| 1814 | const ParmVarDecl *Param1, const ParmVarDecl *Param2) { | ||||
| 1815 | return Suppressor(Param1, Param2); | ||||
| 1816 | } | ||||
| 1817 | |||||
| 1818 | static void padStringAtEnd(SmallVectorImpl<char> &Str, std::size_t ToLen) { | ||||
| 1819 | while (Str.size() < ToLen) | ||||
| 1820 | Str.emplace_back('\0'); | ||||
| 1821 | } | ||||
| 1822 | |||||
| 1823 | static void padStringAtBegin(SmallVectorImpl<char> &Str, std::size_t ToLen) { | ||||
| 1824 | while (Str.size() < ToLen) | ||||
| 1825 | Str.insert(Str.begin(), '\0'); | ||||
| 1826 | } | ||||
| 1827 | |||||
| 1828 | static bool isCommonPrefixWithoutSomeCharacters(std::size_t N, StringRef S1, | ||||
| 1829 | StringRef S2) { | ||||
| 1830 | assert(S1.size() >= N && S2.size() >= N)(static_cast <bool> (S1.size() >= N && S2.size () >= N) ? void (0) : __assert_fail ("S1.size() >= N && S2.size() >= N" , "clang-tools-extra/clang-tidy/bugprone/EasilySwappableParametersCheck.cpp" , 1830, __extension__ __PRETTY_FUNCTION__)); | ||||
| 1831 | StringRef S1Prefix = S1.take_front(S1.size() - N), | ||||
| 1832 | S2Prefix = S2.take_front(S2.size() - N); | ||||
| 1833 | return S1Prefix == S2Prefix && !S1Prefix.empty(); | ||||
| 1834 | } | ||||
| 1835 | |||||
| 1836 | static bool isCommonSuffixWithoutSomeCharacters(std::size_t N, StringRef S1, | ||||
| 1837 | StringRef S2) { | ||||
| 1838 | assert(S1.size() >= N && S2.size() >= N)(static_cast <bool> (S1.size() >= N && S2.size () >= N) ? void (0) : __assert_fail ("S1.size() >= N && S2.size() >= N" , "clang-tools-extra/clang-tidy/bugprone/EasilySwappableParametersCheck.cpp" , 1838, __extension__ __PRETTY_FUNCTION__)); | ||||
| 1839 | StringRef S1Suffix = S1.take_back(S1.size() - N), | ||||
| 1840 | S2Suffix = S2.take_back(S2.size() - N); | ||||
| 1841 | return S1Suffix == S2Suffix && !S1Suffix.empty(); | ||||
| 1842 | } | ||||
| 1843 | |||||
| 1844 | /// Returns whether the two strings are prefixes or suffixes of each other with | ||||
| 1845 | /// at most Threshold characters differing on the non-common end. | ||||
| 1846 | static bool prefixSuffixCoverUnderThreshold(std::size_t Threshold, | ||||
| 1847 | StringRef Str1, StringRef Str2) { | ||||
| 1848 | if (Threshold == 0) | ||||
| 1849 | return false; | ||||
| 1850 | |||||
| 1851 | // Pad the two strings to the longer length. | ||||
| 1852 | std::size_t BiggerLength = std::max(Str1.size(), Str2.size()); | ||||
| 1853 | |||||
| 1854 | if (BiggerLength <= Threshold) | ||||
| 1855 | // If the length of the strings is still smaller than the threshold, they | ||||
| 1856 | // would be covered by an empty prefix/suffix with the rest differing. | ||||
| 1857 | // (E.g. "A" and "X" with Threshold = 1 would mean we think they are | ||||
| 1858 | // similar and do not warn about them, which is a too eager assumption.) | ||||
| 1859 | return false; | ||||
| 1860 | |||||
| 1861 | SmallString<32> S1PadE{Str1}, S2PadE{Str2}; | ||||
| 1862 | padStringAtEnd(S1PadE, BiggerLength); | ||||
| 1863 | padStringAtEnd(S2PadE, BiggerLength); | ||||
| 1864 | |||||
| 1865 | if (isCommonPrefixWithoutSomeCharacters( | ||||
| 1866 | Threshold, StringRef{S1PadE.begin(), BiggerLength}, | ||||
| 1867 | StringRef{S2PadE.begin(), BiggerLength})) | ||||
| 1868 | return true; | ||||
| 1869 | |||||
| 1870 | SmallString<32> S1PadB{Str1}, S2PadB{Str2}; | ||||
| 1871 | padStringAtBegin(S1PadB, BiggerLength); | ||||
| 1872 | padStringAtBegin(S2PadB, BiggerLength); | ||||
| 1873 | |||||
| 1874 | if (isCommonSuffixWithoutSomeCharacters( | ||||
| 1875 | Threshold, StringRef{S1PadB.begin(), BiggerLength}, | ||||
| 1876 | StringRef{S2PadB.begin(), BiggerLength})) | ||||
| 1877 | return true; | ||||
| 1878 | |||||
| 1879 | return false; | ||||
| 1880 | } | ||||
| 1881 | |||||
| 1882 | } // namespace filter | ||||
| 1883 | |||||
| 1884 | /// Matches functions that have at least the specified amount of parameters. | ||||
| 1885 | AST_MATCHER_P(FunctionDecl, parameterCountGE, unsigned, N)namespace internal { class matcher_parameterCountGE0Matcher : public ::clang::ast_matchers::internal::MatcherInterface< FunctionDecl> { public: explicit matcher_parameterCountGE0Matcher ( unsigned const &AN) : N(AN) {} bool matches(const FunctionDecl &Node, ::clang::ast_matchers::internal::ASTMatchFinder * Finder, ::clang::ast_matchers::internal::BoundNodesTreeBuilder *Builder) const override; private: unsigned N; }; } inline :: clang::ast_matchers::internal::Matcher<FunctionDecl> parameterCountGE ( unsigned const &N) { return ::clang::ast_matchers::internal ::makeMatcher( new internal::matcher_parameterCountGE0Matcher (N)); } typedef ::clang::ast_matchers::internal::Matcher<FunctionDecl > ( ¶meterCountGE_Type0)(unsigned const &N); inline bool internal::matcher_parameterCountGE0Matcher::matches( const FunctionDecl &Node, ::clang::ast_matchers::internal::ASTMatchFinder *Finder, ::clang::ast_matchers::internal::BoundNodesTreeBuilder *Builder) const { | ||||
| 1886 | return Node.getNumParams() >= N; | ||||
| 1887 | } | ||||
| 1888 | |||||
| 1889 | /// Matches *any* overloaded unary and binary operators. | ||||
| 1890 | AST_MATCHER(FunctionDecl, isOverloadedUnaryOrBinaryOperator)namespace internal { class matcher_isOverloadedUnaryOrBinaryOperatorMatcher : public ::clang::ast_matchers::internal::MatcherInterface< FunctionDecl> { public: explicit matcher_isOverloadedUnaryOrBinaryOperatorMatcher () = default; bool matches(const FunctionDecl &Node, ::clang ::ast_matchers::internal::ASTMatchFinder *Finder, ::clang::ast_matchers ::internal::BoundNodesTreeBuilder *Builder) const override; } ; } inline ::clang::ast_matchers::internal::Matcher<FunctionDecl > isOverloadedUnaryOrBinaryOperator() { return ::clang::ast_matchers ::internal::makeMatcher( new internal::matcher_isOverloadedUnaryOrBinaryOperatorMatcher ()); } inline bool internal::matcher_isOverloadedUnaryOrBinaryOperatorMatcher ::matches( const FunctionDecl &Node, ::clang::ast_matchers ::internal::ASTMatchFinder *Finder, ::clang::ast_matchers::internal ::BoundNodesTreeBuilder *Builder) const { | ||||
| 1891 | switch (Node.getOverloadedOperator()) { | ||||
| 1892 | case OO_None: | ||||
| 1893 | case OO_New: | ||||
| 1894 | case OO_Delete: | ||||
| 1895 | case OO_Array_New: | ||||
| 1896 | case OO_Array_Delete: | ||||
| 1897 | case OO_Conditional: | ||||
| 1898 | case OO_Coawait: | ||||
| 1899 | return false; | ||||
| 1900 | |||||
| 1901 | default: | ||||
| 1902 | return Node.getNumParams() <= 2; | ||||
| 1903 | } | ||||
| 1904 | } | ||||
| 1905 | |||||
| 1906 | /// Returns the DefaultMinimumLength if the Value of requested minimum length | ||||
| 1907 | /// is less than 2. Minimum lengths of 0 or 1 are not accepted. | ||||
| 1908 | static inline unsigned clampMinimumLength(const unsigned Value) { | ||||
| 1909 | return Value < 2 ? DefaultMinimumLength : Value; | ||||
| 1910 | } | ||||
| 1911 | |||||
| 1912 | // FIXME: Maybe unneeded, getNameForDiagnostic() is expected to change to return | ||||
| 1913 | // a crafted location when the node itself is unnamed. (See D84658, D85033.) | ||||
| 1914 | /// Returns the diagnostic-friendly name of the node, or empty string. | ||||
| 1915 | static SmallString<64> getName(const NamedDecl *ND) { | ||||
| 1916 | SmallString<64> Name; | ||||
| 1917 | llvm::raw_svector_ostream OS{Name}; | ||||
| 1918 | ND->getNameForDiagnostic(OS, ND->getASTContext().getPrintingPolicy(), false); | ||||
| 1919 | return Name; | ||||
| 1920 | } | ||||
| 1921 | |||||
| 1922 | /// Returns the diagnostic-friendly name of the node, or a constant value. | ||||
| 1923 | static SmallString<64> getNameOrUnnamed(const NamedDecl *ND) { | ||||
| 1924 | auto Name = getName(ND); | ||||
| 1925 | if (Name.empty()) | ||||
| 1926 | Name = "<unnamed>"; | ||||
| 1927 | return Name; | ||||
| 1928 | } | ||||
| 1929 | |||||
| 1930 | /// Returns whether a particular Mix between two parameters should have the | ||||
| 1931 | /// types involved diagnosed to the user. This is only a flag check. | ||||
| 1932 | static inline bool needsToPrintTypeInDiagnostic(const model::Mix &M) { | ||||
| 1933 | using namespace model; | ||||
| 1934 | return static_cast<bool>( | ||||
| 1935 | M.flags() & | ||||
| 1936 | (MixFlags::TypeAlias | MixFlags::ReferenceBind | MixFlags::Qualifiers)); | ||||
| 1937 | } | ||||
| 1938 | |||||
| 1939 | /// Returns whether a particular Mix between the two parameters should have | ||||
| 1940 | /// implicit conversions elaborated. | ||||
| 1941 | static inline bool needsToElaborateImplicitConversion(const model::Mix &M) { | ||||
| 1942 | return hasFlag(M.flags(), model::MixFlags::ImplicitConversion); | ||||
| 1943 | } | ||||
| 1944 | |||||
| 1945 | namespace { | ||||
| 1946 | |||||
| 1947 | /// This class formats a conversion sequence into a "Ty1 -> Ty2 -> Ty3" line | ||||
| 1948 | /// that can be used in diagnostics. | ||||
| 1949 | struct FormattedConversionSequence { | ||||
| 1950 | std::string DiagnosticText; | ||||
| 1951 | |||||
| 1952 | /// The formatted sequence is trivial if it is "Ty1 -> Ty2", but Ty1 and | ||||
| 1953 | /// Ty2 are the types that are shown in the code. A trivial diagnostic | ||||
| 1954 | /// does not need to be printed. | ||||
| 1955 | bool Trivial; | ||||
| 1956 | |||||
| 1957 | FormattedConversionSequence(const PrintingPolicy &PP, | ||||
| 1958 | StringRef StartTypeAsDiagnosed, | ||||
| 1959 | const model::ConversionSequence &Conv, | ||||
| 1960 | StringRef DestinationTypeAsDiagnosed) { | ||||
| 1961 | Trivial = true; | ||||
| 1962 | llvm::raw_string_ostream OS{DiagnosticText}; | ||||
| 1963 | |||||
| 1964 | // Print the type name as it is printed in other places in the diagnostic. | ||||
| 1965 | OS << '\'' << StartTypeAsDiagnosed << '\''; | ||||
| 1966 | std::string LastAddedType = StartTypeAsDiagnosed.str(); | ||||
| 1967 | std::size_t NumElementsAdded = 1; | ||||
| 1968 | |||||
| 1969 | // However, the parameter's defined type might not be what the implicit | ||||
| 1970 | // conversion started with, e.g. if a typedef is found to convert. | ||||
| 1971 | std::string SeqBeginTypeStr = Conv.Begin.getAsString(PP); | ||||
| 1972 | std::string SeqEndTypeStr = Conv.End.getAsString(PP); | ||||
| 1973 | if (StartTypeAsDiagnosed != SeqBeginTypeStr) { | ||||
| 1974 | OS << " (as '" << SeqBeginTypeStr << "')"; | ||||
| 1975 | LastAddedType = SeqBeginTypeStr; | ||||
| 1976 | Trivial = false; | ||||
| 1977 | } | ||||
| 1978 | |||||
| 1979 | auto AddType = [&](StringRef ToAdd) { | ||||
| 1980 | if (LastAddedType != ToAdd && ToAdd != SeqEndTypeStr) { | ||||
| 1981 | OS << " -> '" << ToAdd << "'"; | ||||
| 1982 | LastAddedType = ToAdd.str(); | ||||
| 1983 | ++NumElementsAdded; | ||||
| 1984 | } | ||||
| 1985 | }; | ||||
| 1986 | for (QualType InvolvedType : Conv.getInvolvedTypesInSequence()) | ||||
| 1987 | // Print every type that's unique in the sequence into the diagnosis. | ||||
| 1988 | AddType(InvolvedType.getAsString(PP)); | ||||
| 1989 | |||||
| 1990 | if (LastAddedType != DestinationTypeAsDiagnosed) { | ||||
| 1991 | OS << " -> '" << DestinationTypeAsDiagnosed << "'"; | ||||
| 1992 | LastAddedType = DestinationTypeAsDiagnosed.str(); | ||||
| 1993 | ++NumElementsAdded; | ||||
| 1994 | } | ||||
| 1995 | |||||
| 1996 | // Same reasoning as with the Begin, e.g. if the converted-to type is a | ||||
| 1997 | // typedef, it will not be the same inside the conversion sequence (where | ||||
| 1998 | // the model already tore off typedefs) as in the code. | ||||
| 1999 | if (DestinationTypeAsDiagnosed != SeqEndTypeStr) { | ||||
| 2000 | OS << " (as '" << SeqEndTypeStr << "')"; | ||||
| 2001 | LastAddedType = SeqEndTypeStr; | ||||
| 2002 | Trivial = false; | ||||
| 2003 | } | ||||
| 2004 | |||||
| 2005 | if (Trivial && NumElementsAdded > 2) | ||||
| 2006 | // If the thing is still marked trivial but we have more than the | ||||
| 2007 | // from and to types added, it should not be trivial, and elaborated | ||||
| 2008 | // when printing the diagnostic. | ||||
| 2009 | Trivial = false; | ||||
| 2010 | } | ||||
| 2011 | }; | ||||
| 2012 | |||||
| 2013 | /// Retains the elements called with and returns whether the call is done with | ||||
| 2014 | /// a new element. | ||||
| 2015 | template <typename E, std::size_t N> class InsertOnce { | ||||
| 2016 | llvm::SmallSet<E, N> CalledWith; | ||||
| 2017 | |||||
| 2018 | public: | ||||
| 2019 | bool operator()(E El) { return CalledWith.insert(std::move(El)).second; } | ||||
| 2020 | |||||
| 2021 | bool calledWith(const E &El) const { return CalledWith.contains(El); } | ||||
| 2022 | }; | ||||
| 2023 | |||||
| 2024 | struct SwappedEqualQualTypePair { | ||||
| 2025 | QualType LHSType, RHSType; | ||||
| 2026 | |||||
| 2027 | bool operator==(const SwappedEqualQualTypePair &Other) const { | ||||
| 2028 | return (LHSType == Other.LHSType && RHSType == Other.RHSType) || | ||||
| 2029 | (LHSType == Other.RHSType && RHSType == Other.LHSType); | ||||
| 2030 | } | ||||
| 2031 | |||||
| 2032 | bool operator<(const SwappedEqualQualTypePair &Other) const { | ||||
| 2033 | return LHSType < Other.LHSType && RHSType < Other.RHSType; | ||||
| 2034 | } | ||||
| 2035 | }; | ||||
| 2036 | |||||
| 2037 | struct TypeAliasDiagnosticTuple { | ||||
| 2038 | QualType LHSType, RHSType, CommonType; | ||||
| 2039 | |||||
| 2040 | bool operator==(const TypeAliasDiagnosticTuple &Other) const { | ||||
| 2041 | return CommonType == Other.CommonType && | ||||
| 2042 | ((LHSType == Other.LHSType && RHSType == Other.RHSType) || | ||||
| 2043 | (LHSType == Other.RHSType && RHSType == Other.LHSType)); | ||||
| 2044 | } | ||||
| 2045 | |||||
| 2046 | bool operator<(const TypeAliasDiagnosticTuple &Other) const { | ||||
| 2047 | return CommonType < Other.CommonType && LHSType < Other.LHSType && | ||||
| 2048 | RHSType < Other.RHSType; | ||||
| 2049 | } | ||||
| 2050 | }; | ||||
| 2051 | |||||
| 2052 | /// Helper class to only emit a diagnostic related to MixFlags::TypeAlias once. | ||||
| 2053 | class UniqueTypeAliasDiagnosticHelper | ||||
| 2054 | : public InsertOnce<TypeAliasDiagnosticTuple, 8> { | ||||
| 2055 | using Base = InsertOnce<TypeAliasDiagnosticTuple, 8>; | ||||
| 2056 | |||||
| 2057 | public: | ||||
| 2058 | /// Returns whether the diagnostic for LHSType and RHSType which are both | ||||
| 2059 | /// referring to CommonType being the same has not been emitted already. | ||||
| 2060 | bool operator()(QualType LHSType, QualType RHSType, QualType CommonType) { | ||||
| 2061 | if (CommonType.isNull() || CommonType == LHSType || CommonType == RHSType) | ||||
| 2062 | return Base::operator()({LHSType, RHSType, {}}); | ||||
| 2063 | |||||
| 2064 | TypeAliasDiagnosticTuple ThreeTuple{LHSType, RHSType, CommonType}; | ||||
| 2065 | if (!Base::operator()(ThreeTuple)) | ||||
| 2066 | return false; | ||||
| 2067 | |||||
| 2068 | bool AlreadySaidLHSAndCommonIsSame = calledWith({LHSType, CommonType, {}}); | ||||
| 2069 | bool AlreadySaidRHSAndCommonIsSame = calledWith({RHSType, CommonType, {}}); | ||||
| 2070 | if (AlreadySaidLHSAndCommonIsSame && AlreadySaidRHSAndCommonIsSame) { | ||||
| 2071 | // "SomeInt == int" && "SomeOtherInt == int" => "Common(SomeInt, | ||||
| 2072 | // SomeOtherInt) == int", no need to diagnose it. Save the 3-tuple only | ||||
| 2073 | // for shortcut if it ever appears again. | ||||
| 2074 | return false; | ||||
| 2075 | } | ||||
| 2076 | |||||
| 2077 | return true; | ||||
| 2078 | } | ||||
| 2079 | }; | ||||
| 2080 | |||||
| 2081 | } // namespace | ||||
| 2082 | |||||
| 2083 | EasilySwappableParametersCheck::EasilySwappableParametersCheck( | ||||
| 2084 | StringRef Name, ClangTidyContext *Context) | ||||
| 2085 | : ClangTidyCheck(Name, Context), | ||||
| 2086 | MinimumLength(clampMinimumLength( | ||||
| 2087 | Options.get("MinimumLength", DefaultMinimumLength))), | ||||
| 2088 | IgnoredParameterNames(optutils::parseStringList( | ||||
| 2089 | Options.get("IgnoredParameterNames", DefaultIgnoredParameterNames))), | ||||
| 2090 | IgnoredParameterTypeSuffixes(optutils::parseStringList( | ||||
| 2091 | Options.get("IgnoredParameterTypeSuffixes", | ||||
| 2092 | DefaultIgnoredParameterTypeSuffixes))), | ||||
| 2093 | QualifiersMix(Options.get("QualifiersMix", DefaultQualifiersMix)), | ||||
| 2094 | ModelImplicitConversions(Options.get("ModelImplicitConversions", | ||||
| 2095 | DefaultModelImplicitConversions)), | ||||
| 2096 | SuppressParametersUsedTogether( | ||||
| 2097 | Options.get("SuppressParametersUsedTogether", | ||||
| 2098 | DefaultSuppressParametersUsedTogether)), | ||||
| 2099 | NamePrefixSuffixSilenceDissimilarityTreshold( | ||||
| 2100 | Options.get("NamePrefixSuffixSilenceDissimilarityTreshold", | ||||
| 2101 | DefaultNamePrefixSuffixSilenceDissimilarityTreshold)) {} | ||||
| 2102 | |||||
| 2103 | void EasilySwappableParametersCheck::storeOptions( | ||||
| 2104 | ClangTidyOptions::OptionMap &Opts) { | ||||
| 2105 | Options.store(Opts, "MinimumLength", MinimumLength); | ||||
| 2106 | Options.store(Opts, "IgnoredParameterNames", | ||||
| 2107 | optutils::serializeStringList(IgnoredParameterNames)); | ||||
| 2108 | Options.store(Opts, "IgnoredParameterTypeSuffixes", | ||||
| 2109 | optutils::serializeStringList(IgnoredParameterTypeSuffixes)); | ||||
| 2110 | Options.store(Opts, "QualifiersMix", QualifiersMix); | ||||
| 2111 | Options.store(Opts, "ModelImplicitConversions", ModelImplicitConversions); | ||||
| 2112 | Options.store(Opts, "SuppressParametersUsedTogether", | ||||
| 2113 | SuppressParametersUsedTogether); | ||||
| 2114 | Options.store(Opts, "NamePrefixSuffixSilenceDissimilarityTreshold", | ||||
| 2115 | NamePrefixSuffixSilenceDissimilarityTreshold); | ||||
| 2116 | } | ||||
| 2117 | |||||
| 2118 | void EasilySwappableParametersCheck::registerMatchers(MatchFinder *Finder) { | ||||
| 2119 | const auto BaseConstraints = functionDecl( | ||||
| 2120 | // Only report for definition nodes, as fixing the issues reported | ||||
| 2121 | // requires the user to be able to change code. | ||||
| 2122 | isDefinition(), parameterCountGE(MinimumLength), | ||||
| 2123 | unless(isOverloadedUnaryOrBinaryOperator())); | ||||
| 2124 | |||||
| 2125 | Finder->addMatcher( | ||||
| 2126 | functionDecl(BaseConstraints, | ||||
| 2127 | unless(ast_matchers::isTemplateInstantiation())) | ||||
| 2128 | .bind("func"), | ||||
| 2129 | this); | ||||
| 2130 | Finder->addMatcher( | ||||
| 2131 | functionDecl(BaseConstraints, isExplicitTemplateSpecialization()) | ||||
| 2132 | .bind("func"), | ||||
| 2133 | this); | ||||
| 2134 | } | ||||
| 2135 | |||||
| 2136 | void EasilySwappableParametersCheck::check( | ||||
| 2137 | const MatchFinder::MatchResult &Result) { | ||||
| 2138 | using namespace model; | ||||
| 2139 | using namespace filter; | ||||
| 2140 | |||||
| 2141 | const auto *FD = Result.Nodes.getNodeAs<FunctionDecl>("func"); | ||||
| 2142 | assert(FD)(static_cast <bool> (FD) ? void (0) : __assert_fail ("FD" , "clang-tools-extra/clang-tidy/bugprone/EasilySwappableParametersCheck.cpp" , 2142, __extension__ __PRETTY_FUNCTION__)); | ||||
| |||||
| 2143 | |||||
| 2144 | const PrintingPolicy &PP = FD->getASTContext().getPrintingPolicy(); | ||||
| 2145 | std::size_t NumParams = FD->getNumParams(); | ||||
| 2146 | std::size_t MixableRangeStartIndex = 0; | ||||
| 2147 | |||||
| 2148 | // Spawn one suppressor and if the user requested, gather information from | ||||
| 2149 | // the AST for the parameters' usages. | ||||
| 2150 | filter::SimilarlyUsedParameterPairSuppressor UsageBasedSuppressor{ | ||||
| 2151 | FD, SuppressParametersUsedTogether}; | ||||
| 2152 | |||||
| 2153 | LLVM_DEBUG(llvm::dbgs() << "Begin analysis of " << getName(FD) << " with "do { if (::llvm::DebugFlag && ::llvm::isCurrentDebugType ("EasilySwappableParametersCheck")) { llvm::dbgs() << "Begin analysis of " << getName(FD) << " with " << NumParams << " parameters...\n"; } } while (false) | ||||
| 2154 | << NumParams << " parameters...\n")do { if (::llvm::DebugFlag && ::llvm::isCurrentDebugType ("EasilySwappableParametersCheck")) { llvm::dbgs() << "Begin analysis of " << getName(FD) << " with " << NumParams << " parameters...\n"; } } while (false); | ||||
| 2155 | while (MixableRangeStartIndex < NumParams) { | ||||
| 2156 | if (isIgnoredParameter(*this, FD->getParamDecl(MixableRangeStartIndex))) { | ||||
| 2157 | LLVM_DEBUG(llvm::dbgs()do { if (::llvm::DebugFlag && ::llvm::isCurrentDebugType ("EasilySwappableParametersCheck")) { llvm::dbgs() << "Parameter #" << MixableRangeStartIndex << " ignored.\n"; } } while (false) | ||||
| 2158 | << "Parameter #" << MixableRangeStartIndex << " ignored.\n")do { if (::llvm::DebugFlag && ::llvm::isCurrentDebugType ("EasilySwappableParametersCheck")) { llvm::dbgs() << "Parameter #" << MixableRangeStartIndex << " ignored.\n"; } } while (false); | ||||
| 2159 | ++MixableRangeStartIndex; | ||||
| 2160 | continue; | ||||
| 2161 | } | ||||
| 2162 | |||||
| 2163 | MixableParameterRange R = modelMixingRange( | ||||
| 2164 | *this, FD, MixableRangeStartIndex, UsageBasedSuppressor); | ||||
| 2165 | assert(R.NumParamsChecked > 0 && "Ensure forward progress!")(static_cast <bool> (R.NumParamsChecked > 0 && "Ensure forward progress!") ? void (0) : __assert_fail ("R.NumParamsChecked > 0 && \"Ensure forward progress!\"" , "clang-tools-extra/clang-tidy/bugprone/EasilySwappableParametersCheck.cpp" , 2165, __extension__ __PRETTY_FUNCTION__)); | ||||
| 2166 | MixableRangeStartIndex += R.NumParamsChecked; | ||||
| 2167 | if (R.NumParamsChecked < MinimumLength) { | ||||
| 2168 | LLVM_DEBUG(llvm::dbgs() << "Ignoring range of " << R.NumParamsCheckeddo { if (::llvm::DebugFlag && ::llvm::isCurrentDebugType ("EasilySwappableParametersCheck")) { llvm::dbgs() << "Ignoring range of " << R.NumParamsChecked << " lower than limit.\n"; } } while (false) | ||||
| 2169 | << " lower than limit.\n")do { if (::llvm::DebugFlag && ::llvm::isCurrentDebugType ("EasilySwappableParametersCheck")) { llvm::dbgs() << "Ignoring range of " << R.NumParamsChecked << " lower than limit.\n"; } } while (false); | ||||
| 2170 | continue; | ||||
| 2171 | } | ||||
| 2172 | |||||
| 2173 | bool NeedsAnyTypeNote = llvm::any_of(R.Mixes, needsToPrintTypeInDiagnostic); | ||||
| 2174 | bool HasAnyImplicits = | ||||
| 2175 | llvm::any_of(R.Mixes, needsToElaborateImplicitConversion); | ||||
| 2176 | const ParmVarDecl *First = R.getFirstParam(), *Last = R.getLastParam(); | ||||
| 2177 | std::string FirstParamTypeAsWritten = First->getType().getAsString(PP); | ||||
| 2178 | { | ||||
| 2179 | StringRef DiagText; | ||||
| 2180 | |||||
| 2181 | if (HasAnyImplicits
| ||||
| 2182 | DiagText = "%0 adjacent parameters of %1 of convertible types are " | ||||
| 2183 | "easily swapped by mistake"; | ||||
| 2184 | else if (NeedsAnyTypeNote
| ||||
| 2185 | DiagText = "%0 adjacent parameters of %1 of similar type are easily " | ||||
| 2186 | "swapped by mistake"; | ||||
| 2187 | else | ||||
| 2188 | DiagText = "%0 adjacent parameters of %1 of similar type ('%2') are " | ||||
| 2189 | "easily swapped by mistake"; | ||||
| 2190 | |||||
| 2191 | auto Diag = diag(First->getOuterLocStart(), DiagText) | ||||
| 2192 | << static_cast<unsigned>(R.NumParamsChecked) << FD; | ||||
| 2193 | if (!NeedsAnyTypeNote
| ||||
| 2194 | Diag << FirstParamTypeAsWritten; | ||||
| 2195 | |||||
| 2196 | CharSourceRange HighlightRange = CharSourceRange::getTokenRange( | ||||
| 2197 | First->getBeginLoc(), Last->getEndLoc()); | ||||
| 2198 | Diag << HighlightRange; | ||||
| 2199 | } | ||||
| 2200 | |||||
| 2201 | // There is a chance that the previous highlight did not succeed, e.g. when | ||||
| 2202 | // the two parameters are on different lines. For clarity, show the user | ||||
| 2203 | // the involved variable explicitly. | ||||
| 2204 | diag(First->getLocation(), "the first parameter in the range is '%0'", | ||||
| 2205 | DiagnosticIDs::Note) | ||||
| 2206 | << getNameOrUnnamed(First) | ||||
| 2207 | << CharSourceRange::getTokenRange(First->getLocation(), | ||||
| 2208 | First->getLocation()); | ||||
| 2209 | diag(Last->getLocation(), "the last parameter in the range is '%0'", | ||||
| 2210 | DiagnosticIDs::Note) | ||||
| 2211 | << getNameOrUnnamed(Last) | ||||
| 2212 | << CharSourceRange::getTokenRange(Last->getLocation(), | ||||
| 2213 | Last->getLocation()); | ||||
| 2214 | |||||
| 2215 | // Helper classes to silence elaborative diagnostic notes that would be | ||||
| 2216 | // too verbose. | ||||
| 2217 | UniqueTypeAliasDiagnosticHelper UniqueTypeAlias; | ||||
| 2218 | InsertOnce<SwappedEqualQualTypePair, 8> UniqueBindPower; | ||||
| 2219 | InsertOnce<SwappedEqualQualTypePair, 8> UniqueImplicitConversion; | ||||
| 2220 | |||||
| 2221 | for (const model::Mix &M : R.Mixes) { | ||||
| 2222 | assert(M.mixable() && "Sentinel or false mix in result.")(static_cast <bool> (M.mixable() && "Sentinel or false mix in result." ) ? void (0) : __assert_fail ("M.mixable() && \"Sentinel or false mix in result.\"" , "clang-tools-extra/clang-tidy/bugprone/EasilySwappableParametersCheck.cpp" , 2222, __extension__ __PRETTY_FUNCTION__)); | ||||
| 2223 | if (!needsToPrintTypeInDiagnostic(M) && | ||||
| 2224 | !needsToElaborateImplicitConversion(M)) | ||||
| 2225 | continue; | ||||
| 2226 | |||||
| 2227 | // Typedefs might result in the type of the variable needing to be | ||||
| 2228 | // emitted to a note diagnostic, so prepare it. | ||||
| 2229 | const ParmVarDecl *LVar = M.First; | ||||
| 2230 | const ParmVarDecl *RVar = M.Second; | ||||
| 2231 | QualType LType = LVar->getType(); | ||||
| 2232 | QualType RType = RVar->getType(); | ||||
| 2233 | QualType CommonType = M.commonUnderlyingType(); | ||||
| 2234 | std::string LTypeStr = LType.getAsString(PP); | ||||
| 2235 | std::string RTypeStr = RType.getAsString(PP); | ||||
| 2236 | std::string CommonTypeStr = CommonType.getAsString(PP); | ||||
| 2237 | |||||
| 2238 | if (hasFlag(M.flags(), MixFlags::TypeAlias) && | ||||
| 2239 | UniqueTypeAlias(LType, RType, CommonType)) { | ||||
| 2240 | StringRef DiagText; | ||||
| 2241 | bool ExplicitlyPrintCommonType = false; | ||||
| 2242 | if (LTypeStr == CommonTypeStr || RTypeStr == CommonTypeStr) { | ||||
| 2243 | if (hasFlag(M.flags(), MixFlags::Qualifiers)) | ||||
| 2244 | DiagText = "after resolving type aliases, '%0' and '%1' share a " | ||||
| 2245 | "common type"; | ||||
| 2246 | else | ||||
| 2247 | DiagText = | ||||
| 2248 | "after resolving type aliases, '%0' and '%1' are the same"; | ||||
| 2249 | } else if (!CommonType.isNull()) { | ||||
| 2250 | DiagText = "after resolving type aliases, the common type of '%0' " | ||||
| 2251 | "and '%1' is '%2'"; | ||||
| 2252 | ExplicitlyPrintCommonType = true; | ||||
| 2253 | } | ||||
| 2254 | |||||
| 2255 | auto Diag = | ||||
| 2256 | diag(LVar->getOuterLocStart(), DiagText, DiagnosticIDs::Note) | ||||
| 2257 | << LTypeStr << RTypeStr; | ||||
| 2258 | if (ExplicitlyPrintCommonType) | ||||
| 2259 | Diag << CommonTypeStr; | ||||
| 2260 | } | ||||
| 2261 | |||||
| 2262 | if ((hasFlag(M.flags(), MixFlags::ReferenceBind) || | ||||
| 2263 | hasFlag(M.flags(), MixFlags::Qualifiers)) && | ||||
| 2264 | UniqueBindPower({LType, RType})) { | ||||
| 2265 | StringRef DiagText = "'%0' and '%1' parameters accept and bind the " | ||||
| 2266 | "same kind of values"; | ||||
| 2267 | diag(RVar->getOuterLocStart(), DiagText, DiagnosticIDs::Note) | ||||
| 2268 | << LTypeStr << RTypeStr; | ||||
| 2269 | } | ||||
| 2270 | |||||
| 2271 | if (needsToElaborateImplicitConversion(M) && | ||||
| 2272 | UniqueImplicitConversion({LType, RType})) { | ||||
| 2273 | const model::ConversionSequence <R = | ||||
| 2274 | M.leftToRightConversionSequence(); | ||||
| 2275 | const model::ConversionSequence &RTL = | ||||
| 2276 | M.rightToLeftConversionSequence(); | ||||
| 2277 | FormattedConversionSequence LTRFmt{PP, LTypeStr, LTR, RTypeStr}; | ||||
| 2278 | FormattedConversionSequence RTLFmt{PP, RTypeStr, RTL, LTypeStr}; | ||||
| 2279 | |||||
| 2280 | StringRef DiagText = "'%0' and '%1' may be implicitly converted"; | ||||
| 2281 | if (!LTRFmt.Trivial || !RTLFmt.Trivial) | ||||
| 2282 | DiagText = "'%0' and '%1' may be implicitly converted: %2, %3"; | ||||
| 2283 | |||||
| 2284 | { | ||||
| 2285 | auto Diag = | ||||
| 2286 | diag(RVar->getOuterLocStart(), DiagText, DiagnosticIDs::Note) | ||||
| 2287 | << LTypeStr << RTypeStr; | ||||
| 2288 | |||||
| 2289 | if (!LTRFmt.Trivial || !RTLFmt.Trivial) | ||||
| 2290 | Diag << LTRFmt.DiagnosticText << RTLFmt.DiagnosticText; | ||||
| 2291 | } | ||||
| 2292 | |||||
| 2293 | StringRef ConversionFunctionDiagText = | ||||
| 2294 | "the implicit conversion involves the " | ||||
| 2295 | "%select{|converting constructor|conversion operator}0 " | ||||
| 2296 | "declared here"; | ||||
| 2297 | if (const FunctionDecl *LFD = LTR.getUserDefinedConversionFunction()) | ||||
| 2298 | diag(LFD->getLocation(), ConversionFunctionDiagText, | ||||
| 2299 | DiagnosticIDs::Note) | ||||
| 2300 | << static_cast<unsigned>(LTR.UDConvKind) | ||||
| 2301 | << LTR.getUserDefinedConversionHighlight(); | ||||
| 2302 | if (const FunctionDecl *RFD = RTL.getUserDefinedConversionFunction()) | ||||
| 2303 | diag(RFD->getLocation(), ConversionFunctionDiagText, | ||||
| 2304 | DiagnosticIDs::Note) | ||||
| 2305 | << static_cast<unsigned>(RTL.UDConvKind) | ||||
| 2306 | << RTL.getUserDefinedConversionHighlight(); | ||||
| 2307 | } | ||||
| 2308 | } | ||||
| 2309 | } | ||||
| 2310 | } | ||||
| 2311 | |||||
| 2312 | } // namespace clang::tidy::bugprone |
| 1 | //===- Diagnostic.h - C Language Family Diagnostic Handling -----*- C++ -*-===// | ||||
| 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 | /// \file | ||||
| 10 | /// Defines the Diagnostic-related interfaces. | ||||
| 11 | // | ||||
| 12 | //===----------------------------------------------------------------------===// | ||||
| 13 | |||||
| 14 | #ifndef LLVM_CLANG_BASIC_DIAGNOSTIC_H | ||||
| 15 | #define LLVM_CLANG_BASIC_DIAGNOSTIC_H | ||||
| 16 | |||||
| 17 | #include "clang/Basic/DiagnosticIDs.h" | ||||
| 18 | #include "clang/Basic/DiagnosticOptions.h" | ||||
| 19 | #include "clang/Basic/SourceLocation.h" | ||||
| 20 | #include "clang/Basic/Specifiers.h" | ||||
| 21 | #include "llvm/ADT/ArrayRef.h" | ||||
| 22 | #include "llvm/ADT/DenseMap.h" | ||||
| 23 | #include "llvm/ADT/IntrusiveRefCntPtr.h" | ||||
| 24 | #include "llvm/ADT/SmallVector.h" | ||||
| 25 | #include "llvm/ADT/StringRef.h" | ||||
| 26 | #include "llvm/ADT/iterator_range.h" | ||||
| 27 | #include "llvm/Support/Compiler.h" | ||||
| 28 | #include <cassert> | ||||
| 29 | #include <cstdint> | ||||
| 30 | #include <limits> | ||||
| 31 | #include <list> | ||||
| 32 | #include <map> | ||||
| 33 | #include <memory> | ||||
| 34 | #include <optional> | ||||
| 35 | #include <string> | ||||
| 36 | #include <type_traits> | ||||
| 37 | #include <utility> | ||||
| 38 | #include <vector> | ||||
| 39 | |||||
| 40 | namespace llvm { | ||||
| 41 | class Error; | ||||
| 42 | class raw_ostream; | ||||
| 43 | } // namespace llvm | ||||
| 44 | |||||
| 45 | namespace clang { | ||||
| 46 | |||||
| 47 | class DeclContext; | ||||
| 48 | class DiagnosticBuilder; | ||||
| 49 | class DiagnosticConsumer; | ||||
| 50 | class IdentifierInfo; | ||||
| 51 | class LangOptions; | ||||
| 52 | class Preprocessor; | ||||
| 53 | class SourceManager; | ||||
| 54 | class StoredDiagnostic; | ||||
| 55 | |||||
| 56 | namespace tok { | ||||
| 57 | |||||
| 58 | enum TokenKind : unsigned short; | ||||
| 59 | |||||
| 60 | } // namespace tok | ||||
| 61 | |||||
| 62 | /// Annotates a diagnostic with some code that should be | ||||
| 63 | /// inserted, removed, or replaced to fix the problem. | ||||
| 64 | /// | ||||
| 65 | /// This kind of hint should be used when we are certain that the | ||||
| 66 | /// introduction, removal, or modification of a particular (small!) | ||||
| 67 | /// amount of code will correct a compilation error. The compiler | ||||
| 68 | /// should also provide full recovery from such errors, such that | ||||
| 69 | /// suppressing the diagnostic output can still result in successful | ||||
| 70 | /// compilation. | ||||
| 71 | class FixItHint { | ||||
| 72 | public: | ||||
| 73 | /// Code that should be replaced to correct the error. Empty for an | ||||
| 74 | /// insertion hint. | ||||
| 75 | CharSourceRange RemoveRange; | ||||
| 76 | |||||
| 77 | /// Code in the specific range that should be inserted in the insertion | ||||
| 78 | /// location. | ||||
| 79 | CharSourceRange InsertFromRange; | ||||
| 80 | |||||
| 81 | /// The actual code to insert at the insertion location, as a | ||||
| 82 | /// string. | ||||
| 83 | std::string CodeToInsert; | ||||
| 84 | |||||
| 85 | bool BeforePreviousInsertions = false; | ||||
| 86 | |||||
| 87 | /// Empty code modification hint, indicating that no code | ||||
| 88 | /// modification is known. | ||||
| 89 | FixItHint() = default; | ||||
| 90 | |||||
| 91 | bool isNull() const { | ||||
| 92 | return !RemoveRange.isValid(); | ||||
| 93 | } | ||||
| 94 | |||||
| 95 | /// Create a code modification hint that inserts the given | ||||
| 96 | /// code string at a specific location. | ||||
| 97 | static FixItHint CreateInsertion(SourceLocation InsertionLoc, | ||||
| 98 | StringRef Code, | ||||
| 99 | bool BeforePreviousInsertions = false) { | ||||
| 100 | FixItHint Hint; | ||||
| 101 | Hint.RemoveRange = | ||||
| 102 | CharSourceRange::getCharRange(InsertionLoc, InsertionLoc); | ||||
| 103 | Hint.CodeToInsert = std::string(Code); | ||||
| 104 | Hint.BeforePreviousInsertions = BeforePreviousInsertions; | ||||
| 105 | return Hint; | ||||
| 106 | } | ||||
| 107 | |||||
| 108 | /// Create a code modification hint that inserts the given | ||||
| 109 | /// code from \p FromRange at a specific location. | ||||
| 110 | static FixItHint CreateInsertionFromRange(SourceLocation InsertionLoc, | ||||
| 111 | CharSourceRange FromRange, | ||||
| 112 | bool BeforePreviousInsertions = false) { | ||||
| 113 | FixItHint Hint; | ||||
| 114 | Hint.RemoveRange = | ||||
| 115 | CharSourceRange::getCharRange(InsertionLoc, InsertionLoc); | ||||
| 116 | Hint.InsertFromRange = FromRange; | ||||
| 117 | Hint.BeforePreviousInsertions = BeforePreviousInsertions; | ||||
| 118 | return Hint; | ||||
| 119 | } | ||||
| 120 | |||||
| 121 | /// Create a code modification hint that removes the given | ||||
| 122 | /// source range. | ||||
| 123 | static FixItHint CreateRemoval(CharSourceRange RemoveRange) { | ||||
| 124 | FixItHint Hint; | ||||
| 125 | Hint.RemoveRange = RemoveRange; | ||||
| 126 | return Hint; | ||||
| 127 | } | ||||
| 128 | static FixItHint CreateRemoval(SourceRange RemoveRange) { | ||||
| 129 | return CreateRemoval(CharSourceRange::getTokenRange(RemoveRange)); | ||||
| 130 | } | ||||
| 131 | |||||
| 132 | /// Create a code modification hint that replaces the given | ||||
| 133 | /// source range with the given code string. | ||||
| 134 | static FixItHint CreateReplacement(CharSourceRange RemoveRange, | ||||
| 135 | StringRef Code) { | ||||
| 136 | FixItHint Hint; | ||||
| 137 | Hint.RemoveRange = RemoveRange; | ||||
| 138 | Hint.CodeToInsert = std::string(Code); | ||||
| 139 | return Hint; | ||||
| 140 | } | ||||
| 141 | |||||
| 142 | static FixItHint CreateReplacement(SourceRange RemoveRange, | ||||
| 143 | StringRef Code) { | ||||
| 144 | return CreateReplacement(CharSourceRange::getTokenRange(RemoveRange), Code); | ||||
| 145 | } | ||||
| 146 | }; | ||||
| 147 | |||||
| 148 | struct DiagnosticStorage { | ||||
| 149 | enum { | ||||
| 150 | /// The maximum number of arguments we can hold. We | ||||
| 151 | /// currently only support up to 10 arguments (%0-%9). | ||||
| 152 | /// | ||||
| 153 | /// A single diagnostic with more than that almost certainly has to | ||||
| 154 | /// be simplified anyway. | ||||
| 155 | MaxArguments = 10 | ||||
| 156 | }; | ||||
| 157 | |||||
| 158 | /// The number of entries in Arguments. | ||||
| 159 | unsigned char NumDiagArgs = 0; | ||||
| 160 | |||||
| 161 | /// Specifies for each argument whether it is in DiagArgumentsStr | ||||
| 162 | /// or in DiagArguments. | ||||
| 163 | unsigned char DiagArgumentsKind[MaxArguments]; | ||||
| 164 | |||||
| 165 | /// The values for the various substitution positions. | ||||
| 166 | /// | ||||
| 167 | /// This is used when the argument is not an std::string. The specific value | ||||
| 168 | /// is mangled into an uint64_t and the interpretation depends on exactly | ||||
| 169 | /// what sort of argument kind it is. | ||||
| 170 | uint64_t DiagArgumentsVal[MaxArguments]; | ||||
| 171 | |||||
| 172 | /// The values for the various substitution positions that have | ||||
| 173 | /// string arguments. | ||||
| 174 | std::string DiagArgumentsStr[MaxArguments]; | ||||
| 175 | |||||
| 176 | /// The list of ranges added to this diagnostic. | ||||
| 177 | SmallVector<CharSourceRange, 8> DiagRanges; | ||||
| 178 | |||||
| 179 | /// If valid, provides a hint with some code to insert, remove, or | ||||
| 180 | /// modify at a particular position. | ||||
| 181 | SmallVector<FixItHint, 6> FixItHints; | ||||
| 182 | |||||
| 183 | DiagnosticStorage() = default; | ||||
| 184 | }; | ||||
| 185 | |||||
| 186 | /// Concrete class used by the front-end to report problems and issues. | ||||
| 187 | /// | ||||
| 188 | /// This massages the diagnostics (e.g. handling things like "report warnings | ||||
| 189 | /// as errors" and passes them off to the DiagnosticConsumer for reporting to | ||||
| 190 | /// the user. DiagnosticsEngine is tied to one translation unit and one | ||||
| 191 | /// SourceManager. | ||||
| 192 | class DiagnosticsEngine : public RefCountedBase<DiagnosticsEngine> { | ||||
| 193 | public: | ||||
| 194 | /// The level of the diagnostic, after it has been through mapping. | ||||
| 195 | enum Level { | ||||
| 196 | Ignored = DiagnosticIDs::Ignored, | ||||
| 197 | Note = DiagnosticIDs::Note, | ||||
| 198 | Remark = DiagnosticIDs::Remark, | ||||
| 199 | Warning = DiagnosticIDs::Warning, | ||||
| 200 | Error = DiagnosticIDs::Error, | ||||
| 201 | Fatal = DiagnosticIDs::Fatal | ||||
| 202 | }; | ||||
| 203 | |||||
| 204 | enum ArgumentKind { | ||||
| 205 | /// std::string | ||||
| 206 | ak_std_string, | ||||
| 207 | |||||
| 208 | /// const char * | ||||
| 209 | ak_c_string, | ||||
| 210 | |||||
| 211 | /// int | ||||
| 212 | ak_sint, | ||||
| 213 | |||||
| 214 | /// unsigned | ||||
| 215 | ak_uint, | ||||
| 216 | |||||
| 217 | /// enum TokenKind : unsigned | ||||
| 218 | ak_tokenkind, | ||||
| 219 | |||||
| 220 | /// IdentifierInfo | ||||
| 221 | ak_identifierinfo, | ||||
| 222 | |||||
| 223 | /// address space | ||||
| 224 | ak_addrspace, | ||||
| 225 | |||||
| 226 | /// Qualifiers | ||||
| 227 | ak_qual, | ||||
| 228 | |||||
| 229 | /// QualType | ||||
| 230 | ak_qualtype, | ||||
| 231 | |||||
| 232 | /// DeclarationName | ||||
| 233 | ak_declarationname, | ||||
| 234 | |||||
| 235 | /// NamedDecl * | ||||
| 236 | ak_nameddecl, | ||||
| 237 | |||||
| 238 | /// NestedNameSpecifier * | ||||
| 239 | ak_nestednamespec, | ||||
| 240 | |||||
| 241 | /// DeclContext * | ||||
| 242 | ak_declcontext, | ||||
| 243 | |||||
| 244 | /// pair<QualType, QualType> | ||||
| 245 | ak_qualtype_pair, | ||||
| 246 | |||||
| 247 | /// Attr * | ||||
| 248 | ak_attr | ||||
| 249 | }; | ||||
| 250 | |||||
| 251 | /// Represents on argument value, which is a union discriminated | ||||
| 252 | /// by ArgumentKind, with a value. | ||||
| 253 | using ArgumentValue = std::pair<ArgumentKind, intptr_t>; | ||||
| 254 | |||||
| 255 | private: | ||||
| 256 | // Used by __extension__ | ||||
| 257 | unsigned char AllExtensionsSilenced = 0; | ||||
| 258 | |||||
| 259 | // Treat fatal errors like errors. | ||||
| 260 | bool FatalsAsError = false; | ||||
| 261 | |||||
| 262 | // Suppress all diagnostics. | ||||
| 263 | bool SuppressAllDiagnostics = false; | ||||
| 264 | |||||
| 265 | // Elide common types of templates. | ||||
| 266 | bool ElideType = true; | ||||
| 267 | |||||
| 268 | // Print a tree when comparing templates. | ||||
| 269 | bool PrintTemplateTree = false; | ||||
| 270 | |||||
| 271 | // Color printing is enabled. | ||||
| 272 | bool ShowColors = false; | ||||
| 273 | |||||
| 274 | // Which overload candidates to show. | ||||
| 275 | OverloadsShown ShowOverloads = Ovl_All; | ||||
| 276 | |||||
| 277 | // With Ovl_Best, the number of overload candidates to show when we encounter | ||||
| 278 | // an error. | ||||
| 279 | // | ||||
| 280 | // The value here is the number of candidates to show in the first nontrivial | ||||
| 281 | // error. Future errors may show a different number of candidates. | ||||
| 282 | unsigned NumOverloadsToShow = 32; | ||||
| 283 | |||||
| 284 | // Cap of # errors emitted, 0 -> no limit. | ||||
| 285 | unsigned ErrorLimit = 0; | ||||
| 286 | |||||
| 287 | // Cap on depth of template backtrace stack, 0 -> no limit. | ||||
| 288 | unsigned TemplateBacktraceLimit = 0; | ||||
| 289 | |||||
| 290 | // Cap on depth of constexpr evaluation backtrace stack, 0 -> no limit. | ||||
| 291 | unsigned ConstexprBacktraceLimit = 0; | ||||
| 292 | |||||
| 293 | IntrusiveRefCntPtr<DiagnosticIDs> Diags; | ||||
| 294 | IntrusiveRefCntPtr<DiagnosticOptions> DiagOpts; | ||||
| 295 | DiagnosticConsumer *Client = nullptr; | ||||
| 296 | std::unique_ptr<DiagnosticConsumer> Owner; | ||||
| 297 | SourceManager *SourceMgr = nullptr; | ||||
| 298 | |||||
| 299 | /// Mapping information for diagnostics. | ||||
| 300 | /// | ||||
| 301 | /// Mapping info is packed into four bits per diagnostic. The low three | ||||
| 302 | /// bits are the mapping (an instance of diag::Severity), or zero if unset. | ||||
| 303 | /// The high bit is set when the mapping was established as a user mapping. | ||||
| 304 | /// If the high bit is clear, then the low bits are set to the default | ||||
| 305 | /// value, and should be mapped with -pedantic, -Werror, etc. | ||||
| 306 | /// | ||||
| 307 | /// A new DiagState is created and kept around when diagnostic pragmas modify | ||||
| 308 | /// the state so that we know what is the diagnostic state at any given | ||||
| 309 | /// source location. | ||||
| 310 | class DiagState { | ||||
| 311 | llvm::DenseMap<unsigned, DiagnosticMapping> DiagMap; | ||||
| 312 | |||||
| 313 | public: | ||||
| 314 | // "Global" configuration state that can actually vary between modules. | ||||
| 315 | |||||
| 316 | // Ignore all warnings: -w | ||||
| 317 | unsigned IgnoreAllWarnings : 1; | ||||
| 318 | |||||
| 319 | // Enable all warnings. | ||||
| 320 | unsigned EnableAllWarnings : 1; | ||||
| 321 | |||||
| 322 | // Treat warnings like errors. | ||||
| 323 | unsigned WarningsAsErrors : 1; | ||||
| 324 | |||||
| 325 | // Treat errors like fatal errors. | ||||
| 326 | unsigned ErrorsAsFatal : 1; | ||||
| 327 | |||||
| 328 | // Suppress warnings in system headers. | ||||
| 329 | unsigned SuppressSystemWarnings : 1; | ||||
| 330 | |||||
| 331 | // Map extensions to warnings or errors? | ||||
| 332 | diag::Severity ExtBehavior = diag::Severity::Ignored; | ||||
| 333 | |||||
| 334 | DiagState() | ||||
| 335 | : IgnoreAllWarnings(false), EnableAllWarnings(false), | ||||
| 336 | WarningsAsErrors(false), ErrorsAsFatal(false), | ||||
| 337 | SuppressSystemWarnings(false) {} | ||||
| 338 | |||||
| 339 | using iterator = llvm::DenseMap<unsigned, DiagnosticMapping>::iterator; | ||||
| 340 | using const_iterator = | ||||
| 341 | llvm::DenseMap<unsigned, DiagnosticMapping>::const_iterator; | ||||
| 342 | |||||
| 343 | void setMapping(diag::kind Diag, DiagnosticMapping Info) { | ||||
| 344 | DiagMap[Diag] = Info; | ||||
| 345 | } | ||||
| 346 | |||||
| 347 | DiagnosticMapping lookupMapping(diag::kind Diag) const { | ||||
| 348 | return DiagMap.lookup(Diag); | ||||
| 349 | } | ||||
| 350 | |||||
| 351 | DiagnosticMapping &getOrAddMapping(diag::kind Diag); | ||||
| 352 | |||||
| 353 | const_iterator begin() const { return DiagMap.begin(); } | ||||
| 354 | const_iterator end() const { return DiagMap.end(); } | ||||
| 355 | }; | ||||
| 356 | |||||
| 357 | /// Keeps and automatically disposes all DiagStates that we create. | ||||
| 358 | std::list<DiagState> DiagStates; | ||||
| 359 | |||||
| 360 | /// A mapping from files to the diagnostic states for those files. Lazily | ||||
| 361 | /// built on demand for files in which the diagnostic state has not changed. | ||||
| 362 | class DiagStateMap { | ||||
| 363 | public: | ||||
| 364 | /// Add an initial diagnostic state. | ||||
| 365 | void appendFirst(DiagState *State); | ||||
| 366 | |||||
| 367 | /// Add a new latest state point. | ||||
| 368 | void append(SourceManager &SrcMgr, SourceLocation Loc, DiagState *State); | ||||
| 369 | |||||
| 370 | /// Look up the diagnostic state at a given source location. | ||||
| 371 | DiagState *lookup(SourceManager &SrcMgr, SourceLocation Loc) const; | ||||
| 372 | |||||
| 373 | /// Determine whether this map is empty. | ||||
| 374 | bool empty() const { return Files.empty(); } | ||||
| 375 | |||||
| 376 | /// Clear out this map. | ||||
| 377 | void clear() { | ||||
| 378 | Files.clear(); | ||||
| 379 | FirstDiagState = CurDiagState = nullptr; | ||||
| 380 | CurDiagStateLoc = SourceLocation(); | ||||
| 381 | } | ||||
| 382 | |||||
| 383 | /// Produce a debugging dump of the diagnostic state. | ||||
| 384 | LLVM_DUMP_METHOD__attribute__((noinline)) __attribute__((__used__)) void dump(SourceManager &SrcMgr, | ||||
| 385 | StringRef DiagName = StringRef()) const; | ||||
| 386 | |||||
| 387 | /// Grab the most-recently-added state point. | ||||
| 388 | DiagState *getCurDiagState() const { return CurDiagState; } | ||||
| 389 | |||||
| 390 | /// Get the location at which a diagnostic state was last added. | ||||
| 391 | SourceLocation getCurDiagStateLoc() const { return CurDiagStateLoc; } | ||||
| 392 | |||||
| 393 | private: | ||||
| 394 | friend class ASTReader; | ||||
| 395 | friend class ASTWriter; | ||||
| 396 | |||||
| 397 | /// Represents a point in source where the diagnostic state was | ||||
| 398 | /// modified because of a pragma. | ||||
| 399 | /// | ||||
| 400 | /// 'Loc' can be null if the point represents the diagnostic state | ||||
| 401 | /// modifications done through the command-line. | ||||
| 402 | struct DiagStatePoint { | ||||
| 403 | DiagState *State; | ||||
| 404 | unsigned Offset; | ||||
| 405 | |||||
| 406 | DiagStatePoint(DiagState *State, unsigned Offset) | ||||
| 407 | : State(State), Offset(Offset) {} | ||||
| 408 | }; | ||||
| 409 | |||||
| 410 | /// Description of the diagnostic states and state transitions for a | ||||
| 411 | /// particular FileID. | ||||
| 412 | struct File { | ||||
| 413 | /// The diagnostic state for the parent file. This is strictly redundant, | ||||
| 414 | /// as looking up the DecomposedIncludedLoc for the FileID in the Files | ||||
| 415 | /// map would give us this, but we cache it here for performance. | ||||
| 416 | File *Parent = nullptr; | ||||
| 417 | |||||
| 418 | /// The offset of this file within its parent. | ||||
| 419 | unsigned ParentOffset = 0; | ||||
| 420 | |||||
| 421 | /// Whether this file has any local (not imported from an AST file) | ||||
| 422 | /// diagnostic state transitions. | ||||
| 423 | bool HasLocalTransitions = false; | ||||
| 424 | |||||
| 425 | /// The points within the file where the state changes. There will always | ||||
| 426 | /// be at least one of these (the state on entry to the file). | ||||
| 427 | llvm::SmallVector<DiagStatePoint, 4> StateTransitions; | ||||
| 428 | |||||
| 429 | DiagState *lookup(unsigned Offset) const; | ||||
| 430 | }; | ||||
| 431 | |||||
| 432 | /// The diagnostic states for each file. | ||||
| 433 | mutable std::map<FileID, File> Files; | ||||
| 434 | |||||
| 435 | /// The initial diagnostic state. | ||||
| 436 | DiagState *FirstDiagState; | ||||
| 437 | |||||
| 438 | /// The current diagnostic state. | ||||
| 439 | DiagState *CurDiagState; | ||||
| 440 | |||||
| 441 | /// The location at which the current diagnostic state was established. | ||||
| 442 | SourceLocation CurDiagStateLoc; | ||||
| 443 | |||||
| 444 | /// Get the diagnostic state information for a file. | ||||
| 445 | File *getFile(SourceManager &SrcMgr, FileID ID) const; | ||||
| 446 | }; | ||||
| 447 | |||||
| 448 | DiagStateMap DiagStatesByLoc; | ||||
| 449 | |||||
| 450 | /// Keeps the DiagState that was active during each diagnostic 'push' | ||||
| 451 | /// so we can get back at it when we 'pop'. | ||||
| 452 | std::vector<DiagState *> DiagStateOnPushStack; | ||||
| 453 | |||||
| 454 | DiagState *GetCurDiagState() const { | ||||
| 455 | return DiagStatesByLoc.getCurDiagState(); | ||||
| 456 | } | ||||
| 457 | |||||
| 458 | void PushDiagStatePoint(DiagState *State, SourceLocation L); | ||||
| 459 | |||||
| 460 | /// Finds the DiagStatePoint that contains the diagnostic state of | ||||
| 461 | /// the given source location. | ||||
| 462 | DiagState *GetDiagStateForLoc(SourceLocation Loc) const { | ||||
| 463 | return SourceMgr ? DiagStatesByLoc.lookup(*SourceMgr, Loc) | ||||
| 464 | : DiagStatesByLoc.getCurDiagState(); | ||||
| 465 | } | ||||
| 466 | |||||
| 467 | /// Sticky flag set to \c true when an error is emitted. | ||||
| 468 | bool ErrorOccurred; | ||||
| 469 | |||||
| 470 | /// Sticky flag set to \c true when an "uncompilable error" occurs. | ||||
| 471 | /// I.e. an error that was not upgraded from a warning by -Werror. | ||||
| 472 | bool UncompilableErrorOccurred; | ||||
| 473 | |||||
| 474 | /// Sticky flag set to \c true when a fatal error is emitted. | ||||
| 475 | bool FatalErrorOccurred; | ||||
| 476 | |||||
| 477 | /// Indicates that an unrecoverable error has occurred. | ||||
| 478 | bool UnrecoverableErrorOccurred; | ||||
| 479 | |||||
| 480 | /// Counts for DiagnosticErrorTrap to check whether an error occurred | ||||
| 481 | /// during a parsing section, e.g. during parsing a function. | ||||
| 482 | unsigned TrapNumErrorsOccurred; | ||||
| 483 | unsigned TrapNumUnrecoverableErrorsOccurred; | ||||
| 484 | |||||
| 485 | /// The level of the last diagnostic emitted. | ||||
| 486 | /// | ||||
| 487 | /// This is used to emit continuation diagnostics with the same level as the | ||||
| 488 | /// diagnostic that they follow. | ||||
| 489 | DiagnosticIDs::Level LastDiagLevel; | ||||
| 490 | |||||
| 491 | /// Number of warnings reported | ||||
| 492 | unsigned NumWarnings; | ||||
| 493 | |||||
| 494 | /// Number of errors reported | ||||
| 495 | unsigned NumErrors; | ||||
| 496 | |||||
| 497 | /// A function pointer that converts an opaque diagnostic | ||||
| 498 | /// argument to a strings. | ||||
| 499 | /// | ||||
| 500 | /// This takes the modifiers and argument that was present in the diagnostic. | ||||
| 501 | /// | ||||
| 502 | /// The PrevArgs array indicates the previous arguments formatted for this | ||||
| 503 | /// diagnostic. Implementations of this function can use this information to | ||||
| 504 | /// avoid redundancy across arguments. | ||||
| 505 | /// | ||||
| 506 | /// This is a hack to avoid a layering violation between libbasic and libsema. | ||||
| 507 | using ArgToStringFnTy = void (*)( | ||||
| 508 | ArgumentKind Kind, intptr_t Val, | ||||
| 509 | StringRef Modifier, StringRef Argument, | ||||
| 510 | ArrayRef<ArgumentValue> PrevArgs, | ||||
| 511 | SmallVectorImpl<char> &Output, | ||||
| 512 | void *Cookie, | ||||
| 513 | ArrayRef<intptr_t> QualTypeVals); | ||||
| 514 | |||||
| 515 | void *ArgToStringCookie = nullptr; | ||||
| 516 | ArgToStringFnTy ArgToStringFn; | ||||
| 517 | |||||
| 518 | /// ID of the "delayed" diagnostic, which is a (typically | ||||
| 519 | /// fatal) diagnostic that had to be delayed because it was found | ||||
| 520 | /// while emitting another diagnostic. | ||||
| 521 | unsigned DelayedDiagID; | ||||
| 522 | |||||
| 523 | /// First string argument for the delayed diagnostic. | ||||
| 524 | std::string DelayedDiagArg1; | ||||
| 525 | |||||
| 526 | /// Second string argument for the delayed diagnostic. | ||||
| 527 | std::string DelayedDiagArg2; | ||||
| 528 | |||||
| 529 | /// Third string argument for the delayed diagnostic. | ||||
| 530 | std::string DelayedDiagArg3; | ||||
| 531 | |||||
| 532 | /// Optional flag value. | ||||
| 533 | /// | ||||
| 534 | /// Some flags accept values, for instance: -Wframe-larger-than=<value> and | ||||
| 535 | /// -Rpass=<value>. The content of this string is emitted after the flag name | ||||
| 536 | /// and '='. | ||||
| 537 | std::string FlagValue; | ||||
| 538 | |||||
| 539 | public: | ||||
| 540 | explicit DiagnosticsEngine(IntrusiveRefCntPtr<DiagnosticIDs> Diags, | ||||
| 541 | IntrusiveRefCntPtr<DiagnosticOptions> DiagOpts, | ||||
| 542 | DiagnosticConsumer *client = nullptr, | ||||
| 543 | bool ShouldOwnClient = true); | ||||
| 544 | DiagnosticsEngine(const DiagnosticsEngine &) = delete; | ||||
| 545 | DiagnosticsEngine &operator=(const DiagnosticsEngine &) = delete; | ||||
| 546 | ~DiagnosticsEngine(); | ||||
| 547 | |||||
| 548 | friend void DiagnosticsTestHelper(DiagnosticsEngine &); | ||||
| 549 | LLVM_DUMP_METHOD__attribute__((noinline)) __attribute__((__used__)) void dump() const; | ||||
| 550 | LLVM_DUMP_METHOD__attribute__((noinline)) __attribute__((__used__)) void dump(StringRef DiagName) const; | ||||
| 551 | |||||
| 552 | const IntrusiveRefCntPtr<DiagnosticIDs> &getDiagnosticIDs() const { | ||||
| 553 | return Diags; | ||||
| 554 | } | ||||
| 555 | |||||
| 556 | /// Retrieve the diagnostic options. | ||||
| 557 | DiagnosticOptions &getDiagnosticOptions() const { return *DiagOpts; } | ||||
| 558 | |||||
| 559 | using diag_mapping_range = llvm::iterator_range<DiagState::const_iterator>; | ||||
| 560 | |||||
| 561 | /// Get the current set of diagnostic mappings. | ||||
| 562 | diag_mapping_range getDiagnosticMappings() const { | ||||
| 563 | const DiagState &DS = *GetCurDiagState(); | ||||
| 564 | return diag_mapping_range(DS.begin(), DS.end()); | ||||
| 565 | } | ||||
| 566 | |||||
| 567 | DiagnosticConsumer *getClient() { return Client; } | ||||
| 568 | const DiagnosticConsumer *getClient() const { return Client; } | ||||
| 569 | |||||
| 570 | /// Determine whether this \c DiagnosticsEngine object own its client. | ||||
| 571 | bool ownsClient() const { return Owner != nullptr; } | ||||
| 572 | |||||
| 573 | /// Return the current diagnostic client along with ownership of that | ||||
| 574 | /// client. | ||||
| 575 | std::unique_ptr<DiagnosticConsumer> takeClient() { return std::move(Owner); } | ||||
| 576 | |||||
| 577 | bool hasSourceManager() const { return SourceMgr != nullptr; } | ||||
| 578 | |||||
| 579 | SourceManager &getSourceManager() const { | ||||
| 580 | assert(SourceMgr && "SourceManager not set!")(static_cast <bool> (SourceMgr && "SourceManager not set!" ) ? void (0) : __assert_fail ("SourceMgr && \"SourceManager not set!\"" , "clang/include/clang/Basic/Diagnostic.h", 580, __extension__ __PRETTY_FUNCTION__)); | ||||
| 581 | return *SourceMgr; | ||||
| 582 | } | ||||
| 583 | |||||
| 584 | void setSourceManager(SourceManager *SrcMgr) { | ||||
| 585 | assert(DiagStatesByLoc.empty() &&(static_cast <bool> (DiagStatesByLoc.empty() && "Leftover diag state from a different SourceManager.") ? void (0) : __assert_fail ("DiagStatesByLoc.empty() && \"Leftover diag state from a different SourceManager.\"" , "clang/include/clang/Basic/Diagnostic.h", 586, __extension__ __PRETTY_FUNCTION__)) | ||||
| 586 | "Leftover diag state from a different SourceManager.")(static_cast <bool> (DiagStatesByLoc.empty() && "Leftover diag state from a different SourceManager.") ? void (0) : __assert_fail ("DiagStatesByLoc.empty() && \"Leftover diag state from a different SourceManager.\"" , "clang/include/clang/Basic/Diagnostic.h", 586, __extension__ __PRETTY_FUNCTION__)); | ||||
| 587 | SourceMgr = SrcMgr; | ||||
| 588 | } | ||||
| 589 | |||||
| 590 | //===--------------------------------------------------------------------===// | ||||
| 591 | // DiagnosticsEngine characterization methods, used by a client to customize | ||||
| 592 | // how diagnostics are emitted. | ||||
| 593 | // | ||||
| 594 | |||||
| 595 | /// Copies the current DiagMappings and pushes the new copy | ||||
| 596 | /// onto the top of the stack. | ||||
| 597 | void pushMappings(SourceLocation Loc); | ||||
| 598 | |||||
| 599 | /// Pops the current DiagMappings off the top of the stack, | ||||
| 600 | /// causing the new top of the stack to be the active mappings. | ||||
| 601 | /// | ||||
| 602 | /// \returns \c true if the pop happens, \c false if there is only one | ||||
| 603 | /// DiagMapping on the stack. | ||||
| 604 | bool popMappings(SourceLocation Loc); | ||||
| 605 | |||||
| 606 | /// Set the diagnostic client associated with this diagnostic object. | ||||
| 607 | /// | ||||
| 608 | /// \param ShouldOwnClient true if the diagnostic object should take | ||||
| 609 | /// ownership of \c client. | ||||
| 610 | void setClient(DiagnosticConsumer *client, bool ShouldOwnClient = true); | ||||
| 611 | |||||
| 612 | /// Specify a limit for the number of errors we should | ||||
| 613 | /// emit before giving up. | ||||
| 614 | /// | ||||
| 615 | /// Zero disables the limit. | ||||
| 616 | void setErrorLimit(unsigned Limit) { ErrorLimit = Limit; } | ||||
| 617 | |||||
| 618 | /// Specify the maximum number of template instantiation | ||||
| 619 | /// notes to emit along with a given diagnostic. | ||||
| 620 | void setTemplateBacktraceLimit(unsigned Limit) { | ||||
| 621 | TemplateBacktraceLimit = Limit; | ||||
| 622 | } | ||||
| 623 | |||||
| 624 | /// Retrieve the maximum number of template instantiation | ||||
| 625 | /// notes to emit along with a given diagnostic. | ||||
| 626 | unsigned getTemplateBacktraceLimit() const { | ||||
| 627 | return TemplateBacktraceLimit; | ||||
| 628 | } | ||||
| 629 | |||||
| 630 | /// Specify the maximum number of constexpr evaluation | ||||
| 631 | /// notes to emit along with a given diagnostic. | ||||
| 632 | void setConstexprBacktraceLimit(unsigned Limit) { | ||||
| 633 | ConstexprBacktraceLimit = Limit; | ||||
| 634 | } | ||||
| 635 | |||||
| 636 | /// Retrieve the maximum number of constexpr evaluation | ||||
| 637 | /// notes to emit along with a given diagnostic. | ||||
| 638 | unsigned getConstexprBacktraceLimit() const { | ||||
| 639 | return ConstexprBacktraceLimit; | ||||
| 640 | } | ||||
| 641 | |||||
| 642 | /// When set to true, any unmapped warnings are ignored. | ||||
| 643 | /// | ||||
| 644 | /// If this and WarningsAsErrors are both set, then this one wins. | ||||
| 645 | void setIgnoreAllWarnings(bool Val) { | ||||
| 646 | GetCurDiagState()->IgnoreAllWarnings = Val; | ||||
| 647 | } | ||||
| 648 | bool getIgnoreAllWarnings() const { | ||||
| 649 | return GetCurDiagState()->IgnoreAllWarnings; | ||||
| 650 | } | ||||
| 651 | |||||
| 652 | /// When set to true, any unmapped ignored warnings are no longer | ||||
| 653 | /// ignored. | ||||
| 654 | /// | ||||
| 655 | /// If this and IgnoreAllWarnings are both set, then that one wins. | ||||
| 656 | void setEnableAllWarnings(bool Val) { | ||||
| 657 | GetCurDiagState()->EnableAllWarnings = Val; | ||||
| 658 | } | ||||
| 659 | bool getEnableAllWarnings() const { | ||||
| 660 | return GetCurDiagState()->EnableAllWarnings; | ||||
| 661 | } | ||||
| 662 | |||||
| 663 | /// When set to true, any warnings reported are issued as errors. | ||||
| 664 | void setWarningsAsErrors(bool Val) { | ||||
| 665 | GetCurDiagState()->WarningsAsErrors = Val; | ||||
| 666 | } | ||||
| 667 | bool getWarningsAsErrors() const { | ||||
| 668 | return GetCurDiagState()->WarningsAsErrors; | ||||
| 669 | } | ||||
| 670 | |||||
| 671 | /// When set to true, any error reported is made a fatal error. | ||||
| 672 | void setErrorsAsFatal(bool Val) { GetCurDiagState()->ErrorsAsFatal = Val; } | ||||
| 673 | bool getErrorsAsFatal() const { return GetCurDiagState()->ErrorsAsFatal; } | ||||
| 674 | |||||
| 675 | /// \brief When set to true, any fatal error reported is made an error. | ||||
| 676 | /// | ||||
| 677 | /// This setting takes precedence over the setErrorsAsFatal setting above. | ||||
| 678 | void setFatalsAsError(bool Val) { FatalsAsError = Val; } | ||||
| 679 | bool getFatalsAsError() const { return FatalsAsError; } | ||||
| 680 | |||||
| 681 | /// When set to true mask warnings that come from system headers. | ||||
| 682 | void setSuppressSystemWarnings(bool Val) { | ||||
| 683 | GetCurDiagState()->SuppressSystemWarnings = Val; | ||||
| 684 | } | ||||
| 685 | bool getSuppressSystemWarnings() const { | ||||
| 686 | return GetCurDiagState()->SuppressSystemWarnings; | ||||
| 687 | } | ||||
| 688 | |||||
| 689 | /// Suppress all diagnostics, to silence the front end when we | ||||
| 690 | /// know that we don't want any more diagnostics to be passed along to the | ||||
| 691 | /// client | ||||
| 692 | void setSuppressAllDiagnostics(bool Val) { SuppressAllDiagnostics = Val; } | ||||
| 693 | bool getSuppressAllDiagnostics() const { return SuppressAllDiagnostics; } | ||||
| 694 | |||||
| 695 | /// Set type eliding, to skip outputting same types occurring in | ||||
| 696 | /// template types. | ||||
| 697 | void setElideType(bool Val) { ElideType = Val; } | ||||
| 698 | bool getElideType() { return ElideType; } | ||||
| 699 | |||||
| 700 | /// Set tree printing, to outputting the template difference in a | ||||
| 701 | /// tree format. | ||||
| 702 | void setPrintTemplateTree(bool Val) { PrintTemplateTree = Val; } | ||||
| 703 | bool getPrintTemplateTree() { return PrintTemplateTree; } | ||||
| 704 | |||||
| 705 | /// Set color printing, so the type diffing will inject color markers | ||||
| 706 | /// into the output. | ||||
| 707 | void setShowColors(bool Val) { ShowColors = Val; } | ||||
| 708 | bool getShowColors() { return ShowColors; } | ||||
| 709 | |||||
| 710 | /// Specify which overload candidates to show when overload resolution | ||||
| 711 | /// fails. | ||||
| 712 | /// | ||||
| 713 | /// By default, we show all candidates. | ||||
| 714 | void setShowOverloads(OverloadsShown Val) { | ||||
| 715 | ShowOverloads = Val; | ||||
| 716 | } | ||||
| 717 | OverloadsShown getShowOverloads() const { return ShowOverloads; } | ||||
| 718 | |||||
| 719 | /// When a call or operator fails, print out up to this many candidate | ||||
| 720 | /// overloads as suggestions. | ||||
| 721 | /// | ||||
| 722 | /// With Ovl_Best, we set a high limit for the first nontrivial overload set | ||||
| 723 | /// we print, and a lower limit for later sets. This way the user has a | ||||
| 724 | /// chance of diagnosing at least one callsite in their program without | ||||
| 725 | /// having to recompile with -fshow-overloads=all. | ||||
| 726 | unsigned getNumOverloadCandidatesToShow() const { | ||||
| 727 | switch (getShowOverloads()) { | ||||
| 728 | case Ovl_All: | ||||
| 729 | // INT_MAX rather than UINT_MAX so that we don't have to think about the | ||||
| 730 | // effect of implicit conversions on this value. In practice we'll never | ||||
| 731 | // hit 2^31 candidates anyway. | ||||
| 732 | return std::numeric_limits<int>::max(); | ||||
| 733 | case Ovl_Best: | ||||
| 734 | return NumOverloadsToShow; | ||||
| 735 | } | ||||
| 736 | llvm_unreachable("invalid OverloadsShown kind")::llvm::llvm_unreachable_internal("invalid OverloadsShown kind" , "clang/include/clang/Basic/Diagnostic.h", 736); | ||||
| 737 | } | ||||
| 738 | |||||
| 739 | /// Call this after showing N overload candidates. This influences the value | ||||
| 740 | /// returned by later calls to getNumOverloadCandidatesToShow(). | ||||
| 741 | void overloadCandidatesShown(unsigned N) { | ||||
| 742 | // Current heuristic: Start out with a large value for NumOverloadsToShow, | ||||
| 743 | // and then once we print one nontrivially-large overload set, decrease it | ||||
| 744 | // for future calls. | ||||
| 745 | if (N > 4) { | ||||
| 746 | NumOverloadsToShow = 4; | ||||
| 747 | } | ||||
| 748 | } | ||||
| 749 | |||||
| 750 | /// Pretend that the last diagnostic issued was ignored, so any | ||||
| 751 | /// subsequent notes will be suppressed, or restore a prior ignoring | ||||
| 752 | /// state after ignoring some diagnostics and their notes, possibly in | ||||
| 753 | /// the middle of another diagnostic. | ||||
| 754 | /// | ||||
| 755 | /// This can be used by clients who suppress diagnostics themselves. | ||||
| 756 | void setLastDiagnosticIgnored(bool Ignored) { | ||||
| 757 | if (LastDiagLevel == DiagnosticIDs::Fatal) | ||||
| 758 | FatalErrorOccurred = true; | ||||
| 759 | LastDiagLevel = Ignored ? DiagnosticIDs::Ignored : DiagnosticIDs::Warning; | ||||
| 760 | } | ||||
| 761 | |||||
| 762 | /// Determine whether the previous diagnostic was ignored. This can | ||||
| 763 | /// be used by clients that want to determine whether notes attached to a | ||||
| 764 | /// diagnostic will be suppressed. | ||||
| 765 | bool isLastDiagnosticIgnored() const { | ||||
| 766 | return LastDiagLevel == DiagnosticIDs::Ignored; | ||||
| 767 | } | ||||
| 768 | |||||
| 769 | /// Controls whether otherwise-unmapped extension diagnostics are | ||||
| 770 | /// mapped onto ignore/warning/error. | ||||
| 771 | /// | ||||
| 772 | /// This corresponds to the GCC -pedantic and -pedantic-errors option. | ||||
| 773 | void setExtensionHandlingBehavior(diag::Severity H) { | ||||
| 774 | GetCurDiagState()->ExtBehavior = H; | ||||
| 775 | } | ||||
| 776 | diag::Severity getExtensionHandlingBehavior() const { | ||||
| 777 | return GetCurDiagState()->ExtBehavior; | ||||
| 778 | } | ||||
| 779 | |||||
| 780 | /// Counter bumped when an __extension__ block is/ encountered. | ||||
| 781 | /// | ||||
| 782 | /// When non-zero, all extension diagnostics are entirely silenced, no | ||||
| 783 | /// matter how they are mapped. | ||||
| 784 | void IncrementAllExtensionsSilenced() { ++AllExtensionsSilenced; } | ||||
| 785 | void DecrementAllExtensionsSilenced() { --AllExtensionsSilenced; } | ||||
| 786 | bool hasAllExtensionsSilenced() { return AllExtensionsSilenced != 0; } | ||||
| 787 | |||||
| 788 | /// This allows the client to specify that certain warnings are | ||||
| 789 | /// ignored. | ||||
| 790 | /// | ||||
| 791 | /// Notes can never be mapped, errors can only be mapped to fatal, and | ||||
| 792 | /// WARNINGs and EXTENSIONs can be mapped arbitrarily. | ||||
| 793 | /// | ||||
| 794 | /// \param Loc The source location that this change of diagnostic state should | ||||
| 795 | /// take affect. It can be null if we are setting the latest state. | ||||
| 796 | void setSeverity(diag::kind Diag, diag::Severity Map, SourceLocation Loc); | ||||
| 797 | |||||
| 798 | /// Change an entire diagnostic group (e.g. "unknown-pragmas") to | ||||
| 799 | /// have the specified mapping. | ||||
| 800 | /// | ||||
| 801 | /// \returns true (and ignores the request) if "Group" was unknown, false | ||||
| 802 | /// otherwise. | ||||
| 803 | /// | ||||
| 804 | /// \param Flavor The flavor of group to affect. -Rfoo does not affect the | ||||
| 805 | /// state of the -Wfoo group and vice versa. | ||||
| 806 | /// | ||||
| 807 | /// \param Loc The source location that this change of diagnostic state should | ||||
| 808 | /// take affect. It can be null if we are setting the state from command-line. | ||||
| 809 | bool setSeverityForGroup(diag::Flavor Flavor, StringRef Group, | ||||
| 810 | diag::Severity Map, | ||||
| 811 | SourceLocation Loc = SourceLocation()); | ||||
| 812 | bool setSeverityForGroup(diag::Flavor Flavor, diag::Group Group, | ||||
| 813 | diag::Severity Map, | ||||
| 814 | SourceLocation Loc = SourceLocation()); | ||||
| 815 | |||||
| 816 | /// Set the warning-as-error flag for the given diagnostic group. | ||||
| 817 | /// | ||||
| 818 | /// This function always only operates on the current diagnostic state. | ||||
| 819 | /// | ||||
| 820 | /// \returns True if the given group is unknown, false otherwise. | ||||
| 821 | bool setDiagnosticGroupWarningAsError(StringRef Group, bool Enabled); | ||||
| 822 | |||||
| 823 | /// Set the error-as-fatal flag for the given diagnostic group. | ||||
| 824 | /// | ||||
| 825 | /// This function always only operates on the current diagnostic state. | ||||
| 826 | /// | ||||
| 827 | /// \returns True if the given group is unknown, false otherwise. | ||||
| 828 | bool setDiagnosticGroupErrorAsFatal(StringRef Group, bool Enabled); | ||||
| 829 | |||||
| 830 | /// Add the specified mapping to all diagnostics of the specified | ||||
| 831 | /// flavor. | ||||
| 832 | /// | ||||
| 833 | /// Mainly to be used by -Wno-everything to disable all warnings but allow | ||||
| 834 | /// subsequent -W options to enable specific warnings. | ||||
| 835 | void setSeverityForAll(diag::Flavor Flavor, diag::Severity Map, | ||||
| 836 | SourceLocation Loc = SourceLocation()); | ||||
| 837 | |||||
| 838 | bool hasErrorOccurred() const { return ErrorOccurred; } | ||||
| 839 | |||||
| 840 | /// Errors that actually prevent compilation, not those that are | ||||
| 841 | /// upgraded from a warning by -Werror. | ||||
| 842 | bool hasUncompilableErrorOccurred() const { | ||||
| 843 | return UncompilableErrorOccurred; | ||||
| 844 | } | ||||
| 845 | bool hasFatalErrorOccurred() const { return FatalErrorOccurred; } | ||||
| 846 | |||||
| 847 | /// Determine whether any kind of unrecoverable error has occurred. | ||||
| 848 | bool hasUnrecoverableErrorOccurred() const { | ||||
| 849 | return FatalErrorOccurred || UnrecoverableErrorOccurred; | ||||
| 850 | } | ||||
| 851 | |||||
| 852 | unsigned getNumErrors() const { return NumErrors; } | ||||
| 853 | unsigned getNumWarnings() const { return NumWarnings; } | ||||
| 854 | |||||
| 855 | void setNumWarnings(unsigned NumWarnings) { | ||||
| 856 | this->NumWarnings = NumWarnings; | ||||
| 857 | } | ||||
| 858 | |||||
| 859 | /// Return an ID for a diagnostic with the specified format string and | ||||
| 860 | /// level. | ||||
| 861 | /// | ||||
| 862 | /// If this is the first request for this diagnostic, it is registered and | ||||
| 863 | /// created, otherwise the existing ID is returned. | ||||
| 864 | /// | ||||
| 865 | /// \param FormatString A fixed diagnostic format string that will be hashed | ||||
| 866 | /// and mapped to a unique DiagID. | ||||
| 867 | template <unsigned N> | ||||
| 868 | unsigned getCustomDiagID(Level L, const char (&FormatString)[N]) { | ||||
| 869 | return Diags->getCustomDiagID((DiagnosticIDs::Level)L, | ||||
| 870 | StringRef(FormatString, N - 1)); | ||||
| 871 | } | ||||
| 872 | |||||
| 873 | /// Converts a diagnostic argument (as an intptr_t) into the string | ||||
| 874 | /// that represents it. | ||||
| 875 | void ConvertArgToString(ArgumentKind Kind, intptr_t Val, | ||||
| 876 | StringRef Modifier, StringRef Argument, | ||||
| 877 | ArrayRef<ArgumentValue> PrevArgs, | ||||
| 878 | SmallVectorImpl<char> &Output, | ||||
| 879 | ArrayRef<intptr_t> QualTypeVals) const { | ||||
| 880 | ArgToStringFn(Kind, Val, Modifier, Argument, PrevArgs, Output, | ||||
| 881 | ArgToStringCookie, QualTypeVals); | ||||
| 882 | } | ||||
| 883 | |||||
| 884 | void SetArgToStringFn(ArgToStringFnTy Fn, void *Cookie) { | ||||
| 885 | ArgToStringFn = Fn; | ||||
| 886 | ArgToStringCookie = Cookie; | ||||
| 887 | } | ||||
| 888 | |||||
| 889 | /// Note that the prior diagnostic was emitted by some other | ||||
| 890 | /// \c DiagnosticsEngine, and we may be attaching a note to that diagnostic. | ||||
| 891 | void notePriorDiagnosticFrom(const DiagnosticsEngine &Other) { | ||||
| 892 | LastDiagLevel = Other.LastDiagLevel; | ||||
| 893 | } | ||||
| 894 | |||||
| 895 | /// Reset the state of the diagnostic object to its initial configuration. | ||||
| 896 | /// \param[in] soft - if true, doesn't reset the diagnostic mappings and state | ||||
| 897 | void Reset(bool soft = false); | ||||
| 898 | |||||
| 899 | //===--------------------------------------------------------------------===// | ||||
| 900 | // DiagnosticsEngine classification and reporting interfaces. | ||||
| 901 | // | ||||
| 902 | |||||
| 903 | /// Determine whether the diagnostic is known to be ignored. | ||||
| 904 | /// | ||||
| 905 | /// This can be used to opportunistically avoid expensive checks when it's | ||||
| 906 | /// known for certain that the diagnostic has been suppressed at the | ||||
| 907 | /// specified location \p Loc. | ||||
| 908 | /// | ||||
| 909 | /// \param Loc The source location we are interested in finding out the | ||||
| 910 | /// diagnostic state. Can be null in order to query the latest state. | ||||
| 911 | bool isIgnored(unsigned DiagID, SourceLocation Loc) const { | ||||
| 912 | return Diags->getDiagnosticSeverity(DiagID, Loc, *this) == | ||||
| 913 | diag::Severity::Ignored; | ||||
| 914 | } | ||||
| 915 | |||||
| 916 | /// Based on the way the client configured the DiagnosticsEngine | ||||
| 917 | /// object, classify the specified diagnostic ID into a Level, consumable by | ||||
| 918 | /// the DiagnosticConsumer. | ||||
| 919 | /// | ||||
| 920 | /// To preserve invariant assumptions, this function should not be used to | ||||
| 921 | /// influence parse or semantic analysis actions. Instead consider using | ||||
| 922 | /// \c isIgnored(). | ||||
| 923 | /// | ||||
| 924 | /// \param Loc The source location we are interested in finding out the | ||||
| 925 | /// diagnostic state. Can be null in order to query the latest state. | ||||
| 926 | Level getDiagnosticLevel(unsigned DiagID, SourceLocation Loc) const { | ||||
| 927 | return (Level)Diags->getDiagnosticLevel(DiagID, Loc, *this); | ||||
| 928 | } | ||||
| 929 | |||||
| 930 | /// Issue the message to the client. | ||||
| 931 | /// | ||||
| 932 | /// This actually returns an instance of DiagnosticBuilder which emits the | ||||
| 933 | /// diagnostics (through @c ProcessDiag) when it is destroyed. | ||||
| 934 | /// | ||||
| 935 | /// \param DiagID A member of the @c diag::kind enum. | ||||
| 936 | /// \param Loc Represents the source location associated with the diagnostic, | ||||
| 937 | /// which can be an invalid location if no position information is available. | ||||
| 938 | inline DiagnosticBuilder Report(SourceLocation Loc, unsigned DiagID); | ||||
| 939 | inline DiagnosticBuilder Report(unsigned DiagID); | ||||
| 940 | |||||
| 941 | void Report(const StoredDiagnostic &storedDiag); | ||||
| 942 | |||||
| 943 | /// Determine whethere there is already a diagnostic in flight. | ||||
| 944 | bool isDiagnosticInFlight() const { | ||||
| 945 | return CurDiagID != std::numeric_limits<unsigned>::max(); | ||||
| 946 | } | ||||
| 947 | |||||
| 948 | /// Set the "delayed" diagnostic that will be emitted once | ||||
| 949 | /// the current diagnostic completes. | ||||
| 950 | /// | ||||
| 951 | /// If a diagnostic is already in-flight but the front end must | ||||
| 952 | /// report a problem (e.g., with an inconsistent file system | ||||
| 953 | /// state), this routine sets a "delayed" diagnostic that will be | ||||
| 954 | /// emitted after the current diagnostic completes. This should | ||||
| 955 | /// only be used for fatal errors detected at inconvenient | ||||
| 956 | /// times. If emitting a delayed diagnostic causes a second delayed | ||||
| 957 | /// diagnostic to be introduced, that second delayed diagnostic | ||||
| 958 | /// will be ignored. | ||||
| 959 | /// | ||||
| 960 | /// \param DiagID The ID of the diagnostic being delayed. | ||||
| 961 | /// | ||||
| 962 | /// \param Arg1 A string argument that will be provided to the | ||||
| 963 | /// diagnostic. A copy of this string will be stored in the | ||||
| 964 | /// DiagnosticsEngine object itself. | ||||
| 965 | /// | ||||
| 966 | /// \param Arg2 A string argument that will be provided to the | ||||
| 967 | /// diagnostic. A copy of this string will be stored in the | ||||
| 968 | /// DiagnosticsEngine object itself. | ||||
| 969 | /// | ||||
| 970 | /// \param Arg3 A string argument that will be provided to the | ||||
| 971 | /// diagnostic. A copy of this string will be stored in the | ||||
| 972 | /// DiagnosticsEngine object itself. | ||||
| 973 | void SetDelayedDiagnostic(unsigned DiagID, StringRef Arg1 = "", | ||||
| 974 | StringRef Arg2 = "", StringRef Arg3 = ""); | ||||
| 975 | |||||
| 976 | /// Clear out the current diagnostic. | ||||
| 977 | void Clear() { CurDiagID = std::numeric_limits<unsigned>::max(); } | ||||
| 978 | |||||
| 979 | /// Return the value associated with this diagnostic flag. | ||||
| 980 | StringRef getFlagValue() const { return FlagValue; } | ||||
| 981 | |||||
| 982 | private: | ||||
| 983 | // This is private state used by DiagnosticBuilder. We put it here instead of | ||||
| 984 | // in DiagnosticBuilder in order to keep DiagnosticBuilder a small lightweight | ||||
| 985 | // object. This implementation choice means that we can only have one | ||||
| 986 | // diagnostic "in flight" at a time, but this seems to be a reasonable | ||||
| 987 | // tradeoff to keep these objects small. Assertions verify that only one | ||||
| 988 | // diagnostic is in flight at a time. | ||||
| 989 | friend class Diagnostic; | ||||
| 990 | friend class DiagnosticBuilder; | ||||
| 991 | friend class DiagnosticErrorTrap; | ||||
| 992 | friend class DiagnosticIDs; | ||||
| 993 | friend class PartialDiagnostic; | ||||
| 994 | |||||
| 995 | /// Report the delayed diagnostic. | ||||
| 996 | void ReportDelayed(); | ||||
| 997 | |||||
| 998 | /// The location of the current diagnostic that is in flight. | ||||
| 999 | SourceLocation CurDiagLoc; | ||||
| 1000 | |||||
| 1001 | /// The ID of the current diagnostic that is in flight. | ||||
| 1002 | /// | ||||
| 1003 | /// This is set to std::numeric_limits<unsigned>::max() when there is no | ||||
| 1004 | /// diagnostic in flight. | ||||
| 1005 | unsigned CurDiagID; | ||||
| 1006 | |||||
| 1007 | enum { | ||||
| 1008 | /// The maximum number of arguments we can hold. | ||||
| 1009 | /// | ||||
| 1010 | /// We currently only support up to 10 arguments (%0-%9). A single | ||||
| 1011 | /// diagnostic with more than that almost certainly has to be simplified | ||||
| 1012 | /// anyway. | ||||
| 1013 | MaxArguments = DiagnosticStorage::MaxArguments, | ||||
| 1014 | }; | ||||
| 1015 | |||||
| 1016 | DiagnosticStorage DiagStorage; | ||||
| 1017 | |||||
| 1018 | DiagnosticMapping makeUserMapping(diag::Severity Map, SourceLocation L) { | ||||
| 1019 | bool isPragma = L.isValid(); | ||||
| 1020 | DiagnosticMapping Mapping = | ||||
| 1021 | DiagnosticMapping::Make(Map, /*IsUser=*/true, isPragma); | ||||
| 1022 | |||||
| 1023 | // If this is a pragma mapping, then set the diagnostic mapping flags so | ||||
| 1024 | // that we override command line options. | ||||
| 1025 | if (isPragma) { | ||||
| 1026 | Mapping.setNoWarningAsError(true); | ||||
| 1027 | Mapping.setNoErrorAsFatal(true); | ||||
| 1028 | } | ||||
| 1029 | |||||
| 1030 | return Mapping; | ||||
| 1031 | } | ||||
| 1032 | |||||
| 1033 | /// Used to report a diagnostic that is finally fully formed. | ||||
| 1034 | /// | ||||
| 1035 | /// \returns true if the diagnostic was emitted, false if it was suppressed. | ||||
| 1036 | bool ProcessDiag() { | ||||
| 1037 | return Diags->ProcessDiag(*this); | ||||
| 1038 | } | ||||
| 1039 | |||||
| 1040 | /// @name Diagnostic Emission | ||||
| 1041 | /// @{ | ||||
| 1042 | protected: | ||||
| 1043 | friend class ASTReader; | ||||
| 1044 | friend class ASTWriter; | ||||
| 1045 | |||||
| 1046 | // Sema requires access to the following functions because the current design | ||||
| 1047 | // of SFINAE requires it to use its own SemaDiagnosticBuilder, which needs to | ||||
| 1048 | // access us directly to ensure we minimize the emitted code for the common | ||||
| 1049 | // Sema::Diag() patterns. | ||||
| 1050 | friend class Sema; | ||||
| 1051 | |||||
| 1052 | /// Emit the current diagnostic and clear the diagnostic state. | ||||
| 1053 | /// | ||||
| 1054 | /// \param Force Emit the diagnostic regardless of suppression settings. | ||||
| 1055 | bool EmitCurrentDiagnostic(bool Force = false); | ||||
| 1056 | |||||
| 1057 | unsigned getCurrentDiagID() const { return CurDiagID; } | ||||
| 1058 | |||||
| 1059 | SourceLocation getCurrentDiagLoc() const { return CurDiagLoc; } | ||||
| 1060 | |||||
| 1061 | /// @} | ||||
| 1062 | }; | ||||
| 1063 | |||||
| 1064 | /// RAII class that determines when any errors have occurred | ||||
| 1065 | /// between the time the instance was created and the time it was | ||||
| 1066 | /// queried. | ||||
| 1067 | /// | ||||
| 1068 | /// Note that you almost certainly do not want to use this. It's usually | ||||
| 1069 | /// meaningless to ask whether a particular scope triggered an error message, | ||||
| 1070 | /// because error messages outside that scope can mark things invalid (or cause | ||||
| 1071 | /// us to reach an error limit), which can suppress errors within that scope. | ||||
| 1072 | class DiagnosticErrorTrap { | ||||
| 1073 | DiagnosticsEngine &Diag; | ||||
| 1074 | unsigned NumErrors; | ||||
| 1075 | unsigned NumUnrecoverableErrors; | ||||
| 1076 | |||||
| 1077 | public: | ||||
| 1078 | explicit DiagnosticErrorTrap(DiagnosticsEngine &Diag) | ||||
| 1079 | : Diag(Diag) { reset(); } | ||||
| 1080 | |||||
| 1081 | /// Determine whether any errors have occurred since this | ||||
| 1082 | /// object instance was created. | ||||
| 1083 | bool hasErrorOccurred() const { | ||||
| 1084 | return Diag.TrapNumErrorsOccurred > NumErrors; | ||||
| 1085 | } | ||||
| 1086 | |||||
| 1087 | /// Determine whether any unrecoverable errors have occurred since this | ||||
| 1088 | /// object instance was created. | ||||
| 1089 | bool hasUnrecoverableErrorOccurred() const { | ||||
| 1090 | return Diag.TrapNumUnrecoverableErrorsOccurred > NumUnrecoverableErrors; | ||||
| 1091 | } | ||||
| 1092 | |||||
| 1093 | /// Set to initial state of "no errors occurred". | ||||
| 1094 | void reset() { | ||||
| 1095 | NumErrors = Diag.TrapNumErrorsOccurred; | ||||
| 1096 | NumUnrecoverableErrors = Diag.TrapNumUnrecoverableErrorsOccurred; | ||||
| 1097 | } | ||||
| 1098 | }; | ||||
| 1099 | |||||
| 1100 | /// The streaming interface shared between DiagnosticBuilder and | ||||
| 1101 | /// PartialDiagnostic. This class is not intended to be constructed directly | ||||
| 1102 | /// but only as base class of DiagnosticBuilder and PartialDiagnostic builder. | ||||
| 1103 | /// | ||||
| 1104 | /// Any new type of argument accepted by DiagnosticBuilder and PartialDiagnostic | ||||
| 1105 | /// should be implemented as a '<<' operator of StreamingDiagnostic, e.g. | ||||
| 1106 | /// | ||||
| 1107 | /// const StreamingDiagnostic& | ||||
| 1108 | /// operator<<(const StreamingDiagnostic&, NewArgType); | ||||
| 1109 | /// | ||||
| 1110 | class StreamingDiagnostic { | ||||
| 1111 | public: | ||||
| 1112 | /// An allocator for DiagnosticStorage objects, which uses a small cache to | ||||
| 1113 | /// objects, used to reduce malloc()/free() traffic for partial diagnostics. | ||||
| 1114 | class DiagStorageAllocator { | ||||
| 1115 | static const unsigned NumCached = 16; | ||||
| 1116 | DiagnosticStorage Cached[NumCached]; | ||||
| 1117 | DiagnosticStorage *FreeList[NumCached]; | ||||
| 1118 | unsigned NumFreeListEntries; | ||||
| 1119 | |||||
| 1120 | public: | ||||
| 1121 | DiagStorageAllocator(); | ||||
| 1122 | ~DiagStorageAllocator(); | ||||
| 1123 | |||||
| 1124 | /// Allocate new storage. | ||||
| 1125 | DiagnosticStorage *Allocate() { | ||||
| 1126 | if (NumFreeListEntries == 0) | ||||
| 1127 | return new DiagnosticStorage; | ||||
| 1128 | |||||
| 1129 | DiagnosticStorage *Result = FreeList[--NumFreeListEntries]; | ||||
| 1130 | Result->NumDiagArgs = 0; | ||||
| 1131 | Result->DiagRanges.clear(); | ||||
| 1132 | Result->FixItHints.clear(); | ||||
| 1133 | return Result; | ||||
| 1134 | } | ||||
| 1135 | |||||
| 1136 | /// Free the given storage object. | ||||
| 1137 | void Deallocate(DiagnosticStorage *S) { | ||||
| 1138 | if (S >= Cached && S <= Cached + NumCached) { | ||||
| 1139 | FreeList[NumFreeListEntries++] = S; | ||||
| 1140 | return; | ||||
| 1141 | } | ||||
| 1142 | |||||
| 1143 | delete S; | ||||
| 1144 | } | ||||
| 1145 | }; | ||||
| 1146 | |||||
| 1147 | protected: | ||||
| 1148 | mutable DiagnosticStorage *DiagStorage = nullptr; | ||||
| 1149 | |||||
| 1150 | /// Allocator used to allocate storage for this diagnostic. | ||||
| 1151 | DiagStorageAllocator *Allocator = nullptr; | ||||
| 1152 | |||||
| 1153 | public: | ||||
| 1154 | /// Retrieve storage for this particular diagnostic. | ||||
| 1155 | DiagnosticStorage *getStorage() const { | ||||
| 1156 | if (DiagStorage) | ||||
| 1157 | return DiagStorage; | ||||
| 1158 | |||||
| 1159 | assert(Allocator)(static_cast <bool> (Allocator) ? void (0) : __assert_fail ("Allocator", "clang/include/clang/Basic/Diagnostic.h", 1159 , __extension__ __PRETTY_FUNCTION__)); | ||||
| 1160 | DiagStorage = Allocator->Allocate(); | ||||
| 1161 | return DiagStorage; | ||||
| 1162 | } | ||||
| 1163 | |||||
| 1164 | void freeStorage() { | ||||
| 1165 | if (!DiagStorage
| ||||
| 1166 | return; | ||||
| 1167 | |||||
| 1168 | // The hot path for PartialDiagnostic is when we just used it to wrap an ID | ||||
| 1169 | // (typically so we have the flexibility of passing a more complex | ||||
| 1170 | // diagnostic into the callee, but that does not commonly occur). | ||||
| 1171 | // | ||||
| 1172 | // Split this out into a slow function for silly compilers (*cough*) which | ||||
| 1173 | // can't do decent partial inlining. | ||||
| 1174 | freeStorageSlow(); | ||||
| 1175 | } | ||||
| 1176 | |||||
| 1177 | void freeStorageSlow() { | ||||
| 1178 | if (!Allocator) | ||||
| 1179 | return; | ||||
| 1180 | Allocator->Deallocate(DiagStorage); | ||||
| 1181 | DiagStorage = nullptr; | ||||
| 1182 | } | ||||
| 1183 | |||||
| 1184 | void AddTaggedVal(uint64_t V, DiagnosticsEngine::ArgumentKind Kind) const { | ||||
| 1185 | if (!DiagStorage) | ||||
| 1186 | DiagStorage = getStorage(); | ||||
| 1187 | |||||
| 1188 | assert(DiagStorage->NumDiagArgs < DiagnosticStorage::MaxArguments &&(static_cast <bool> (DiagStorage->NumDiagArgs < DiagnosticStorage ::MaxArguments && "Too many arguments to diagnostic!" ) ? void (0) : __assert_fail ("DiagStorage->NumDiagArgs < DiagnosticStorage::MaxArguments && \"Too many arguments to diagnostic!\"" , "clang/include/clang/Basic/Diagnostic.h", 1189, __extension__ __PRETTY_FUNCTION__)) | ||||
| 1189 | "Too many arguments to diagnostic!")(static_cast <bool> (DiagStorage->NumDiagArgs < DiagnosticStorage ::MaxArguments && "Too many arguments to diagnostic!" ) ? void (0) : __assert_fail ("DiagStorage->NumDiagArgs < DiagnosticStorage::MaxArguments && \"Too many arguments to diagnostic!\"" , "clang/include/clang/Basic/Diagnostic.h", 1189, __extension__ __PRETTY_FUNCTION__)); | ||||
| 1190 | DiagStorage->DiagArgumentsKind[DiagStorage->NumDiagArgs] = Kind; | ||||
| 1191 | DiagStorage->DiagArgumentsVal[DiagStorage->NumDiagArgs++] = V; | ||||
| 1192 | } | ||||
| 1193 | |||||
| 1194 | void AddString(StringRef V) const { | ||||
| 1195 | if (!DiagStorage
| ||||
| 1196 | DiagStorage = getStorage(); | ||||
| 1197 | |||||
| 1198 | assert(DiagStorage->NumDiagArgs < DiagnosticStorage::MaxArguments &&(static_cast <bool> (DiagStorage->NumDiagArgs < DiagnosticStorage ::MaxArguments && "Too many arguments to diagnostic!" ) ? void (0) : __assert_fail ("DiagStorage->NumDiagArgs < DiagnosticStorage::MaxArguments && \"Too many arguments to diagnostic!\"" , "clang/include/clang/Basic/Diagnostic.h", 1199, __extension__ __PRETTY_FUNCTION__)) | ||||
| |||||
| 1199 | "Too many arguments to diagnostic!")(static_cast <bool> (DiagStorage->NumDiagArgs < DiagnosticStorage ::MaxArguments && "Too many arguments to diagnostic!" ) ? void (0) : __assert_fail ("DiagStorage->NumDiagArgs < DiagnosticStorage::MaxArguments && \"Too many arguments to diagnostic!\"" , "clang/include/clang/Basic/Diagnostic.h", 1199, __extension__ __PRETTY_FUNCTION__)); | ||||
| 1200 | DiagStorage->DiagArgumentsKind[DiagStorage->NumDiagArgs] = | ||||
| 1201 | DiagnosticsEngine::ak_std_string; | ||||
| 1202 | DiagStorage->DiagArgumentsStr[DiagStorage->NumDiagArgs++] = std::string(V); | ||||
| 1203 | } | ||||
| 1204 | |||||
| 1205 | void AddSourceRange(const CharSourceRange &R) const { | ||||
| 1206 | if (!DiagStorage) | ||||
| 1207 | DiagStorage = getStorage(); | ||||
| 1208 | |||||
| 1209 | DiagStorage->DiagRanges.push_back(R); | ||||
| 1210 | } | ||||
| 1211 | |||||
| 1212 | void AddFixItHint(const FixItHint &Hint) const { | ||||
| 1213 | if (Hint.isNull()) | ||||
| 1214 | return; | ||||
| 1215 | |||||
| 1216 | if (!DiagStorage) | ||||
| 1217 | DiagStorage = getStorage(); | ||||
| 1218 | |||||
| 1219 | DiagStorage->FixItHints.push_back(Hint); | ||||
| 1220 | } | ||||
| 1221 | |||||
| 1222 | /// Conversion of StreamingDiagnostic to bool always returns \c true. | ||||
| 1223 | /// | ||||
| 1224 | /// This allows is to be used in boolean error contexts (where \c true is | ||||
| 1225 | /// used to indicate that an error has occurred), like: | ||||
| 1226 | /// \code | ||||
| 1227 | /// return Diag(...); | ||||
| 1228 | /// \endcode | ||||
| 1229 | operator bool() const { return true; } | ||||
| 1230 | |||||
| 1231 | protected: | ||||
| 1232 | StreamingDiagnostic() = default; | ||||
| 1233 | |||||
| 1234 | /// Construct with an external storage not owned by itself. The allocator | ||||
| 1235 | /// is a null pointer in this case. | ||||
| 1236 | explicit StreamingDiagnostic(DiagnosticStorage *Storage) | ||||
| 1237 | : DiagStorage(Storage) {} | ||||
| 1238 | |||||
| 1239 | /// Construct with a storage allocator which will manage the storage. The | ||||
| 1240 | /// allocator is not a null pointer in this case. | ||||
| 1241 | explicit StreamingDiagnostic(DiagStorageAllocator &Alloc) | ||||
| 1242 | : Allocator(&Alloc) {} | ||||
| 1243 | |||||
| 1244 | StreamingDiagnostic(const StreamingDiagnostic &Diag) = default; | ||||
| 1245 | StreamingDiagnostic(StreamingDiagnostic &&Diag) = default; | ||||
| 1246 | |||||
| 1247 | ~StreamingDiagnostic() { freeStorage(); } | ||||
| 1248 | }; | ||||
| 1249 | |||||
| 1250 | //===----------------------------------------------------------------------===// | ||||
| 1251 | // DiagnosticBuilder | ||||
| 1252 | //===----------------------------------------------------------------------===// | ||||
| 1253 | |||||
| 1254 | /// A little helper class used to produce diagnostics. | ||||
| 1255 | /// | ||||
| 1256 | /// This is constructed by the DiagnosticsEngine::Report method, and | ||||
| 1257 | /// allows insertion of extra information (arguments and source ranges) into | ||||
| 1258 | /// the currently "in flight" diagnostic. When the temporary for the builder | ||||
| 1259 | /// is destroyed, the diagnostic is issued. | ||||
| 1260 | /// | ||||
| 1261 | /// Note that many of these will be created as temporary objects (many call | ||||
| 1262 | /// sites), so we want them to be small and we never want their address taken. | ||||
| 1263 | /// This ensures that compilers with somewhat reasonable optimizers will promote | ||||
| 1264 | /// the common fields to registers, eliminating increments of the NumArgs field, | ||||
| 1265 | /// for example. | ||||
| 1266 | class DiagnosticBuilder : public StreamingDiagnostic { | ||||
| 1267 | friend class DiagnosticsEngine; | ||||
| 1268 | friend class PartialDiagnostic; | ||||
| 1269 | |||||
| 1270 | mutable DiagnosticsEngine *DiagObj = nullptr; | ||||
| 1271 | |||||
| 1272 | /// Status variable indicating if this diagnostic is still active. | ||||
| 1273 | /// | ||||
| 1274 | // NOTE: This field is redundant with DiagObj (IsActive iff (DiagObj == 0)), | ||||
| 1275 | // but LLVM is not currently smart enough to eliminate the null check that | ||||
| 1276 | // Emit() would end up with if we used that as our status variable. | ||||
| 1277 | mutable bool IsActive = false; | ||||
| 1278 | |||||
| 1279 | /// Flag indicating that this diagnostic is being emitted via a | ||||
| 1280 | /// call to ForceEmit. | ||||
| 1281 | mutable bool IsForceEmit = false; | ||||
| 1282 | |||||
| 1283 | DiagnosticBuilder() = default; | ||||
| 1284 | |||||
| 1285 | explicit DiagnosticBuilder(DiagnosticsEngine *diagObj) | ||||
| 1286 | : StreamingDiagnostic(&diagObj->DiagStorage), DiagObj(diagObj), | ||||
| 1287 | IsActive(true) { | ||||
| 1288 | assert(diagObj && "DiagnosticBuilder requires a valid DiagnosticsEngine!")(static_cast <bool> (diagObj && "DiagnosticBuilder requires a valid DiagnosticsEngine!" ) ? void (0) : __assert_fail ("diagObj && \"DiagnosticBuilder requires a valid DiagnosticsEngine!\"" , "clang/include/clang/Basic/Diagnostic.h", 1288, __extension__ __PRETTY_FUNCTION__)); | ||||
| 1289 | assert(DiagStorage &&(static_cast <bool> (DiagStorage && "DiagnosticBuilder requires a valid DiagnosticStorage!" ) ? void (0) : __assert_fail ("DiagStorage && \"DiagnosticBuilder requires a valid DiagnosticStorage!\"" , "clang/include/clang/Basic/Diagnostic.h", 1290, __extension__ __PRETTY_FUNCTION__)) | ||||
| 1290 | "DiagnosticBuilder requires a valid DiagnosticStorage!")(static_cast <bool> (DiagStorage && "DiagnosticBuilder requires a valid DiagnosticStorage!" ) ? void (0) : __assert_fail ("DiagStorage && \"DiagnosticBuilder requires a valid DiagnosticStorage!\"" , "clang/include/clang/Basic/Diagnostic.h", 1290, __extension__ __PRETTY_FUNCTION__)); | ||||
| 1291 | DiagStorage->NumDiagArgs = 0; | ||||
| 1292 | DiagStorage->DiagRanges.clear(); | ||||
| 1293 | DiagStorage->FixItHints.clear(); | ||||
| 1294 | } | ||||
| 1295 | |||||
| 1296 | protected: | ||||
| 1297 | /// Clear out the current diagnostic. | ||||
| 1298 | void Clear() const { | ||||
| 1299 | DiagObj = nullptr; | ||||
| 1300 | IsActive = false; | ||||
| 1301 | IsForceEmit = false; | ||||
| 1302 | } | ||||
| 1303 | |||||
| 1304 | /// Determine whether this diagnostic is still active. | ||||
| 1305 | bool isActive() const { return IsActive; } | ||||
| 1306 | |||||
| 1307 | /// Force the diagnostic builder to emit the diagnostic now. | ||||
| 1308 | /// | ||||
| 1309 | /// Once this function has been called, the DiagnosticBuilder object | ||||
| 1310 | /// should not be used again before it is destroyed. | ||||
| 1311 | /// | ||||
| 1312 | /// \returns true if a diagnostic was emitted, false if the | ||||
| 1313 | /// diagnostic was suppressed. | ||||
| 1314 | bool Emit() { | ||||
| 1315 | // If this diagnostic is inactive, then its soul was stolen by the copy ctor | ||||
| 1316 | // (or by a subclass, as in SemaDiagnosticBuilder). | ||||
| 1317 | if (!isActive()) return false; | ||||
| 1318 | |||||
| 1319 | // Process the diagnostic. | ||||
| 1320 | bool Result = DiagObj->EmitCurrentDiagnostic(IsForceEmit); | ||||
| 1321 | |||||
| 1322 | // This diagnostic is dead. | ||||
| 1323 | Clear(); | ||||
| 1324 | |||||
| 1325 | return Result; | ||||
| 1326 | } | ||||
| 1327 | |||||
| 1328 | public: | ||||
| 1329 | /// Copy constructor. When copied, this "takes" the diagnostic info from the | ||||
| 1330 | /// input and neuters it. | ||||
| 1331 | DiagnosticBuilder(const DiagnosticBuilder &D) : StreamingDiagnostic() { | ||||
| 1332 | DiagObj = D.DiagObj; | ||||
| 1333 | DiagStorage = D.DiagStorage; | ||||
| 1334 | IsActive = D.IsActive; | ||||
| 1335 | IsForceEmit = D.IsForceEmit; | ||||
| 1336 | D.Clear(); | ||||
| 1337 | } | ||||
| 1338 | |||||
| 1339 | template <typename T> const DiagnosticBuilder &operator<<(const T &V) const { | ||||
| 1340 | assert(isActive() && "Clients must not add to cleared diagnostic!")(static_cast <bool> (isActive() && "Clients must not add to cleared diagnostic!" ) ? void (0) : __assert_fail ("isActive() && \"Clients must not add to cleared diagnostic!\"" , "clang/include/clang/Basic/Diagnostic.h", 1340, __extension__ __PRETTY_FUNCTION__)); | ||||
| 1341 | const StreamingDiagnostic &DB = *this; | ||||
| 1342 | DB << V; | ||||
| 1343 | return *this; | ||||
| 1344 | } | ||||
| 1345 | |||||
| 1346 | // It is necessary to limit this to rvalue reference to avoid calling this | ||||
| 1347 | // function with a bitfield lvalue argument since non-const reference to | ||||
| 1348 | // bitfield is not allowed. | ||||
| 1349 | template <typename T, | ||||
| 1350 | typename = std::enable_if_t<!std::is_lvalue_reference<T>::value>> | ||||
| 1351 | const DiagnosticBuilder &operator<<(T &&V) const { | ||||
| 1352 | assert(isActive() && "Clients must not add to cleared diagnostic!")(static_cast <bool> (isActive() && "Clients must not add to cleared diagnostic!" ) ? void (0) : __assert_fail ("isActive() && \"Clients must not add to cleared diagnostic!\"" , "clang/include/clang/Basic/Diagnostic.h", 1352, __extension__ __PRETTY_FUNCTION__)); | ||||
| 1353 | const StreamingDiagnostic &DB = *this; | ||||
| 1354 | DB << std::move(V); | ||||
| 1355 | return *this; | ||||
| 1356 | } | ||||
| 1357 | |||||
| 1358 | DiagnosticBuilder &operator=(const DiagnosticBuilder &) = delete; | ||||
| 1359 | |||||
| 1360 | /// Emits the diagnostic. | ||||
| 1361 | ~DiagnosticBuilder() { Emit(); } | ||||
| 1362 | |||||
| 1363 | /// Forces the diagnostic to be emitted. | ||||
| 1364 | const DiagnosticBuilder &setForceEmit() const { | ||||
| 1365 | IsForceEmit = true; | ||||
| 1366 | return *this; | ||||
| 1367 | } | ||||
| 1368 | |||||
| 1369 | void addFlagValue(StringRef V) const { DiagObj->FlagValue = std::string(V); } | ||||
| 1370 | }; | ||||
| 1371 | |||||
| 1372 | struct AddFlagValue { | ||||
| 1373 | StringRef Val; | ||||
| 1374 | |||||
| 1375 | explicit AddFlagValue(StringRef V) : Val(V) {} | ||||
| 1376 | }; | ||||
| 1377 | |||||
| 1378 | /// Register a value for the flag in the current diagnostic. This | ||||
| 1379 | /// value will be shown as the suffix "=value" after the flag name. It is | ||||
| 1380 | /// useful in cases where the diagnostic flag accepts values (e.g., | ||||
| 1381 | /// -Rpass or -Wframe-larger-than). | ||||
| 1382 | inline const DiagnosticBuilder &operator<<(const DiagnosticBuilder &DB, | ||||
| 1383 | const AddFlagValue V) { | ||||
| 1384 | DB.addFlagValue(V.Val); | ||||
| 1385 | return DB; | ||||
| 1386 | } | ||||
| 1387 | |||||
| 1388 | inline const StreamingDiagnostic &operator<<(const StreamingDiagnostic &DB, | ||||
| 1389 | StringRef S) { | ||||
| 1390 | DB.AddString(S); | ||||
| 1391 | return DB; | ||||
| 1392 | } | ||||
| 1393 | |||||
| 1394 | inline const StreamingDiagnostic &operator<<(const StreamingDiagnostic &DB, | ||||
| 1395 | const char *Str) { | ||||
| 1396 | DB.AddTaggedVal(reinterpret_cast<intptr_t>(Str), | ||||
| 1397 | DiagnosticsEngine::ak_c_string); | ||||
| 1398 | return DB; | ||||
| 1399 | } | ||||
| 1400 | |||||
| 1401 | inline const StreamingDiagnostic &operator<<(const StreamingDiagnostic &DB, | ||||
| 1402 | int I) { | ||||
| 1403 | DB.AddTaggedVal(I, DiagnosticsEngine::ak_sint); | ||||
| 1404 | return DB; | ||||
| 1405 | } | ||||
| 1406 | |||||
| 1407 | inline const StreamingDiagnostic &operator<<(const StreamingDiagnostic &DB, | ||||
| 1408 | long I) { | ||||
| 1409 | DB.AddTaggedVal(I, DiagnosticsEngine::ak_sint); | ||||
| 1410 | return DB; | ||||
| 1411 | } | ||||
| 1412 | |||||
| 1413 | inline const StreamingDiagnostic &operator<<(const StreamingDiagnostic &DB, | ||||
| 1414 | long long I) { | ||||
| 1415 | DB.AddTaggedVal(I, DiagnosticsEngine::ak_sint); | ||||
| 1416 | return DB; | ||||
| 1417 | } | ||||
| 1418 | |||||
| 1419 | // We use enable_if here to prevent that this overload is selected for | ||||
| 1420 | // pointers or other arguments that are implicitly convertible to bool. | ||||
| 1421 | template <typename T> | ||||
| 1422 | inline std::enable_if_t<std::is_same<T, bool>::value, | ||||
| 1423 | const StreamingDiagnostic &> | ||||
| 1424 | operator<<(const StreamingDiagnostic &DB, T I) { | ||||
| 1425 | DB.AddTaggedVal(I, DiagnosticsEngine::ak_sint); | ||||
| 1426 | return DB; | ||||
| 1427 | } | ||||
| 1428 | |||||
| 1429 | inline const StreamingDiagnostic &operator<<(const StreamingDiagnostic &DB, | ||||
| 1430 | unsigned I) { | ||||
| 1431 | DB.AddTaggedVal(I, DiagnosticsEngine::ak_uint); | ||||
| 1432 | return DB; | ||||
| 1433 | } | ||||
| 1434 | |||||
| 1435 | inline const StreamingDiagnostic &operator<<(const StreamingDiagnostic &DB, | ||||
| 1436 | unsigned long I) { | ||||
| 1437 | DB.AddTaggedVal(I, DiagnosticsEngine::ak_uint); | ||||
| 1438 | return DB; | ||||
| 1439 | } | ||||
| 1440 | |||||
| 1441 | inline const StreamingDiagnostic &operator<<(const StreamingDiagnostic &DB, | ||||
| 1442 | unsigned long long I) { | ||||
| 1443 | DB.AddTaggedVal(I, DiagnosticsEngine::ak_uint); | ||||
| 1444 | return DB; | ||||
| 1445 | } | ||||
| 1446 | |||||
| 1447 | inline const StreamingDiagnostic &operator<<(const StreamingDiagnostic &DB, | ||||
| 1448 | tok::TokenKind I) { | ||||
| 1449 | DB.AddTaggedVal(static_cast<unsigned>(I), DiagnosticsEngine::ak_tokenkind); | ||||
| 1450 | return DB; | ||||
| 1451 | } | ||||
| 1452 | |||||
| 1453 | inline const StreamingDiagnostic &operator<<(const StreamingDiagnostic &DB, | ||||
| 1454 | const IdentifierInfo *II) { | ||||
| 1455 | DB.AddTaggedVal(reinterpret_cast<intptr_t>(II), | ||||
| 1456 | DiagnosticsEngine::ak_identifierinfo); | ||||
| 1457 | return DB; | ||||
| 1458 | } | ||||
| 1459 | |||||
| 1460 | // Adds a DeclContext to the diagnostic. The enable_if template magic is here | ||||
| 1461 | // so that we only match those arguments that are (statically) DeclContexts; | ||||
| 1462 | // other arguments that derive from DeclContext (e.g., RecordDecls) will not | ||||
| 1463 | // match. | ||||
| 1464 | template <typename T> | ||||
| 1465 | inline std::enable_if_t< | ||||
| 1466 | std::is_same<std::remove_const_t<T>, DeclContext>::value, | ||||
| 1467 | const StreamingDiagnostic &> | ||||
| 1468 | operator<<(const StreamingDiagnostic &DB, T *DC) { | ||||
| 1469 | DB.AddTaggedVal(reinterpret_cast<intptr_t>(DC), | ||||
| 1470 | DiagnosticsEngine::ak_declcontext); | ||||
| 1471 | return DB; | ||||
| 1472 | } | ||||
| 1473 | |||||
| 1474 | inline const StreamingDiagnostic &operator<<(const StreamingDiagnostic &DB, | ||||
| 1475 | SourceLocation L) { | ||||
| 1476 | DB.AddSourceRange(CharSourceRange::getTokenRange(L)); | ||||
| 1477 | return DB; | ||||
| 1478 | } | ||||
| 1479 | |||||
| 1480 | inline const StreamingDiagnostic &operator<<(const StreamingDiagnostic &DB, | ||||
| 1481 | SourceRange R) { | ||||
| 1482 | DB.AddSourceRange(CharSourceRange::getTokenRange(R)); | ||||
| 1483 | return DB; | ||||
| 1484 | } | ||||
| 1485 | |||||
| 1486 | inline const StreamingDiagnostic &operator<<(const StreamingDiagnostic &DB, | ||||
| 1487 | ArrayRef<SourceRange> Ranges) { | ||||
| 1488 | for (SourceRange R : Ranges) | ||||
| 1489 | DB.AddSourceRange(CharSourceRange::getTokenRange(R)); | ||||
| 1490 | return DB; | ||||
| 1491 | } | ||||
| 1492 | |||||
| 1493 | inline const StreamingDiagnostic &operator<<(const StreamingDiagnostic &DB, | ||||
| 1494 | const CharSourceRange &R) { | ||||
| 1495 | DB.AddSourceRange(R); | ||||
| 1496 | return DB; | ||||
| 1497 | } | ||||
| 1498 | |||||
| 1499 | inline const StreamingDiagnostic &operator<<(const StreamingDiagnostic &DB, | ||||
| 1500 | const FixItHint &Hint) { | ||||
| 1501 | DB.AddFixItHint(Hint); | ||||
| 1502 | return DB; | ||||
| 1503 | } | ||||
| 1504 | |||||
| 1505 | inline const StreamingDiagnostic &operator<<(const StreamingDiagnostic &DB, | ||||
| 1506 | ArrayRef<FixItHint> Hints) { | ||||
| 1507 | for (const FixItHint &Hint : Hints) | ||||
| 1508 | DB.AddFixItHint(Hint); | ||||
| 1509 | return DB; | ||||
| 1510 | } | ||||
| 1511 | |||||
| 1512 | inline const StreamingDiagnostic & | ||||
| 1513 | operator<<(const StreamingDiagnostic &DB, | ||||
| 1514 | const std::optional<SourceRange> &Opt) { | ||||
| 1515 | if (Opt) | ||||
| 1516 | DB << *Opt; | ||||
| 1517 | return DB; | ||||
| 1518 | } | ||||
| 1519 | |||||
| 1520 | inline const StreamingDiagnostic & | ||||
| 1521 | operator<<(const StreamingDiagnostic &DB, | ||||
| 1522 | const std::optional<CharSourceRange> &Opt) { | ||||
| 1523 | if (Opt) | ||||
| 1524 | DB << *Opt; | ||||
| 1525 | return DB; | ||||
| 1526 | } | ||||
| 1527 | |||||
| 1528 | inline const StreamingDiagnostic & | ||||
| 1529 | operator<<(const StreamingDiagnostic &DB, const std::optional<FixItHint> &Opt) { | ||||
| 1530 | if (Opt) | ||||
| 1531 | DB << *Opt; | ||||
| 1532 | return DB; | ||||
| 1533 | } | ||||
| 1534 | |||||
| 1535 | /// A nullability kind paired with a bit indicating whether it used a | ||||
| 1536 | /// context-sensitive keyword. | ||||
| 1537 | using DiagNullabilityKind = std::pair<NullabilityKind, bool>; | ||||
| 1538 | |||||
| 1539 | const StreamingDiagnostic &operator<<(const StreamingDiagnostic &DB, | ||||
| 1540 | DiagNullabilityKind nullability); | ||||
| 1541 | |||||
| 1542 | inline DiagnosticBuilder DiagnosticsEngine::Report(SourceLocation Loc, | ||||
| 1543 | unsigned DiagID) { | ||||
| 1544 | assert(CurDiagID == std::numeric_limits<unsigned>::max() &&(static_cast <bool> (CurDiagID == std::numeric_limits< unsigned>::max() && "Multiple diagnostics in flight at once!" ) ? void (0) : __assert_fail ("CurDiagID == std::numeric_limits<unsigned>::max() && \"Multiple diagnostics in flight at once!\"" , "clang/include/clang/Basic/Diagnostic.h", 1545, __extension__ __PRETTY_FUNCTION__)) | ||||
| 1545 | "Multiple diagnostics in flight at once!")(static_cast <bool> (CurDiagID == std::numeric_limits< unsigned>::max() && "Multiple diagnostics in flight at once!" ) ? void (0) : __assert_fail ("CurDiagID == std::numeric_limits<unsigned>::max() && \"Multiple diagnostics in flight at once!\"" , "clang/include/clang/Basic/Diagnostic.h", 1545, __extension__ __PRETTY_FUNCTION__)); | ||||
| 1546 | CurDiagLoc = Loc; | ||||
| 1547 | CurDiagID = DiagID; | ||||
| 1548 | FlagValue.clear(); | ||||
| 1549 | return DiagnosticBuilder(this); | ||||
| 1550 | } | ||||
| 1551 | |||||
| 1552 | const StreamingDiagnostic &operator<<(const StreamingDiagnostic &DB, | ||||
| 1553 | llvm::Error &&E); | ||||
| 1554 | |||||
| 1555 | inline DiagnosticBuilder DiagnosticsEngine::Report(unsigned DiagID) { | ||||
| 1556 | return Report(SourceLocation(), DiagID); | ||||
| 1557 | } | ||||
| 1558 | |||||
| 1559 | //===----------------------------------------------------------------------===// | ||||
| 1560 | // Diagnostic | ||||
| 1561 | //===----------------------------------------------------------------------===// | ||||
| 1562 | |||||
| 1563 | /// A little helper class (which is basically a smart pointer that forwards | ||||
| 1564 | /// info from DiagnosticsEngine) that allows clients to enquire about the | ||||
| 1565 | /// currently in-flight diagnostic. | ||||
| 1566 | class Diagnostic { | ||||
| 1567 | const DiagnosticsEngine *DiagObj; | ||||
| 1568 | std::optional<StringRef> StoredDiagMessage; | ||||
| 1569 | |||||
| 1570 | public: | ||||
| 1571 | explicit Diagnostic(const DiagnosticsEngine *DO) : DiagObj(DO) {} | ||||
| 1572 | Diagnostic(const DiagnosticsEngine *DO, StringRef storedDiagMessage) | ||||
| 1573 | : DiagObj(DO), StoredDiagMessage(storedDiagMessage) {} | ||||
| 1574 | |||||
| 1575 | const DiagnosticsEngine *getDiags() const { return DiagObj; } | ||||
| 1576 | unsigned getID() const { return DiagObj->CurDiagID; } | ||||
| 1577 | const SourceLocation &getLocation() const { return DiagObj->CurDiagLoc; } | ||||
| 1578 | bool hasSourceManager() const { return DiagObj->hasSourceManager(); } | ||||
| 1579 | SourceManager &getSourceManager() const { return DiagObj->getSourceManager();} | ||||
| 1580 | |||||
| 1581 | unsigned getNumArgs() const { return DiagObj->DiagStorage.NumDiagArgs; } | ||||
| 1582 | |||||
| 1583 | /// Return the kind of the specified index. | ||||
| 1584 | /// | ||||
| 1585 | /// Based on the kind of argument, the accessors below can be used to get | ||||
| 1586 | /// the value. | ||||
| 1587 | /// | ||||
| 1588 | /// \pre Idx < getNumArgs() | ||||
| 1589 | DiagnosticsEngine::ArgumentKind getArgKind(unsigned Idx) const { | ||||
| 1590 | assert(Idx < getNumArgs() && "Argument index out of range!")(static_cast <bool> (Idx < getNumArgs() && "Argument index out of range!" ) ? void (0) : __assert_fail ("Idx < getNumArgs() && \"Argument index out of range!\"" , "clang/include/clang/Basic/Diagnostic.h", 1590, __extension__ __PRETTY_FUNCTION__)); | ||||
| 1591 | return (DiagnosticsEngine::ArgumentKind) | ||||
| 1592 | DiagObj->DiagStorage.DiagArgumentsKind[Idx]; | ||||
| 1593 | } | ||||
| 1594 | |||||
| 1595 | /// Return the provided argument string specified by \p Idx. | ||||
| 1596 | /// \pre getArgKind(Idx) == DiagnosticsEngine::ak_std_string | ||||
| 1597 | const std::string &getArgStdStr(unsigned Idx) const { | ||||
| 1598 | assert(getArgKind(Idx) == DiagnosticsEngine::ak_std_string &&(static_cast <bool> (getArgKind(Idx) == DiagnosticsEngine ::ak_std_string && "invalid argument accessor!") ? void (0) : __assert_fail ("getArgKind(Idx) == DiagnosticsEngine::ak_std_string && \"invalid argument accessor!\"" , "clang/include/clang/Basic/Diagnostic.h", 1599, __extension__ __PRETTY_FUNCTION__)) | ||||
| 1599 | "invalid argument accessor!")(static_cast <bool> (getArgKind(Idx) == DiagnosticsEngine ::ak_std_string && "invalid argument accessor!") ? void (0) : __assert_fail ("getArgKind(Idx) == DiagnosticsEngine::ak_std_string && \"invalid argument accessor!\"" , "clang/include/clang/Basic/Diagnostic.h", 1599, __extension__ __PRETTY_FUNCTION__)); | ||||
| 1600 | return DiagObj->DiagStorage.DiagArgumentsStr[Idx]; | ||||
| 1601 | } | ||||
| 1602 | |||||
| 1603 | /// Return the specified C string argument. | ||||
| 1604 | /// \pre getArgKind(Idx) == DiagnosticsEngine::ak_c_string | ||||
| 1605 | const char *getArgCStr(unsigned Idx) const { | ||||
| 1606 | assert(getArgKind(Idx) == DiagnosticsEngine::ak_c_string &&(static_cast <bool> (getArgKind(Idx) == DiagnosticsEngine ::ak_c_string && "invalid argument accessor!") ? void (0) : __assert_fail ("getArgKind(Idx) == DiagnosticsEngine::ak_c_string && \"invalid argument accessor!\"" , "clang/include/clang/Basic/Diagnostic.h", 1607, __extension__ __PRETTY_FUNCTION__)) | ||||
| 1607 | "invalid argument accessor!")(static_cast <bool> (getArgKind(Idx) == DiagnosticsEngine ::ak_c_string && "invalid argument accessor!") ? void (0) : __assert_fail ("getArgKind(Idx) == DiagnosticsEngine::ak_c_string && \"invalid argument accessor!\"" , "clang/include/clang/Basic/Diagnostic.h", 1607, __extension__ __PRETTY_FUNCTION__)); | ||||
| 1608 | return reinterpret_cast<const char *>( | ||||
| 1609 | DiagObj->DiagStorage.DiagArgumentsVal[Idx]); | ||||
| 1610 | } | ||||
| 1611 | |||||
| 1612 | /// Return the specified signed integer argument. | ||||
| 1613 | /// \pre getArgKind(Idx) == DiagnosticsEngine::ak_sint | ||||
| 1614 | int64_t getArgSInt(unsigned Idx) const { | ||||
| 1615 | assert(getArgKind(Idx) == DiagnosticsEngine::ak_sint &&(static_cast <bool> (getArgKind(Idx) == DiagnosticsEngine ::ak_sint && "invalid argument accessor!") ? void (0) : __assert_fail ("getArgKind(Idx) == DiagnosticsEngine::ak_sint && \"invalid argument accessor!\"" , "clang/include/clang/Basic/Diagnostic.h", 1616, __extension__ __PRETTY_FUNCTION__)) | ||||
| 1616 | "invalid argument accessor!")(static_cast <bool> (getArgKind(Idx) == DiagnosticsEngine ::ak_sint && "invalid argument accessor!") ? void (0) : __assert_fail ("getArgKind(Idx) == DiagnosticsEngine::ak_sint && \"invalid argument accessor!\"" , "clang/include/clang/Basic/Diagnostic.h", 1616, __extension__ __PRETTY_FUNCTION__)); | ||||
| 1617 | return (int64_t)DiagObj->DiagStorage.DiagArgumentsVal[Idx]; | ||||
| 1618 | } | ||||
| 1619 | |||||
| 1620 | /// Return the specified unsigned integer argument. | ||||
| 1621 | /// \pre getArgKind(Idx) == DiagnosticsEngine::ak_uint | ||||
| 1622 | uint64_t getArgUInt(unsigned Idx) const { | ||||
| 1623 | assert(getArgKind(Idx) == DiagnosticsEngine::ak_uint &&(static_cast <bool> (getArgKind(Idx) == DiagnosticsEngine ::ak_uint && "invalid argument accessor!") ? void (0) : __assert_fail ("getArgKind(Idx) == DiagnosticsEngine::ak_uint && \"invalid argument accessor!\"" , "clang/include/clang/Basic/Diagnostic.h", 1624, __extension__ __PRETTY_FUNCTION__)) | ||||
| 1624 | "invalid argument accessor!")(static_cast <bool> (getArgKind(Idx) == DiagnosticsEngine ::ak_uint && "invalid argument accessor!") ? void (0) : __assert_fail ("getArgKind(Idx) == DiagnosticsEngine::ak_uint && \"invalid argument accessor!\"" , "clang/include/clang/Basic/Diagnostic.h", 1624, __extension__ __PRETTY_FUNCTION__)); | ||||
| 1625 | return DiagObj->DiagStorage.DiagArgumentsVal[Idx]; | ||||
| 1626 | } | ||||
| 1627 | |||||
| 1628 | /// Return the specified IdentifierInfo argument. | ||||
| 1629 | /// \pre getArgKind(Idx) == DiagnosticsEngine::ak_identifierinfo | ||||
| 1630 | const IdentifierInfo *getArgIdentifier(unsigned Idx) const { | ||||
| 1631 | assert(getArgKind(Idx) == DiagnosticsEngine::ak_identifierinfo &&(static_cast <bool> (getArgKind(Idx) == DiagnosticsEngine ::ak_identifierinfo && "invalid argument accessor!") ? void (0) : __assert_fail ("getArgKind(Idx) == DiagnosticsEngine::ak_identifierinfo && \"invalid argument accessor!\"" , "clang/include/clang/Basic/Diagnostic.h", 1632, __extension__ __PRETTY_FUNCTION__)) | ||||
| 1632 | "invalid argument accessor!")(static_cast <bool> (getArgKind(Idx) == DiagnosticsEngine ::ak_identifierinfo && "invalid argument accessor!") ? void (0) : __assert_fail ("getArgKind(Idx) == DiagnosticsEngine::ak_identifierinfo && \"invalid argument accessor!\"" , "clang/include/clang/Basic/Diagnostic.h", 1632, __extension__ __PRETTY_FUNCTION__)); | ||||
| 1633 | return reinterpret_cast<IdentifierInfo *>( | ||||
| 1634 | DiagObj->DiagStorage.DiagArgumentsVal[Idx]); | ||||
| 1635 | } | ||||
| 1636 | |||||
| 1637 | /// Return the specified non-string argument in an opaque form. | ||||
| 1638 | /// \pre getArgKind(Idx) != DiagnosticsEngine::ak_std_string | ||||
| 1639 | uint64_t getRawArg(unsigned Idx) const { | ||||
| 1640 | assert(getArgKind(Idx) != DiagnosticsEngine::ak_std_string &&(static_cast <bool> (getArgKind(Idx) != DiagnosticsEngine ::ak_std_string && "invalid argument accessor!") ? void (0) : __assert_fail ("getArgKind(Idx) != DiagnosticsEngine::ak_std_string && \"invalid argument accessor!\"" , "clang/include/clang/Basic/Diagnostic.h", 1641, __extension__ __PRETTY_FUNCTION__)) | ||||
| 1641 | "invalid argument accessor!")(static_cast <bool> (getArgKind(Idx) != DiagnosticsEngine ::ak_std_string && "invalid argument accessor!") ? void (0) : __assert_fail ("getArgKind(Idx) != DiagnosticsEngine::ak_std_string && \"invalid argument accessor!\"" , "clang/include/clang/Basic/Diagnostic.h", 1641, __extension__ __PRETTY_FUNCTION__)); | ||||
| 1642 | return DiagObj->DiagStorage.DiagArgumentsVal[Idx]; | ||||
| 1643 | } | ||||
| 1644 | |||||
| 1645 | /// Return the number of source ranges associated with this diagnostic. | ||||
| 1646 | unsigned getNumRanges() const { | ||||
| 1647 | return DiagObj->DiagStorage.DiagRanges.size(); | ||||
| 1648 | } | ||||
| 1649 | |||||
| 1650 | /// \pre Idx < getNumRanges() | ||||
| 1651 | const CharSourceRange &getRange(unsigned Idx) const { | ||||
| 1652 | assert(Idx < getNumRanges() && "Invalid diagnostic range index!")(static_cast <bool> (Idx < getNumRanges() && "Invalid diagnostic range index!") ? void (0) : __assert_fail ("Idx < getNumRanges() && \"Invalid diagnostic range index!\"" , "clang/include/clang/Basic/Diagnostic.h", 1652, __extension__ __PRETTY_FUNCTION__)); | ||||
| 1653 | return DiagObj->DiagStorage.DiagRanges[Idx]; | ||||
| 1654 | } | ||||
| 1655 | |||||
| 1656 | /// Return an array reference for this diagnostic's ranges. | ||||
| 1657 | ArrayRef<CharSourceRange> getRanges() const { | ||||
| 1658 | return DiagObj->DiagStorage.DiagRanges; | ||||
| 1659 | } | ||||
| 1660 | |||||
| 1661 | unsigned getNumFixItHints() const { | ||||
| 1662 | return DiagObj->DiagStorage.FixItHints.size(); | ||||
| 1663 | } | ||||
| 1664 | |||||
| 1665 | const FixItHint &getFixItHint(unsigned Idx) const { | ||||
| 1666 | assert(Idx < getNumFixItHints() && "Invalid index!")(static_cast <bool> (Idx < getNumFixItHints() && "Invalid index!") ? void (0) : __assert_fail ("Idx < getNumFixItHints() && \"Invalid index!\"" , "clang/include/clang/Basic/Diagnostic.h", 1666, __extension__ __PRETTY_FUNCTION__)); | ||||
| 1667 | return DiagObj->DiagStorage.FixItHints[Idx]; | ||||
| 1668 | } | ||||
| 1669 | |||||
| 1670 | ArrayRef<FixItHint> getFixItHints() const { | ||||
| 1671 | return DiagObj->DiagStorage.FixItHints; | ||||
| 1672 | } | ||||
| 1673 | |||||
| 1674 | /// Format this diagnostic into a string, substituting the | ||||
| 1675 | /// formal arguments into the %0 slots. | ||||
| 1676 | /// | ||||
| 1677 | /// The result is appended onto the \p OutStr array. | ||||
| 1678 | void FormatDiagnostic(SmallVectorImpl<char> &OutStr) const; | ||||
| 1679 | |||||
| 1680 | /// Format the given format-string into the output buffer using the | ||||
| 1681 | /// arguments stored in this diagnostic. | ||||
| 1682 | void FormatDiagnostic(const char *DiagStr, const char *DiagEnd, | ||||
| 1683 | SmallVectorImpl<char> &OutStr) const; | ||||
| 1684 | }; | ||||
| 1685 | |||||
| 1686 | /** | ||||
| 1687 | * Represents a diagnostic in a form that can be retained until its | ||||
| 1688 | * corresponding source manager is destroyed. | ||||
| 1689 | */ | ||||
| 1690 | class StoredDiagnostic { | ||||
| 1691 | unsigned ID; | ||||
| 1692 | DiagnosticsEngine::Level Level; | ||||
| 1693 | FullSourceLoc Loc; | ||||
| 1694 | std::string Message; | ||||
| 1695 | std::vector<CharSourceRange> Ranges; | ||||
| 1696 | std::vector<FixItHint> FixIts; | ||||
| 1697 | |||||
| 1698 | public: | ||||
| 1699 | StoredDiagnostic() = default; | ||||
| 1700 | StoredDiagnostic(DiagnosticsEngine::Level Level, const Diagnostic &Info); | ||||
| 1701 | StoredDiagnostic(DiagnosticsEngine::Level Level, unsigned ID, | ||||
| 1702 | StringRef Message); | ||||
| 1703 | StoredDiagnostic(DiagnosticsEngine::Level Level, unsigned ID, | ||||
| 1704 | StringRef Message, FullSourceLoc Loc, | ||||
| 1705 | ArrayRef<CharSourceRange> Ranges, | ||||
| 1706 | ArrayRef<FixItHint> Fixits); | ||||
| 1707 | |||||
| 1708 | /// Evaluates true when this object stores a diagnostic. | ||||
| 1709 | explicit operator bool() const { return !Message.empty(); } | ||||
| 1710 | |||||
| 1711 | unsigned getID() const { return ID; } | ||||
| 1712 | DiagnosticsEngine::Level getLevel() const { return Level; } | ||||
| 1713 | const FullSourceLoc &getLocation() const { return Loc; } | ||||
| 1714 | StringRef getMessage() const { return Message; } | ||||
| 1715 | |||||
| 1716 | void setLocation(FullSourceLoc Loc) { this->Loc = Loc; } | ||||
| 1717 | |||||
| 1718 | using range_iterator = std::vector<CharSourceRange>::const_iterator; | ||||
| 1719 | |||||
| 1720 | range_iterator range_begin() const { return Ranges.begin(); } | ||||
| 1721 | range_iterator range_end() const { return Ranges.end(); } | ||||
| 1722 | unsigned range_size() const { return Ranges.size(); } | ||||
| 1723 | |||||
| 1724 | ArrayRef<CharSourceRange> getRanges() const { return llvm::ArrayRef(Ranges); } | ||||
| 1725 | |||||
| 1726 | using fixit_iterator = std::vector<FixItHint>::const_iterator; | ||||
| 1727 | |||||
| 1728 | fixit_iterator fixit_begin() const { return FixIts.begin(); } | ||||
| 1729 | fixit_iterator fixit_end() const { return FixIts.end(); } | ||||
| 1730 | unsigned fixit_size() const { return FixIts.size(); } | ||||
| 1731 | |||||
| 1732 | ArrayRef<FixItHint> getFixIts() const { return llvm::ArrayRef(FixIts); } | ||||
| 1733 | }; | ||||
| 1734 | |||||
| 1735 | // Simple debug printing of StoredDiagnostic. | ||||
| 1736 | llvm::raw_ostream &operator<<(llvm::raw_ostream &OS, const StoredDiagnostic &); | ||||
| 1737 | |||||
| 1738 | /// Abstract interface, implemented by clients of the front-end, which | ||||
| 1739 | /// formats and prints fully processed diagnostics. | ||||
| 1740 | class DiagnosticConsumer { | ||||
| 1741 | protected: | ||||
| 1742 | unsigned NumWarnings = 0; ///< Number of warnings reported | ||||
| 1743 | unsigned NumErrors = 0; ///< Number of errors reported | ||||
| 1744 | |||||
| 1745 | public: | ||||
| 1746 | DiagnosticConsumer() = default; | ||||
| 1747 | virtual ~DiagnosticConsumer(); | ||||
| 1748 | |||||
| 1749 | unsigned getNumErrors() const { return NumErrors; } | ||||
| 1750 | unsigned getNumWarnings() const { return NumWarnings; } | ||||
| 1751 | virtual void clear() { NumWarnings = NumErrors = 0; } | ||||
| 1752 | |||||
| 1753 | /// Callback to inform the diagnostic client that processing | ||||
| 1754 | /// of a source file is beginning. | ||||
| 1755 | /// | ||||
| 1756 | /// Note that diagnostics may be emitted outside the processing of a source | ||||
| 1757 | /// file, for example during the parsing of command line options. However, | ||||
| 1758 | /// diagnostics with source range information are required to only be emitted | ||||
| 1759 | /// in between BeginSourceFile() and EndSourceFile(). | ||||
| 1760 | /// | ||||
| 1761 | /// \param LangOpts The language options for the source file being processed. | ||||
| 1762 | /// \param PP The preprocessor object being used for the source; this is | ||||
| 1763 | /// optional, e.g., it may not be present when processing AST source files. | ||||
| 1764 | virtual void BeginSourceFile(const LangOptions &LangOpts, | ||||
| 1765 | const Preprocessor *PP = nullptr) {} | ||||
| 1766 | |||||
| 1767 | /// Callback to inform the diagnostic client that processing | ||||
| 1768 | /// of a source file has ended. | ||||
| 1769 | /// | ||||
| 1770 | /// The diagnostic client should assume that any objects made available via | ||||
| 1771 | /// BeginSourceFile() are inaccessible. | ||||
| 1772 | virtual void EndSourceFile() {} | ||||
| 1773 | |||||
| 1774 | /// Callback to inform the diagnostic client that processing of all | ||||
| 1775 | /// source files has ended. | ||||
| 1776 | virtual void finish() {} | ||||
| 1777 | |||||
| 1778 | /// Indicates whether the diagnostics handled by this | ||||
| 1779 | /// DiagnosticConsumer should be included in the number of diagnostics | ||||
| 1780 | /// reported by DiagnosticsEngine. | ||||
| 1781 | /// | ||||
| 1782 | /// The default implementation returns true. | ||||
| 1783 | virtual bool IncludeInDiagnosticCounts() const; | ||||
| 1784 | |||||
| 1785 | /// Handle this diagnostic, reporting it to the user or | ||||
| 1786 | /// capturing it to a log as needed. | ||||
| 1787 | /// | ||||
| 1788 | /// The default implementation just keeps track of the total number of | ||||
| 1789 | /// warnings and errors. | ||||
| 1790 | virtual void HandleDiagnostic(DiagnosticsEngine::Level DiagLevel, | ||||
| 1791 | const Diagnostic &Info); | ||||
| 1792 | }; | ||||
| 1793 | |||||
| 1794 | /// A diagnostic client that ignores all diagnostics. | ||||
| 1795 | class IgnoringDiagConsumer : public DiagnosticConsumer { | ||||
| 1796 | virtual void anchor(); | ||||
| 1797 | |||||
| 1798 | void HandleDiagnostic(DiagnosticsEngine::Level DiagLevel, | ||||
| 1799 | const Diagnostic &Info) override { | ||||
| 1800 | // Just ignore it. | ||||
| 1801 | } | ||||
| 1802 | }; | ||||
| 1803 | |||||
| 1804 | /// Diagnostic consumer that forwards diagnostics along to an | ||||
| 1805 | /// existing, already-initialized diagnostic consumer. | ||||
| 1806 | /// | ||||
| 1807 | class ForwardingDiagnosticConsumer : public DiagnosticConsumer { | ||||
| 1808 | DiagnosticConsumer &Target; | ||||
| 1809 | |||||
| 1810 | public: | ||||
| 1811 | ForwardingDiagnosticConsumer(DiagnosticConsumer &Target) : Target(Target) {} | ||||
| 1812 | ~ForwardingDiagnosticConsumer() override; | ||||
| 1813 | |||||
| 1814 | void HandleDiagnostic(DiagnosticsEngine::Level DiagLevel, | ||||
| 1815 | const Diagnostic &Info) override; | ||||
| 1816 | void clear() override; | ||||
| 1817 | |||||
| 1818 | bool IncludeInDiagnosticCounts() const override; | ||||
| 1819 | }; | ||||
| 1820 | |||||
| 1821 | // Struct used for sending info about how a type should be printed. | ||||
| 1822 | struct TemplateDiffTypes { | ||||
| 1823 | intptr_t FromType; | ||||
| 1824 | intptr_t ToType; | ||||
| 1825 | unsigned PrintTree : 1; | ||||
| 1826 | unsigned PrintFromType : 1; | ||||
| 1827 | unsigned ElideType : 1; | ||||
| 1828 | unsigned ShowColors : 1; | ||||
| 1829 | |||||
| 1830 | // The printer sets this variable to true if the template diff was used. | ||||
| 1831 | unsigned TemplateDiffUsed : 1; | ||||
| 1832 | }; | ||||
| 1833 | |||||
| 1834 | /// Special character that the diagnostic printer will use to toggle the bold | ||||
| 1835 | /// attribute. The character itself will be not be printed. | ||||
| 1836 | const char ToggleHighlight = 127; | ||||
| 1837 | |||||
| 1838 | /// ProcessWarningOptions - Initialize the diagnostic client and process the | ||||
| 1839 | /// warning options specified on the command line. | ||||
| 1840 | void ProcessWarningOptions(DiagnosticsEngine &Diags, | ||||
| 1841 | const DiagnosticOptions &Opts, | ||||
| 1842 | bool ReportDiags = true); | ||||
| 1843 | |||||
| 1844 | } // namespace clang | ||||
| 1845 | |||||
| 1846 | #endif // LLVM_CLANG_BASIC_DIAGNOSTIC_H |