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 |