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