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