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.
hasFlag(MixFlags Data,MixFlags SearchedFlag)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.
formatMixFlags(MixFlags F)151 static inline std::string formatMixFlags(MixFlags F) {
152 if (F == MixFlags::Invalid)
153 return "#Inv!";
154
155 SmallString<8> Str{"-------"};
156
157 if (hasFlag(F, MixFlags::None))
158 // Shows the None bit explicitly, as it can be applied in the recursion
159 // even if other bits are set.
160 Str[0] = '!';
161 if (hasFlag(F, MixFlags::Trivial))
162 Str[1] = 'T';
163 if (hasFlag(F, MixFlags::Canonical))
164 Str[2] = 'C';
165 if (hasFlag(F, MixFlags::TypeAlias))
166 Str[3] = 't';
167 if (hasFlag(F, MixFlags::ReferenceBind))
168 Str[4] = '&';
169 if (hasFlag(F, MixFlags::Qualifiers))
170 Str[5] = 'Q';
171 if (hasFlag(F, MixFlags::ImplicitConversion))
172 Str[6] = 'i';
173
174 if (hasFlag(F, MixFlags::WorkaroundDisableCanonicalEquivalence))
175 Str.append("(~C)");
176
177 return Str.str().str();
178 }
179
180 #endif // NDEBUG
181
182 /// The results of the steps of an Implicit Conversion Sequence is saved in
183 /// an instance of this record.
184 ///
185 /// A ConversionSequence maps the steps of the conversion with a member for
186 /// each type involved in the conversion. Imagine going from a hypothetical
187 /// Complex class to projecting it to the real part as a const double.
188 ///
189 /// I.e., given:
190 ///
191 /// struct Complex {
192 /// operator double() const;
193 /// };
194 ///
195 /// void functionBeingAnalysed(Complex C, const double R);
196 ///
197 /// we will get the following sequence:
198 ///
199 /// (Begin=) Complex
200 ///
201 /// The first standard conversion is a qualification adjustment.
202 /// (AfterFirstStandard=) const Complex
203 ///
204 /// Then the user-defined conversion is executed.
205 /// (UDConvOp.ConversionOperatorResultType=) double
206 ///
207 /// Then this 'double' is qualifier-adjusted to 'const double'.
208 /// (AfterSecondStandard=) double
209 ///
210 /// The conversion's result has now been calculated, so it ends here.
211 /// (End=) double.
212 ///
213 /// Explicit storing of Begin and End in this record is needed, because
214 /// getting to what Begin and End here are needs further resolution of types,
215 /// e.g. in the case of typedefs:
216 ///
217 /// using Comp = Complex;
218 /// using CD = const double;
219 /// void functionBeingAnalysed2(Comp C, CD R);
220 ///
221 /// In this case, the user will be diagnosed with a potential conversion
222 /// between the two typedefs as written in the code, but to elaborate the
223 /// reasoning behind this conversion, we also need to show what the typedefs
224 /// mean. See FormattedConversionSequence towards the bottom of this file!
225 struct ConversionSequence {
226 enum UserDefinedConversionKind { UDCK_None, UDCK_Ctor, UDCK_Oper };
227
228 struct UserDefinedConvertingConstructor {
229 const CXXConstructorDecl *Fun;
230 QualType ConstructorParameterType;
231 QualType UserDefinedType;
232 };
233
234 struct UserDefinedConversionOperator {
235 const CXXConversionDecl *Fun;
236 QualType UserDefinedType;
237 QualType ConversionOperatorResultType;
238 };
239
240 /// The type the conversion stared from.
241 QualType Begin;
242
243 /// The intermediate type after the first Standard Conversion Sequence.
244 QualType AfterFirstStandard;
245
246 /// The details of the user-defined conversion involved, as a tagged union.
247 union {
248 char None;
249 UserDefinedConvertingConstructor UDConvCtor;
250 UserDefinedConversionOperator UDConvOp;
251 };
252 UserDefinedConversionKind UDConvKind;
253
254 /// The intermediate type after performing the second Standard Conversion
255 /// Sequence.
256 QualType AfterSecondStandard;
257
258 /// The result type the conversion targeted.
259 QualType End;
260
ConversionSequenceclang::tidy::bugprone::model::ConversionSequence261 ConversionSequence() : None(0), UDConvKind(UDCK_None) {}
ConversionSequenceclang::tidy::bugprone::model::ConversionSequence262 ConversionSequence(QualType From, QualType To)
263 : Begin(From), None(0), UDConvKind(UDCK_None), End(To) {}
264
operator boolclang::tidy::bugprone::model::ConversionSequence265 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.
getInvolvedTypesInSequenceclang::tidy::bugprone::model::ConversionSequence272 SmallVector<QualType, 4> getInvolvedTypesInSequence() const {
273 SmallVector<QualType, 4> Ret;
274 auto EmplaceIfDifferent = [&Ret](QualType QT) {
275 if (QT.isNull())
276 return;
277 if (Ret.empty())
278 Ret.emplace_back(QT);
279 else if (Ret.back() != QT)
280 Ret.emplace_back(QT);
281 };
282
283 EmplaceIfDifferent(AfterFirstStandard);
284 switch (UDConvKind) {
285 case UDCK_Ctor:
286 EmplaceIfDifferent(UDConvCtor.ConstructorParameterType);
287 EmplaceIfDifferent(UDConvCtor.UserDefinedType);
288 break;
289 case UDCK_Oper:
290 EmplaceIfDifferent(UDConvOp.UserDefinedType);
291 EmplaceIfDifferent(UDConvOp.ConversionOperatorResultType);
292 break;
293 case UDCK_None:
294 break;
295 }
296 EmplaceIfDifferent(AfterSecondStandard);
297
298 return Ret;
299 }
300
301 /// Updates the steps of the conversion sequence with the steps from the
302 /// other instance.
303 ///
304 /// \note This method does not check if the resulting conversion sequence is
305 /// sensible!
updateclang::tidy::bugprone::model::ConversionSequence306 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.
setConversionclang::tidy::bugprone::model::ConversionSequence328 void setConversion(const UserDefinedConvertingConstructor &UDCC) {
329 UDConvKind = UDCK_Ctor;
330 UDConvCtor = UDCC;
331 }
332
333 /// Sets the user-defined conversion to the given operator.
setConversionclang::tidy::bugprone::model::ConversionSequence334 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.
getTypeAfterUserDefinedConversionclang::tidy::bugprone::model::ConversionSequence341 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
getUserDefinedConversionFunctionclang::tidy::bugprone::model::ConversionSequence353 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.
getUserDefinedConversionHighlightclang::tidy::bugprone::model::ConversionSequence369 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
MixDataclang::tidy::bugprone::model::MixData405 MixData(MixFlags Flags) : Flags(Flags) {}
MixDataclang::tidy::bugprone::model::MixData406 MixData(MixFlags Flags, QualType CommonType)
407 : Flags(Flags), CommonType(CommonType) {}
MixDataclang::tidy::bugprone::model::MixData408 MixData(MixFlags Flags, ConversionSequence Conv)
409 : Flags(Flags), Conversion(Conv), CreatedFromOneWayConversion(true) {}
MixDataclang::tidy::bugprone::model::MixData410 MixData(MixFlags Flags, ConversionSequence LTR, ConversionSequence RTL)
411 : Flags(Flags), Conversion(LTR), ConversionRTL(RTL) {}
MixDataclang::tidy::bugprone::model::MixData412 MixData(MixFlags Flags, QualType CommonType, ConversionSequence LTR,
413 ConversionSequence RTL)
414 : Flags(Flags), CommonType(CommonType), Conversion(LTR),
415 ConversionRTL(RTL) {}
416
sanitizeclang::tidy::bugprone::model::MixData417 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(Flags, MixFlags::None)) {
431 // If anywhere down the recursion a potential mix "path" is deemed
432 // impossible, throw away all the other bits because the mix is not
433 // possible.
434 Flags = MixFlags::None;
435 return;
436 }
437
438 if (Flags == MixFlags::Trivial)
439 return;
440
441 if (static_cast<bool>(Flags ^ MixFlags::Trivial))
442 // If the mix involves somewhere trivial equivalence but down the
443 // recursion other bit(s) were set, remove the trivial bit, as it is not
444 // trivial.
445 Flags &= ~MixFlags::Trivial;
446
447 bool ShouldHaveImplicitConvFlag = false;
448 if (CreatedFromOneWayConversion && Conversion)
449 ShouldHaveImplicitConvFlag = true;
450 else if (!CreatedFromOneWayConversion && Conversion && ConversionRTL)
451 // Only say that we have implicit conversion mix possibility if it is
452 // bidirectional. Otherwise, the compiler would report an *actual* swap
453 // at a call site...
454 ShouldHaveImplicitConvFlag = true;
455
456 if (ShouldHaveImplicitConvFlag)
457 Flags |= MixFlags::ImplicitConversion;
458 else
459 Flags &= ~MixFlags::ImplicitConversion;
460 }
461
isValidclang::tidy::bugprone::model::MixData462 bool isValid() const { return Flags >= MixFlags::None; }
463
indicatesMixabilityclang::tidy::bugprone::model::MixData464 bool indicatesMixability() const { return Flags > MixFlags::None; }
465
466 /// Add the specified flag bits to the flags.
operator |clang::tidy::bugprone::model::MixData467 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.
operator |=clang::tidy::bugprone::model::MixData477 MixData &operator|=(MixFlags EnableFlags) {
478 Flags |= EnableFlags;
479 return *this;
480 }
481
withCommonTypeTransformedclang::tidy::bugprone::model::MixData482 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
Mixclang::tidy::bugprone::model::Mix504 Mix(const ParmVarDecl *F, const ParmVarDecl *S, MixData Data)
505 : First(F), Second(S), Data(std::move(Data)) {}
506
sanitizeclang::tidy::bugprone::model::Mix507 void sanitize() { Data.sanitize(); }
flagsclang::tidy::bugprone::model::Mix508 MixFlags flags() const { return Data.Flags; }
flagsValidclang::tidy::bugprone::model::Mix509 bool flagsValid() const { return Data.isValid(); }
mixableclang::tidy::bugprone::model::Mix510 bool mixable() const { return Data.indicatesMixability(); }
commonUnderlyingTypeclang::tidy::bugprone::model::Mix511 QualType commonUnderlyingType() const { return Data.CommonType; }
leftToRightConversionSequenceclang::tidy::bugprone::model::Mix512 const ConversionSequence &leftToRightConversionSequence() const {
513 return Data.Conversion;
514 }
rightToLeftConversionSequenceclang::tidy::bugprone::model::Mix515 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.
getFirstParamclang::tidy::bugprone::model::MixableParameterRange537 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.
getLastParamclang::tidy::bugprone::model::MixableParameterRange544 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
isUselessSugar(const Type * T)579 static inline bool isUselessSugar(const Type *T) {
580 return isa<AttributedType, DecayedType, ElaboratedType, ParenType>(T);
581 }
582
583 namespace {
584
585 struct NonCVRQualifiersResult {
586 /// True if the types are qualified in a way that even after equating or
587 /// removing local CVR qualification, even if the unqualified types
588 /// themselves would mix, the qualified ones don't, because there are some
589 /// other local qualifiers that are not equal.
590 bool HasMixabilityBreakingQualifiers;
591
592 /// The set of equal qualifiers between the two types.
593 Qualifiers CommonQualifiers;
594 };
595
596 } // namespace
597
598 /// Returns if the two types are qualified in a way that ever after equating or
599 /// removing local CVR qualification, even if the unqualified types would mix,
600 /// the qualified ones don't, because there are some other local qualifiers
601 /// that aren't equal.
602 static NonCVRQualifiersResult
getNonCVRQualifiers(const ASTContext & Ctx,QualType LType,QualType RType)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(LQual, 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
calculateMixability(const TheCheck & Check,QualType LType,QualType RType,const ASTContext & Ctx,ImplicitConversionModellingMode ImplicitMode)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(LType.getTypePtr())) {
659 LLVM_DEBUG(llvm::dbgs()
660 << "--- calculateMixability. LHS is useless sugar.\n");
661 return calculateMixability(Check, LType.getSingleStepDesugaredType(Ctx),
662 RType, Ctx, ImplicitMode);
663 }
664 if (isUselessSugar(RType.getTypePtr())) {
665 LLVM_DEBUG(llvm::dbgs()
666 << "--- calculateMixability. RHS is useless sugar.\n");
667 return calculateMixability(
668 Check, LType, RType.getSingleStepDesugaredType(Ctx), Ctx, ImplicitMode);
669 }
670
671 const auto *LLRef = LType->getAs<LValueReferenceType>();
672 const auto *RLRef = RType->getAs<LValueReferenceType>();
673 if (LLRef && RLRef) {
674 LLVM_DEBUG(llvm::dbgs() << "--- calculateMixability. LHS and RHS are &.\n");
675
676 return calculateMixability(Check, LLRef->getPointeeType(),
677 RLRef->getPointeeType(), Ctx, ImplicitMode)
678 .withCommonTypeTransformed(
679 [&Ctx](QualType QT) { return Ctx.getLValueReferenceType(QT); });
680 }
681 // At a particular call site, what could be passed to a 'T' or 'const T' might
682 // also be passed to a 'const T &' without the call site putting a direct
683 // side effect on the passed expressions.
684 if (LLRef) {
685 LLVM_DEBUG(llvm::dbgs() << "--- calculateMixability. LHS is &.\n");
686 return isLRefEquallyBindingToType(Check, LLRef, RType, Ctx, false,
687 ImplicitMode) |
688 MixFlags::ReferenceBind;
689 }
690 if (RLRef) {
691 LLVM_DEBUG(llvm::dbgs() << "--- calculateMixability. RHS is &.\n");
692 return isLRefEquallyBindingToType(Check, RLRef, LType, Ctx, true,
693 ImplicitMode) |
694 MixFlags::ReferenceBind;
695 }
696
697 if (LType->getAs<TypedefType>()) {
698 LLVM_DEBUG(llvm::dbgs() << "--- calculateMixability. LHS is typedef.\n");
699 return calculateMixability(Check, LType.getSingleStepDesugaredType(Ctx),
700 RType, Ctx, ImplicitMode) |
701 MixFlags::TypeAlias;
702 }
703 if (RType->getAs<TypedefType>()) {
704 LLVM_DEBUG(llvm::dbgs() << "--- calculateMixability. RHS is typedef.\n");
705 return calculateMixability(Check, LType,
706 RType.getSingleStepDesugaredType(Ctx), Ctx,
707 ImplicitMode) |
708 MixFlags::TypeAlias;
709 }
710
711 // A parameter of type 'cvr1 T' and another of potentially differently
712 // qualified 'cvr2 T' may bind with the same power, if the user so requested.
713 //
714 // Whether to do this check for the inner unqualified types.
715 bool CompareUnqualifiedTypes = false;
716 if (LType.getLocalCVRQualifiers() != RType.getLocalCVRQualifiers()) {
717 LLVM_DEBUG(if (LType.getLocalCVRQualifiers()) {
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.getLocalUnqualifiedType(),
766 RType.getLocalUnqualifiedType(), Ctx, ImplicitMode)
767 .withCommonTypeTransformed([&AdditionalQuals, &Ctx](QualType QT) {
768 // Once the mixability was deduced, apply the qualifiers common
769 // to the two type back onto the diagnostic printout.
770 return Ctx.getQualifiedType(QT, AdditionalQuals.CommonQualifiers);
771 });
772
773 if (!OriginallySameQualifiers)
774 // User-enabled qualifier change modelled for the mix.
775 return UnqualifiedMixability | MixFlags::Qualifiers;
776
777 // Apply the same qualifier back into the found common type if they were
778 // the same.
779 return UnqualifiedMixability.withCommonTypeTransformed(
780 [&Ctx, LType](QualType QT) {
781 return Ctx.getQualifiedType(QT, LType.getLocalQualifiers());
782 });
783 }
784
785 // Certain constructs match on the last catch-all getCanonicalType() equality,
786 // which is perhaps something not what we want. If this variable is true,
787 // the canonical type equality will be ignored.
788 bool RecursiveReturnDiscardingCanonicalType = false;
789
790 if (LType->isPointerType() && RType->isPointerType()) {
791 // If both types are pointers, and pointed to the exact same type,
792 // LType == RType took care of that. Try to see if the pointee type has
793 // some other match. However, this must not consider implicit conversions.
794 LLVM_DEBUG(llvm::dbgs()
795 << "--- calculateMixability. LHS and RHS are Ptrs.\n");
796 MixData MixOfPointee =
797 calculateMixability(Check, LType->getPointeeType(),
798 RType->getPointeeType(), Ctx,
799 ImplicitConversionModellingMode::None)
800 .withCommonTypeTransformed(
801 [&Ctx](QualType QT) { return Ctx.getPointerType(QT); });
802 if (hasFlag(MixOfPointee.Flags,
803 MixFlags::WorkaroundDisableCanonicalEquivalence))
804 RecursiveReturnDiscardingCanonicalType = true;
805
806 MixOfPointee.sanitize();
807 if (MixOfPointee.indicatesMixability()) {
808 LLVM_DEBUG(llvm::dbgs()
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, 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
isLRefEquallyBindingToType(const TheCheck & Check,const LValueReferenceType * LRef,QualType Ty,const ASTContext & Ctx,bool IsRefRHS,ImplicitConversionModellingMode ImplicitMode)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(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, Ty, NonConstReferredType, Ctx,
943 ImplicitMode)
944 : calculateMixability(Check, NonConstReferredType, Ty, Ctx,
945 ImplicitMode);
946 }
947
isDerivedToBase(const CXXRecordDecl * Derived,const CXXRecordDecl * Base)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>
approximateStandardConversionSequence(const TheCheck & Check,QualType From,QualType To,const ASTContext & Ctx)955 approximateStandardConversionSequence(const TheCheck &Check, QualType From,
956 QualType To, const ASTContext &Ctx) {
957 LLVM_DEBUG(llvm::dbgs() << ">>> approximateStdConv for LType:\n";
958 From.dump(llvm::dbgs(), Ctx); llvm::dbgs() << "\nand RType:\n";
959 To.dump(llvm::dbgs(), Ctx); llvm::dbgs() << '\n';);
960
961 // A standard conversion sequence consists of the following, in order:
962 // * Maybe either LValue->RValue conv., Array->Ptr conv., Function->Ptr conv.
963 // * Maybe Numeric promotion or conversion.
964 // * Maybe function pointer conversion.
965 // * Maybe qualifier adjustments.
966 QualType WorkType = From;
967 // Get out the qualifiers of the original type. This will always be
968 // re-applied to the WorkType to ensure it is the same qualification as the
969 // original From was.
970 auto FastQualifiersToApply = static_cast<unsigned>(
971 From.split().Quals.getAsOpaqueValue() & Qualifiers::FastMask);
972
973 // LValue->RValue is irrelevant for the check, because it is a thing to be
974 // done at a call site, and will be performed if need be performed.
975
976 // Array->Pointer decay is handled by the main method in desugaring
977 // the parameter's DecayedType as "useless sugar".
978
979 // Function->Pointer conversions are also irrelevant, because a
980 // "FunctionType" cannot be the type of a parameter variable, so this
981 // conversion is only meaningful at call sites.
982
983 // Numeric promotions and conversions.
984 const auto *FromBuiltin = WorkType->getAs<BuiltinType>();
985 const auto *ToBuiltin = To->getAs<BuiltinType>();
986 bool FromNumeric = FromBuiltin && (FromBuiltin->isIntegerType() ||
987 FromBuiltin->isFloatingType());
988 bool ToNumeric =
989 ToBuiltin && (ToBuiltin->isIntegerType() || ToBuiltin->isFloatingType());
990 if (FromNumeric && ToNumeric) {
991 // If both are integral types, the numeric conversion is performed.
992 // Reapply the qualifiers of the original type, however, so
993 // "const int -> double" in this case moves over to
994 // "const double -> double".
995 LLVM_DEBUG(llvm::dbgs()
996 << "--- approximateStdConv. Conversion between numerics.\n");
997 WorkType = QualType{ToBuiltin, FastQualifiersToApply};
998 }
999
1000 const auto *FromEnum = WorkType->getAs<EnumType>();
1001 const auto *ToEnum = To->getAs<EnumType>();
1002 if (FromEnum && ToNumeric && FromEnum->isUnscopedEnumerationType()) {
1003 // Unscoped enumerations (or enumerations in C) convert to numerics.
1004 LLVM_DEBUG(llvm::dbgs()
1005 << "--- approximateStdConv. Unscoped enum to numeric.\n");
1006 WorkType = QualType{ToBuiltin, FastQualifiersToApply};
1007 } else if (FromNumeric && ToEnum && ToEnum->isUnscopedEnumerationType()) {
1008 // Numeric types convert to enumerations only in C.
1009 if (Ctx.getLangOpts().CPlusPlus) {
1010 LLVM_DEBUG(llvm::dbgs() << "<<< approximateStdConv. Numeric to unscoped "
1011 "enum, not possible in C++!\n");
1012 return {};
1013 }
1014
1015 LLVM_DEBUG(llvm::dbgs()
1016 << "--- approximateStdConv. Numeric to unscoped enum.\n");
1017 WorkType = QualType{ToEnum, FastQualifiersToApply};
1018 }
1019
1020 // Check for pointer conversions.
1021 const auto *FromPtr = WorkType->getAs<PointerType>();
1022 const auto *ToPtr = To->getAs<PointerType>();
1023 if (FromPtr && ToPtr) {
1024 if (ToPtr->isVoidPointerType()) {
1025 LLVM_DEBUG(llvm::dbgs() << "--- approximateStdConv. To void pointer.\n");
1026 WorkType = QualType{ToPtr, FastQualifiersToApply};
1027 }
1028
1029 const auto *FromRecordPtr = FromPtr->getPointeeCXXRecordDecl();
1030 const auto *ToRecordPtr = ToPtr->getPointeeCXXRecordDecl();
1031 if (isDerivedToBase(FromRecordPtr, ToRecordPtr)) {
1032 LLVM_DEBUG(llvm::dbgs() << "--- approximateStdConv. Derived* to Base*\n");
1033 WorkType = QualType{ToPtr, FastQualifiersToApply};
1034 }
1035 }
1036
1037 // Model the slicing Derived-to-Base too, as "BaseT temporary = derived;"
1038 // can also be compiled.
1039 const auto *FromRecord = WorkType->getAsCXXRecordDecl();
1040 const auto *ToRecord = To->getAsCXXRecordDecl();
1041 if (isDerivedToBase(FromRecord, ToRecord)) {
1042 LLVM_DEBUG(llvm::dbgs() << "--- approximateStdConv. Derived To Base.\n");
1043 WorkType = QualType{ToRecord->getTypeForDecl(), FastQualifiersToApply};
1044 }
1045
1046 if (Ctx.getLangOpts().CPlusPlus17 && FromPtr && ToPtr) {
1047 // Function pointer conversion: A noexcept function pointer can be passed
1048 // to a non-noexcept one.
1049 const auto *FromFunctionPtr =
1050 FromPtr->getPointeeType()->getAs<FunctionProtoType>();
1051 const auto *ToFunctionPtr =
1052 ToPtr->getPointeeType()->getAs<FunctionProtoType>();
1053 if (FromFunctionPtr && ToFunctionPtr &&
1054 FromFunctionPtr->hasNoexceptExceptionSpec() &&
1055 !ToFunctionPtr->hasNoexceptExceptionSpec()) {
1056 LLVM_DEBUG(llvm::dbgs() << "--- approximateStdConv. noexcept function "
1057 "pointer to non-noexcept.\n");
1058 WorkType = QualType{ToPtr, FastQualifiersToApply};
1059 }
1060 }
1061
1062 // Qualifier adjustments are modelled according to the user's request in
1063 // the QualifiersMix check config.
1064 LLVM_DEBUG(llvm::dbgs()
1065 << "--- approximateStdConv. Trying qualifier adjustment...\n");
1066 MixData QualConv = calculateMixability(Check, WorkType, To, Ctx,
1067 ImplicitConversionModellingMode::None);
1068 QualConv.sanitize();
1069 if (hasFlag(QualConv.Flags, MixFlags::Qualifiers)) {
1070 LLVM_DEBUG(llvm::dbgs()
1071 << "<<< approximateStdConv. Qualifiers adjusted.\n");
1072 WorkType = To;
1073 }
1074
1075 if (WorkType == To) {
1076 LLVM_DEBUG(llvm::dbgs() << "<<< approximateStdConv. Reached 'To' type.\n");
1077 return {WorkType};
1078 }
1079
1080 LLVM_DEBUG(llvm::dbgs() << "<<< approximateStdConv. Did not reach 'To'.\n");
1081 return {};
1082 }
1083
1084 namespace {
1085
1086 /// Helper class for storing possible user-defined conversion calls that
1087 /// *could* take place in an implicit conversion, and selecting the one that
1088 /// most likely *does*, if any.
1089 class UserDefinedConversionSelector {
1090 public:
1091 /// The conversion associated with a conversion function, together with the
1092 /// mixability flags of the conversion function's parameter or return type
1093 /// to the rest of the sequence the selector is used in, and the sequence
1094 /// that applied through the conversion itself.
1095 struct PreparedConversion {
1096 const CXXMethodDecl *ConversionFun;
1097 MixFlags Flags;
1098 ConversionSequence Seq;
1099
PreparedConversionclang::tidy::bugprone::model::__anoneac3b3da0811::UserDefinedConversionSelector::PreparedConversion1100 PreparedConversion(const CXXMethodDecl *CMD, MixFlags F,
1101 ConversionSequence S)
1102 : ConversionFun(CMD), Flags(F), Seq(S) {}
1103 };
1104
UserDefinedConversionSelector(const TheCheck & Check)1105 UserDefinedConversionSelector(const TheCheck &Check) : Check(Check) {}
1106
1107 /// Adds the conversion between the two types for the given function into
1108 /// the possible implicit conversion set. FromType and ToType is either:
1109 /// * the result of a standard sequence and a converting ctor parameter
1110 /// * the return type of a conversion operator and the expected target of
1111 /// an implicit conversion.
addConversion(const CXXMethodDecl * ConvFun,QualType FromType,QualType ToType)1112 void addConversion(const CXXMethodDecl *ConvFun, QualType FromType,
1113 QualType ToType) {
1114 // Try to go from the FromType to the ToType with only a single implicit
1115 // conversion, to see if the conversion function is applicable.
1116 MixData Mix = calculateMixability(
1117 Check, FromType, ToType, ConvFun->getASTContext(),
1118 ImplicitConversionModellingMode::OneWaySingleStandardOnly);
1119 Mix.sanitize();
1120 if (!Mix.indicatesMixability())
1121 return;
1122
1123 LLVM_DEBUG(llvm::dbgs() << "--- tryConversion. Found viable with flags: "
1124 << formatMixFlags(Mix.Flags) << '\n');
1125 FlaggedConversions.emplace_back(ConvFun, Mix.Flags, Mix.Conversion);
1126 }
1127
1128 /// Selects the best conversion function that is applicable from the
1129 /// prepared set of potential conversion functions taken.
operator ()() const1130 std::optional<PreparedConversion> operator()() const {
1131 if (FlaggedConversions.empty()) {
1132 LLVM_DEBUG(llvm::dbgs() << "--- selectUserDefinedConv. Empty.\n");
1133 return {};
1134 }
1135 if (FlaggedConversions.size() == 1) {
1136 LLVM_DEBUG(llvm::dbgs() << "--- selectUserDefinedConv. Single.\n");
1137 return FlaggedConversions.front();
1138 }
1139
1140 std::optional<PreparedConversion> BestConversion;
1141 unsigned short HowManyGoodConversions = 0;
1142 for (const auto &Prepared : FlaggedConversions) {
1143 LLVM_DEBUG(llvm::dbgs() << "--- selectUserDefinedConv. Candidate flags: "
1144 << formatMixFlags(Prepared.Flags) << '\n');
1145 if (!BestConversion) {
1146 BestConversion = Prepared;
1147 ++HowManyGoodConversions;
1148 continue;
1149 }
1150
1151 bool BestConversionHasImplicit =
1152 hasFlag(BestConversion->Flags, MixFlags::ImplicitConversion);
1153 bool ThisConversionHasImplicit =
1154 hasFlag(Prepared.Flags, MixFlags::ImplicitConversion);
1155 if (!BestConversionHasImplicit && ThisConversionHasImplicit)
1156 // This is a worse conversion, because a better one was found earlier.
1157 continue;
1158
1159 if (BestConversionHasImplicit && !ThisConversionHasImplicit) {
1160 // If the so far best selected conversion needs a previous implicit
1161 // conversion to match the user-defined converting function, but this
1162 // conversion does not, this is a better conversion, and we can throw
1163 // away the previously selected conversion(s).
1164 BestConversion = Prepared;
1165 HowManyGoodConversions = 1;
1166 continue;
1167 }
1168
1169 if (BestConversionHasImplicit == ThisConversionHasImplicit)
1170 // The current conversion is the same in term of goodness than the
1171 // already selected one.
1172 ++HowManyGoodConversions;
1173 }
1174
1175 if (HowManyGoodConversions == 1) {
1176 LLVM_DEBUG(llvm::dbgs()
1177 << "--- selectUserDefinedConv. Unique result. Flags: "
1178 << formatMixFlags(BestConversion->Flags) << '\n');
1179 return BestConversion;
1180 }
1181
1182 LLVM_DEBUG(llvm::dbgs()
1183 << "--- selectUserDefinedConv. No, or ambiguous.\n");
1184 return {};
1185 }
1186
1187 private:
1188 llvm::SmallVector<PreparedConversion, 2> FlaggedConversions;
1189 const TheCheck &Check;
1190 };
1191
1192 } // namespace
1193
1194 static std::optional<ConversionSequence>
tryConversionOperators(const TheCheck & Check,const CXXRecordDecl * RD,QualType ToType)1195 tryConversionOperators(const TheCheck &Check, const CXXRecordDecl *RD,
1196 QualType ToType) {
1197 if (!RD || !RD->isCompleteDefinition())
1198 return {};
1199 RD = RD->getDefinition();
1200
1201 LLVM_DEBUG(llvm::dbgs() << ">>> tryConversionOperators: " << RD->getName()
1202 << " to:\n";
1203 ToType.dump(llvm::dbgs(), RD->getASTContext());
1204 llvm::dbgs() << '\n';);
1205
1206 UserDefinedConversionSelector ConversionSet{Check};
1207
1208 for (const NamedDecl *Method : RD->getVisibleConversionFunctions()) {
1209 const auto *Con = dyn_cast<CXXConversionDecl>(Method);
1210 if (!Con || Con->isExplicit())
1211 continue;
1212 LLVM_DEBUG(llvm::dbgs() << "--- tryConversionOperators. Trying:\n";
1213 Con->dump(llvm::dbgs()); llvm::dbgs() << '\n';);
1214
1215 // Try to go from the result of conversion operator to the expected type,
1216 // without calculating another user-defined conversion.
1217 ConversionSet.addConversion(Con, Con->getConversionType(), ToType);
1218 }
1219
1220 if (std::optional<UserDefinedConversionSelector::PreparedConversion>
1221 SelectedConversion = ConversionSet()) {
1222 QualType RecordType{RD->getTypeForDecl(), 0};
1223
1224 ConversionSequence Result{RecordType, ToType};
1225 // The conversion from the operator call's return type to ToType was
1226 // modelled as a "pre-conversion" in the operator call, but it is the
1227 // "post-conversion" from the point of view of the original conversion
1228 // we are modelling.
1229 Result.AfterSecondStandard = SelectedConversion->Seq.AfterFirstStandard;
1230
1231 ConversionSequence::UserDefinedConversionOperator ConvOp;
1232 ConvOp.Fun = cast<CXXConversionDecl>(SelectedConversion->ConversionFun);
1233 ConvOp.UserDefinedType = RecordType;
1234 ConvOp.ConversionOperatorResultType = ConvOp.Fun->getConversionType();
1235 Result.setConversion(ConvOp);
1236
1237 LLVM_DEBUG(llvm::dbgs() << "<<< tryConversionOperators. Found result.\n");
1238 return Result;
1239 }
1240
1241 LLVM_DEBUG(llvm::dbgs() << "<<< tryConversionOperators. No conversion.\n");
1242 return {};
1243 }
1244
1245 static std::optional<ConversionSequence>
tryConvertingConstructors(const TheCheck & Check,QualType FromType,const CXXRecordDecl * RD)1246 tryConvertingConstructors(const TheCheck &Check, QualType FromType,
1247 const CXXRecordDecl *RD) {
1248 if (!RD || !RD->isCompleteDefinition())
1249 return {};
1250 RD = RD->getDefinition();
1251
1252 LLVM_DEBUG(llvm::dbgs() << ">>> tryConveringConstructors: " << RD->getName()
1253 << " from:\n";
1254 FromType.dump(llvm::dbgs(), RD->getASTContext());
1255 llvm::dbgs() << '\n';);
1256
1257 UserDefinedConversionSelector ConversionSet{Check};
1258
1259 for (const CXXConstructorDecl *Con : RD->ctors()) {
1260 if (Con->isCopyOrMoveConstructor() ||
1261 !Con->isConvertingConstructor(/* AllowExplicit =*/false))
1262 continue;
1263 LLVM_DEBUG(llvm::dbgs() << "--- tryConvertingConstructors. Trying:\n";
1264 Con->dump(llvm::dbgs()); llvm::dbgs() << '\n';);
1265
1266 // Try to go from the original FromType to the converting constructor's
1267 // parameter type without another user-defined conversion.
1268 ConversionSet.addConversion(Con, FromType, Con->getParamDecl(0)->getType());
1269 }
1270
1271 if (std::optional<UserDefinedConversionSelector::PreparedConversion>
1272 SelectedConversion = ConversionSet()) {
1273 QualType RecordType{RD->getTypeForDecl(), 0};
1274
1275 ConversionSequence Result{FromType, RecordType};
1276 Result.AfterFirstStandard = SelectedConversion->Seq.AfterFirstStandard;
1277
1278 ConversionSequence::UserDefinedConvertingConstructor Ctor;
1279 Ctor.Fun = cast<CXXConstructorDecl>(SelectedConversion->ConversionFun);
1280 Ctor.ConstructorParameterType = Ctor.Fun->getParamDecl(0)->getType();
1281 Ctor.UserDefinedType = RecordType;
1282 Result.setConversion(Ctor);
1283
1284 LLVM_DEBUG(llvm::dbgs()
1285 << "<<< tryConvertingConstructors. Found result.\n");
1286 return Result;
1287 }
1288
1289 LLVM_DEBUG(llvm::dbgs() << "<<< tryConvertingConstructors. No conversion.\n");
1290 return {};
1291 }
1292
1293 /// Returns whether an expression of LType can be used in an RType context, as
1294 /// per the implicit conversion rules.
1295 ///
1296 /// Note: the result of this operation, unlike that of calculateMixability, is
1297 /// **NOT** symmetric.
1298 static MixData
approximateImplicitConversion(const TheCheck & Check,QualType LType,QualType RType,const ASTContext & Ctx,ImplicitConversionModellingMode ImplicitMode)1299 approximateImplicitConversion(const TheCheck &Check, QualType LType,
1300 QualType RType, const ASTContext &Ctx,
1301 ImplicitConversionModellingMode ImplicitMode) {
1302 LLVM_DEBUG(llvm::dbgs() << ">>> approximateImplicitConversion for LType:\n";
1303 LType.dump(llvm::dbgs(), Ctx); llvm::dbgs() << "\nand RType:\n";
1304 RType.dump(llvm::dbgs(), Ctx);
1305 llvm::dbgs() << "\nimplicit mode: "; switch (ImplicitMode) {
1306 case ImplicitConversionModellingMode::None:
1307 llvm::dbgs() << "None";
1308 break;
1309 case ImplicitConversionModellingMode::All:
1310 llvm::dbgs() << "All";
1311 break;
1312 case ImplicitConversionModellingMode::OneWaySingleStandardOnly:
1313 llvm::dbgs() << "OneWay, Single, STD Only";
1314 break;
1315 } llvm::dbgs() << '\n';);
1316 if (LType == RType)
1317 return {MixFlags::Trivial, LType};
1318
1319 // An implicit conversion sequence consists of the following, in order:
1320 // * Maybe standard conversion sequence.
1321 // * Maybe user-defined conversion.
1322 // * Maybe standard conversion sequence.
1323 ConversionSequence ImplicitSeq{LType, RType};
1324 QualType WorkType = LType;
1325
1326 std::optional<QualType> AfterFirstStdConv =
1327 approximateStandardConversionSequence(Check, LType, RType, Ctx);
1328 if (AfterFirstStdConv) {
1329 LLVM_DEBUG(llvm::dbgs() << "--- approximateImplicitConversion. Standard "
1330 "Pre-Conversion found!\n");
1331 ImplicitSeq.AfterFirstStandard = *AfterFirstStdConv;
1332 WorkType = ImplicitSeq.AfterFirstStandard;
1333 }
1334
1335 if (ImplicitMode == ImplicitConversionModellingMode::OneWaySingleStandardOnly)
1336 // If the caller only requested modelling of a standard conversion, bail.
1337 return {ImplicitSeq.AfterFirstStandard.isNull()
1338 ? MixFlags::None
1339 : MixFlags::ImplicitConversion,
1340 ImplicitSeq};
1341
1342 if (Ctx.getLangOpts().CPlusPlus) {
1343 bool FoundConversionOperator = false, FoundConvertingCtor = false;
1344
1345 if (const auto *LRD = WorkType->getAsCXXRecordDecl()) {
1346 std::optional<ConversionSequence> ConversionOperatorResult =
1347 tryConversionOperators(Check, LRD, RType);
1348 if (ConversionOperatorResult) {
1349 LLVM_DEBUG(llvm::dbgs() << "--- approximateImplicitConversion. Found "
1350 "conversion operator.\n");
1351 ImplicitSeq.update(*ConversionOperatorResult);
1352 WorkType = ImplicitSeq.getTypeAfterUserDefinedConversion();
1353 FoundConversionOperator = true;
1354 }
1355 }
1356
1357 if (const auto *RRD = RType->getAsCXXRecordDecl()) {
1358 // Use the original "LType" here, and not WorkType, because the
1359 // conversion to the converting constructors' parameters will be
1360 // modelled in the recursive call.
1361 std::optional<ConversionSequence> ConvCtorResult =
1362 tryConvertingConstructors(Check, LType, RRD);
1363 if (ConvCtorResult) {
1364 LLVM_DEBUG(llvm::dbgs() << "--- approximateImplicitConversion. Found "
1365 "converting constructor.\n");
1366 ImplicitSeq.update(*ConvCtorResult);
1367 WorkType = ImplicitSeq.getTypeAfterUserDefinedConversion();
1368 FoundConvertingCtor = true;
1369 }
1370 }
1371
1372 if (FoundConversionOperator && FoundConvertingCtor) {
1373 // If both an operator and a ctor matches, the sequence is ambiguous.
1374 LLVM_DEBUG(llvm::dbgs()
1375 << "<<< approximateImplicitConversion. Found both "
1376 "user-defined conversion kinds in the same sequence!\n");
1377 return {MixFlags::None};
1378 }
1379 }
1380
1381 // After the potential user-defined conversion, another standard conversion
1382 // sequence might exist.
1383 LLVM_DEBUG(
1384 llvm::dbgs()
1385 << "--- approximateImplicitConversion. Try to find post-conversion.\n");
1386 MixData SecondStdConv = approximateImplicitConversion(
1387 Check, WorkType, RType, Ctx,
1388 ImplicitConversionModellingMode::OneWaySingleStandardOnly);
1389 if (SecondStdConv.indicatesMixability()) {
1390 LLVM_DEBUG(llvm::dbgs() << "--- approximateImplicitConversion. Standard "
1391 "Post-Conversion found!\n");
1392
1393 // The single-step modelling puts the modelled conversion into the "PreStd"
1394 // variable in the recursive call, but from the PoV of this function, it is
1395 // the post-conversion.
1396 ImplicitSeq.AfterSecondStandard =
1397 SecondStdConv.Conversion.AfterFirstStandard;
1398 WorkType = ImplicitSeq.AfterSecondStandard;
1399 }
1400
1401 if (ImplicitSeq) {
1402 LLVM_DEBUG(llvm::dbgs()
1403 << "<<< approximateImplicitConversion. Found a conversion.\n");
1404 return {MixFlags::ImplicitConversion, ImplicitSeq};
1405 }
1406
1407 LLVM_DEBUG(
1408 llvm::dbgs() << "<<< approximateImplicitConversion. No match found.\n");
1409 return {MixFlags::None};
1410 }
1411
modelMixingRange(const TheCheck & Check,const FunctionDecl * FD,std::size_t StartIndex,const filter::SimilarlyUsedParameterPairSuppressor & UsageBasedSuppressor)1412 static MixableParameterRange modelMixingRange(
1413 const TheCheck &Check, const FunctionDecl *FD, std::size_t StartIndex,
1414 const filter::SimilarlyUsedParameterPairSuppressor &UsageBasedSuppressor) {
1415 std::size_t NumParams = FD->getNumParams();
1416 assert(StartIndex < NumParams && "out of bounds for start");
1417 const ASTContext &Ctx = FD->getASTContext();
1418
1419 MixableParameterRange Ret;
1420 // A parameter at index 'StartIndex' had been trivially "checked".
1421 Ret.NumParamsChecked = 1;
1422
1423 for (std::size_t I = StartIndex + 1; I < NumParams; ++I) {
1424 const ParmVarDecl *Ith = FD->getParamDecl(I);
1425 StringRef ParamName = Ith->getName();
1426 LLVM_DEBUG(llvm::dbgs()
1427 << "Check param #" << I << " '" << ParamName << "'...\n");
1428 if (filter::isIgnoredParameter(Check, Ith)) {
1429 LLVM_DEBUG(llvm::dbgs() << "Param #" << I << " is ignored. Break!\n");
1430 break;
1431 }
1432
1433 StringRef PrevParamName = FD->getParamDecl(I - 1)->getName();
1434 if (!ParamName.empty() && !PrevParamName.empty() &&
1435 filter::prefixSuffixCoverUnderThreshold(
1436 Check.NamePrefixSuffixSilenceDissimilarityTreshold, PrevParamName,
1437 ParamName)) {
1438 LLVM_DEBUG(llvm::dbgs() << "Parameter '" << ParamName
1439 << "' follows a pattern with previous parameter '"
1440 << PrevParamName << "'. Break!\n");
1441 break;
1442 }
1443
1444 // Now try to go forward and build the range of [Start, ..., I, I + 1, ...]
1445 // parameters that can be messed up at a call site.
1446 MixableParameterRange::MixVector MixesOfIth;
1447 for (std::size_t J = StartIndex; J < I; ++J) {
1448 const ParmVarDecl *Jth = FD->getParamDecl(J);
1449 LLVM_DEBUG(llvm::dbgs()
1450 << "Check mix of #" << J << " against #" << I << "...\n");
1451
1452 if (isSimilarlyUsedParameter(UsageBasedSuppressor, Ith, Jth)) {
1453 // Consider the two similarly used parameters to not be possible in a
1454 // mix-up at the user's request, if they enabled this heuristic.
1455 LLVM_DEBUG(llvm::dbgs() << "Parameters #" << I << " and #" << J
1456 << " deemed related, ignoring...\n");
1457
1458 // If the parameter #I and #J mixes, then I is mixable with something
1459 // in the current range, so the range has to be broken and I not
1460 // included.
1461 MixesOfIth.clear();
1462 break;
1463 }
1464
1465 Mix M{Jth, Ith,
1466 calculateMixability(Check, Jth->getType(), Ith->getType(), Ctx,
1467 Check.ModelImplicitConversions
1468 ? ImplicitConversionModellingMode::All
1469 : ImplicitConversionModellingMode::None)};
1470 LLVM_DEBUG(llvm::dbgs() << "Mix flags (raw) : "
1471 << formatMixFlags(M.flags()) << '\n');
1472 M.sanitize();
1473 LLVM_DEBUG(llvm::dbgs() << "Mix flags (after sanitize): "
1474 << formatMixFlags(M.flags()) << '\n');
1475
1476 assert(M.flagsValid() && "All flags decayed!");
1477
1478 if (M.mixable())
1479 MixesOfIth.emplace_back(std::move(M));
1480 }
1481
1482 if (MixesOfIth.empty()) {
1483 // If there weren't any new mixes stored for Ith, the range is
1484 // [Start, ..., I].
1485 LLVM_DEBUG(llvm::dbgs()
1486 << "Param #" << I
1487 << " does not mix with any in the current range. Break!\n");
1488 break;
1489 }
1490
1491 Ret.Mixes.insert(Ret.Mixes.end(), MixesOfIth.begin(), MixesOfIth.end());
1492 ++Ret.NumParamsChecked; // Otherwise a new param was iterated.
1493 }
1494
1495 return Ret;
1496 }
1497
1498 } // namespace model
1499
1500 /// Matches DeclRefExprs and their ignorable wrappers to ParmVarDecls.
AST_MATCHER_FUNCTION(ast_matchers::internal::Matcher<Stmt>,paramRefExpr)1501 AST_MATCHER_FUNCTION(ast_matchers::internal::Matcher<Stmt>, paramRefExpr) {
1502 return expr(ignoringParenImpCasts(ignoringElidableConstructorCall(
1503 declRefExpr(to(parmVarDecl().bind("param"))))));
1504 }
1505
1506 namespace filter {
1507
1508 /// Returns whether the parameter's name or the parameter's type's name is
1509 /// configured by the user to be ignored from analysis and diagnostic.
isIgnoredParameter(const TheCheck & Check,const ParmVarDecl * Node)1510 static bool isIgnoredParameter(const TheCheck &Check, const ParmVarDecl *Node) {
1511 LLVM_DEBUG(llvm::dbgs() << "Checking if '" << Node->getName()
1512 << "' is ignored.\n");
1513
1514 if (!Node->getIdentifier())
1515 return llvm::is_contained(Check.IgnoredParameterNames, "\"\"");
1516
1517 StringRef NodeName = Node->getName();
1518 if (llvm::is_contained(Check.IgnoredParameterNames, NodeName)) {
1519 LLVM_DEBUG(llvm::dbgs() << "\tName ignored.\n");
1520 return true;
1521 }
1522
1523 StringRef NodeTypeName = [Node] {
1524 const ASTContext &Ctx = Node->getASTContext();
1525 const SourceManager &SM = Ctx.getSourceManager();
1526 SourceLocation B = Node->getTypeSpecStartLoc();
1527 SourceLocation E = Node->getTypeSpecEndLoc();
1528 LangOptions LO;
1529
1530 LLVM_DEBUG(llvm::dbgs() << "\tType name code is '"
1531 << Lexer::getSourceText(
1532 CharSourceRange::getTokenRange(B, E), SM, LO)
1533 << "'...\n");
1534 if (B.isMacroID()) {
1535 LLVM_DEBUG(llvm::dbgs() << "\t\tBeginning is macro.\n");
1536 B = SM.getTopMacroCallerLoc(B);
1537 }
1538 if (E.isMacroID()) {
1539 LLVM_DEBUG(llvm::dbgs() << "\t\tEnding is macro.\n");
1540 E = Lexer::getLocForEndOfToken(SM.getTopMacroCallerLoc(E), 0, SM, LO);
1541 }
1542 LLVM_DEBUG(llvm::dbgs() << "\tType name code is '"
1543 << Lexer::getSourceText(
1544 CharSourceRange::getTokenRange(B, E), SM, LO)
1545 << "'...\n");
1546
1547 return Lexer::getSourceText(CharSourceRange::getTokenRange(B, E), SM, LO);
1548 }();
1549
1550 LLVM_DEBUG(llvm::dbgs() << "\tType name is '" << NodeTypeName << "'\n");
1551 if (!NodeTypeName.empty()) {
1552 if (llvm::any_of(Check.IgnoredParameterTypeSuffixes,
1553 [NodeTypeName](StringRef E) {
1554 return !E.empty() && NodeTypeName.ends_with(E);
1555 })) {
1556 LLVM_DEBUG(llvm::dbgs() << "\tType suffix ignored.\n");
1557 return true;
1558 }
1559 }
1560
1561 return false;
1562 }
1563
1564 /// This namespace contains the implementations for the suppression of
1565 /// diagnostics from similarly-used ("related") parameters.
1566 namespace relatedness_heuristic {
1567
1568 static constexpr std::size_t SmallDataStructureSize = 4;
1569
1570 template <typename T, std::size_t N = SmallDataStructureSize>
1571 using ParamToSmallSetMap =
1572 llvm::DenseMap<const ParmVarDecl *, llvm::SmallSet<T, N>>;
1573
1574 /// Returns whether the sets mapped to the two elements in the map have at
1575 /// least one element in common.
1576 template <typename MapTy, typename ElemTy>
lazyMapOfSetsIntersectionExists(const MapTy & Map,const ElemTy & E1,const ElemTy & E2)1577 bool lazyMapOfSetsIntersectionExists(const MapTy &Map, const ElemTy &E1,
1578 const ElemTy &E2) {
1579 auto E1Iterator = Map.find(E1);
1580 auto E2Iterator = Map.find(E2);
1581 if (E1Iterator == Map.end() || E2Iterator == Map.end())
1582 return false;
1583
1584 for (const auto &E1SetElem : E1Iterator->second)
1585 if (E2Iterator->second.contains(E1SetElem))
1586 return true;
1587
1588 return false;
1589 }
1590
1591 /// Implements the heuristic that marks two parameters related if there is
1592 /// a usage for both in the same strict expression subtree. A strict
1593 /// expression subtree is a tree which only includes Expr nodes, i.e. no
1594 /// Stmts and no Decls.
1595 class AppearsInSameExpr : public RecursiveASTVisitor<AppearsInSameExpr> {
1596 using Base = RecursiveASTVisitor<AppearsInSameExpr>;
1597
1598 const FunctionDecl *FD;
1599 const Expr *CurrentExprOnlyTreeRoot = nullptr;
1600 llvm::DenseMap<const ParmVarDecl *,
1601 llvm::SmallPtrSet<const Expr *, SmallDataStructureSize>>
1602 ParentExprsForParamRefs;
1603
1604 public:
setup(const FunctionDecl * FD)1605 void setup(const FunctionDecl *FD) {
1606 this->FD = FD;
1607 TraverseFunctionDecl(const_cast<FunctionDecl *>(FD));
1608 }
1609
operator ()(const ParmVarDecl * Param1,const ParmVarDecl * Param2) const1610 bool operator()(const ParmVarDecl *Param1, const ParmVarDecl *Param2) const {
1611 return lazyMapOfSetsIntersectionExists(ParentExprsForParamRefs, Param1,
1612 Param2);
1613 }
1614
TraverseDecl(Decl * D)1615 bool TraverseDecl(Decl *D) {
1616 CurrentExprOnlyTreeRoot = nullptr;
1617 return Base::TraverseDecl(D);
1618 }
1619
TraverseStmt(Stmt * S,DataRecursionQueue * Queue=nullptr)1620 bool TraverseStmt(Stmt *S, DataRecursionQueue *Queue = nullptr) {
1621 if (auto *E = dyn_cast_or_null<Expr>(S)) {
1622 bool RootSetInCurrentStackFrame = false;
1623 if (!CurrentExprOnlyTreeRoot) {
1624 CurrentExprOnlyTreeRoot = E;
1625 RootSetInCurrentStackFrame = true;
1626 }
1627
1628 bool Ret = Base::TraverseStmt(S);
1629
1630 if (RootSetInCurrentStackFrame)
1631 CurrentExprOnlyTreeRoot = nullptr;
1632
1633 return Ret;
1634 }
1635
1636 // A Stmt breaks the strictly Expr subtree.
1637 CurrentExprOnlyTreeRoot = nullptr;
1638 return Base::TraverseStmt(S);
1639 }
1640
VisitDeclRefExpr(DeclRefExpr * DRE)1641 bool VisitDeclRefExpr(DeclRefExpr *DRE) {
1642 if (!CurrentExprOnlyTreeRoot)
1643 return true;
1644
1645 if (auto *PVD = dyn_cast<ParmVarDecl>(DRE->getDecl()))
1646 if (llvm::find(FD->parameters(), PVD))
1647 ParentExprsForParamRefs[PVD].insert(CurrentExprOnlyTreeRoot);
1648
1649 return true;
1650 }
1651 };
1652
1653 /// Implements the heuristic that marks two parameters related if there are
1654 /// two separate calls to the same function (overload) and the parameters are
1655 /// passed to the same index in both calls, i.e f(a, b) and f(a, c) passes
1656 /// b and c to the same index (2) of f(), marking them related.
1657 class PassedToSameFunction {
1658 ParamToSmallSetMap<std::pair<const FunctionDecl *, unsigned>> TargetParams;
1659
1660 public:
setup(const FunctionDecl * FD)1661 void setup(const FunctionDecl *FD) {
1662 auto ParamsAsArgsInFnCalls =
1663 match(functionDecl(forEachDescendant(
1664 callExpr(forEachArgumentWithParam(
1665 paramRefExpr(), parmVarDecl().bind("passed-to")))
1666 .bind("call-expr"))),
1667 *FD, FD->getASTContext());
1668 for (const auto &Match : ParamsAsArgsInFnCalls) {
1669 const auto *PassedParamOfThisFn = Match.getNodeAs<ParmVarDecl>("param");
1670 const auto *CE = Match.getNodeAs<CallExpr>("call-expr");
1671 const auto *PassedToParam = Match.getNodeAs<ParmVarDecl>("passed-to");
1672 assert(PassedParamOfThisFn && CE && PassedToParam);
1673
1674 const FunctionDecl *CalledFn = CE->getDirectCallee();
1675 if (!CalledFn)
1676 continue;
1677
1678 std::optional<unsigned> TargetIdx;
1679 unsigned NumFnParams = CalledFn->getNumParams();
1680 for (unsigned Idx = 0; Idx < NumFnParams; ++Idx)
1681 if (CalledFn->getParamDecl(Idx) == PassedToParam)
1682 TargetIdx.emplace(Idx);
1683
1684 assert(TargetIdx && "Matched, but didn't find index?");
1685 TargetParams[PassedParamOfThisFn].insert(
1686 {CalledFn->getCanonicalDecl(), *TargetIdx});
1687 }
1688 }
1689
operator ()(const ParmVarDecl * Param1,const ParmVarDecl * Param2) const1690 bool operator()(const ParmVarDecl *Param1, const ParmVarDecl *Param2) const {
1691 return lazyMapOfSetsIntersectionExists(TargetParams, Param1, Param2);
1692 }
1693 };
1694
1695 /// Implements the heuristic that marks two parameters related if the same
1696 /// member is accessed (referred to) inside the current function's body.
1697 class AccessedSameMemberOf {
1698 ParamToSmallSetMap<const Decl *> AccessedMembers;
1699
1700 public:
setup(const FunctionDecl * FD)1701 void setup(const FunctionDecl *FD) {
1702 auto MembersCalledOnParams = match(
1703 functionDecl(forEachDescendant(
1704 memberExpr(hasObjectExpression(paramRefExpr())).bind("mem-expr"))),
1705 *FD, FD->getASTContext());
1706
1707 for (const auto &Match : MembersCalledOnParams) {
1708 const auto *AccessedParam = Match.getNodeAs<ParmVarDecl>("param");
1709 const auto *ME = Match.getNodeAs<MemberExpr>("mem-expr");
1710 assert(AccessedParam && ME);
1711 AccessedMembers[AccessedParam].insert(
1712 ME->getMemberDecl()->getCanonicalDecl());
1713 }
1714 }
1715
operator ()(const ParmVarDecl * Param1,const ParmVarDecl * Param2) const1716 bool operator()(const ParmVarDecl *Param1, const ParmVarDecl *Param2) const {
1717 return lazyMapOfSetsIntersectionExists(AccessedMembers, Param1, Param2);
1718 }
1719 };
1720
1721 /// Implements the heuristic that marks two parameters related if different
1722 /// ReturnStmts return them from the function.
1723 class Returned {
1724 llvm::SmallVector<const ParmVarDecl *, SmallDataStructureSize> ReturnedParams;
1725
1726 public:
setup(const FunctionDecl * FD)1727 void setup(const FunctionDecl *FD) {
1728 // TODO: Handle co_return.
1729 auto ParamReturns = match(functionDecl(forEachDescendant(
1730 returnStmt(hasReturnValue(paramRefExpr())))),
1731 *FD, FD->getASTContext());
1732 for (const auto &Match : ParamReturns) {
1733 const auto *ReturnedParam = Match.getNodeAs<ParmVarDecl>("param");
1734 assert(ReturnedParam);
1735
1736 if (find(FD->parameters(), ReturnedParam) == FD->param_end())
1737 // Inside the subtree of a FunctionDecl there might be ReturnStmts of
1738 // a parameter that isn't the parameter of the function, e.g. in the
1739 // case of lambdas.
1740 continue;
1741
1742 ReturnedParams.emplace_back(ReturnedParam);
1743 }
1744 }
1745
operator ()(const ParmVarDecl * Param1,const ParmVarDecl * Param2) const1746 bool operator()(const ParmVarDecl *Param1, const ParmVarDecl *Param2) const {
1747 return llvm::is_contained(ReturnedParams, Param1) &&
1748 llvm::is_contained(ReturnedParams, Param2);
1749 }
1750 };
1751
1752 } // namespace relatedness_heuristic
1753
1754 /// Helper class that is used to detect if two parameters of the same function
1755 /// are used in a similar fashion, to suppress the result.
1756 class SimilarlyUsedParameterPairSuppressor {
1757 const bool Enabled;
1758 relatedness_heuristic::AppearsInSameExpr SameExpr;
1759 relatedness_heuristic::PassedToSameFunction PassToFun;
1760 relatedness_heuristic::AccessedSameMemberOf SameMember;
1761 relatedness_heuristic::Returned Returns;
1762
1763 public:
SimilarlyUsedParameterPairSuppressor(const FunctionDecl * FD,bool Enable)1764 SimilarlyUsedParameterPairSuppressor(const FunctionDecl *FD, bool Enable)
1765 : Enabled(Enable) {
1766 if (!Enable)
1767 return;
1768
1769 SameExpr.setup(FD);
1770 PassToFun.setup(FD);
1771 SameMember.setup(FD);
1772 Returns.setup(FD);
1773 }
1774
1775 /// Returns whether the specified two parameters are deemed similarly used
1776 /// or related by the heuristics.
operator ()(const ParmVarDecl * Param1,const ParmVarDecl * Param2) const1777 bool operator()(const ParmVarDecl *Param1, const ParmVarDecl *Param2) const {
1778 if (!Enabled)
1779 return false;
1780
1781 LLVM_DEBUG(llvm::dbgs()
1782 << "::: Matching similar usage / relatedness heuristic...\n");
1783
1784 if (SameExpr(Param1, Param2)) {
1785 LLVM_DEBUG(llvm::dbgs() << "::: Used in the same expression.\n");
1786 return true;
1787 }
1788
1789 if (PassToFun(Param1, Param2)) {
1790 LLVM_DEBUG(llvm::dbgs()
1791 << "::: Passed to same function in different calls.\n");
1792 return true;
1793 }
1794
1795 if (SameMember(Param1, Param2)) {
1796 LLVM_DEBUG(llvm::dbgs()
1797 << "::: Same member field access or method called.\n");
1798 return true;
1799 }
1800
1801 if (Returns(Param1, Param2)) {
1802 LLVM_DEBUG(llvm::dbgs() << "::: Both parameter returned.\n");
1803 return true;
1804 }
1805
1806 LLVM_DEBUG(llvm::dbgs() << "::: None.\n");
1807 return false;
1808 }
1809 };
1810
1811 // (This function hoists the call to operator() of the wrapper, so we do not
1812 // need to define the previous class at the top of the file.)
1813 static inline bool
isSimilarlyUsedParameter(const SimilarlyUsedParameterPairSuppressor & Suppressor,const ParmVarDecl * Param1,const ParmVarDecl * Param2)1814 isSimilarlyUsedParameter(const SimilarlyUsedParameterPairSuppressor &Suppressor,
1815 const ParmVarDecl *Param1, const ParmVarDecl *Param2) {
1816 return Suppressor(Param1, Param2);
1817 }
1818
padStringAtEnd(SmallVectorImpl<char> & Str,std::size_t ToLen)1819 static void padStringAtEnd(SmallVectorImpl<char> &Str, std::size_t ToLen) {
1820 while (Str.size() < ToLen)
1821 Str.emplace_back('\0');
1822 }
1823
padStringAtBegin(SmallVectorImpl<char> & Str,std::size_t ToLen)1824 static void padStringAtBegin(SmallVectorImpl<char> &Str, std::size_t ToLen) {
1825 while (Str.size() < ToLen)
1826 Str.insert(Str.begin(), '\0');
1827 }
1828
isCommonPrefixWithoutSomeCharacters(std::size_t N,StringRef S1,StringRef S2)1829 static bool isCommonPrefixWithoutSomeCharacters(std::size_t N, StringRef S1,
1830 StringRef S2) {
1831 assert(S1.size() >= N && S2.size() >= N);
1832 StringRef S1Prefix = S1.take_front(S1.size() - N),
1833 S2Prefix = S2.take_front(S2.size() - N);
1834 return S1Prefix == S2Prefix && !S1Prefix.empty();
1835 }
1836
isCommonSuffixWithoutSomeCharacters(std::size_t N,StringRef S1,StringRef S2)1837 static bool isCommonSuffixWithoutSomeCharacters(std::size_t N, StringRef S1,
1838 StringRef S2) {
1839 assert(S1.size() >= N && S2.size() >= N);
1840 StringRef S1Suffix = S1.take_back(S1.size() - N),
1841 S2Suffix = S2.take_back(S2.size() - N);
1842 return S1Suffix == S2Suffix && !S1Suffix.empty();
1843 }
1844
1845 /// Returns whether the two strings are prefixes or suffixes of each other with
1846 /// at most Threshold characters differing on the non-common end.
prefixSuffixCoverUnderThreshold(std::size_t Threshold,StringRef Str1,StringRef Str2)1847 static bool prefixSuffixCoverUnderThreshold(std::size_t Threshold,
1848 StringRef Str1, StringRef Str2) {
1849 if (Threshold == 0)
1850 return false;
1851
1852 // Pad the two strings to the longer length.
1853 std::size_t BiggerLength = std::max(Str1.size(), Str2.size());
1854
1855 if (BiggerLength <= Threshold)
1856 // If the length of the strings is still smaller than the threshold, they
1857 // would be covered by an empty prefix/suffix with the rest differing.
1858 // (E.g. "A" and "X" with Threshold = 1 would mean we think they are
1859 // similar and do not warn about them, which is a too eager assumption.)
1860 return false;
1861
1862 SmallString<32> S1PadE{Str1}, S2PadE{Str2};
1863 padStringAtEnd(S1PadE, BiggerLength);
1864 padStringAtEnd(S2PadE, BiggerLength);
1865
1866 if (isCommonPrefixWithoutSomeCharacters(
1867 Threshold, StringRef{S1PadE.begin(), BiggerLength},
1868 StringRef{S2PadE.begin(), BiggerLength}))
1869 return true;
1870
1871 SmallString<32> S1PadB{Str1}, S2PadB{Str2};
1872 padStringAtBegin(S1PadB, BiggerLength);
1873 padStringAtBegin(S2PadB, BiggerLength);
1874
1875 if (isCommonSuffixWithoutSomeCharacters(
1876 Threshold, StringRef{S1PadB.begin(), BiggerLength},
1877 StringRef{S2PadB.begin(), BiggerLength}))
1878 return true;
1879
1880 return false;
1881 }
1882
1883 } // namespace filter
1884
1885 /// Matches functions that have at least the specified amount of parameters.
AST_MATCHER_P(FunctionDecl,parameterCountGE,unsigned,N)1886 AST_MATCHER_P(FunctionDecl, parameterCountGE, unsigned, N) {
1887 return Node.getNumParams() >= N;
1888 }
1889
1890 /// Matches *any* overloaded unary and binary operators.
AST_MATCHER(FunctionDecl,isOverloadedUnaryOrBinaryOperator)1891 AST_MATCHER(FunctionDecl, isOverloadedUnaryOrBinaryOperator) {
1892 switch (Node.getOverloadedOperator()) {
1893 case OO_None:
1894 case OO_New:
1895 case OO_Delete:
1896 case OO_Array_New:
1897 case OO_Array_Delete:
1898 case OO_Conditional:
1899 case OO_Coawait:
1900 return false;
1901
1902 default:
1903 return Node.getNumParams() <= 2;
1904 }
1905 }
1906
1907 /// Returns the DefaultMinimumLength if the Value of requested minimum length
1908 /// is less than 2. Minimum lengths of 0 or 1 are not accepted.
clampMinimumLength(const unsigned Value)1909 static inline unsigned clampMinimumLength(const unsigned Value) {
1910 return Value < 2 ? DefaultMinimumLength : Value;
1911 }
1912
1913 // FIXME: Maybe unneeded, getNameForDiagnostic() is expected to change to return
1914 // a crafted location when the node itself is unnamed. (See D84658, D85033.)
1915 /// Returns the diagnostic-friendly name of the node, or empty string.
getName(const NamedDecl * ND)1916 static SmallString<64> getName(const NamedDecl *ND) {
1917 SmallString<64> Name;
1918 llvm::raw_svector_ostream OS{Name};
1919 ND->getNameForDiagnostic(OS, ND->getASTContext().getPrintingPolicy(), false);
1920 return Name;
1921 }
1922
1923 /// Returns the diagnostic-friendly name of the node, or a constant value.
getNameOrUnnamed(const NamedDecl * ND)1924 static SmallString<64> getNameOrUnnamed(const NamedDecl *ND) {
1925 auto Name = getName(ND);
1926 if (Name.empty())
1927 Name = "<unnamed>";
1928 return Name;
1929 }
1930
1931 /// Returns whether a particular Mix between two parameters should have the
1932 /// types involved diagnosed to the user. This is only a flag check.
needsToPrintTypeInDiagnostic(const model::Mix & M)1933 static inline bool needsToPrintTypeInDiagnostic(const model::Mix &M) {
1934 using namespace model;
1935 return static_cast<bool>(
1936 M.flags() &
1937 (MixFlags::TypeAlias | MixFlags::ReferenceBind | MixFlags::Qualifiers));
1938 }
1939
1940 /// Returns whether a particular Mix between the two parameters should have
1941 /// implicit conversions elaborated.
needsToElaborateImplicitConversion(const model::Mix & M)1942 static inline bool needsToElaborateImplicitConversion(const model::Mix &M) {
1943 return hasFlag(M.flags(), model::MixFlags::ImplicitConversion);
1944 }
1945
1946 namespace {
1947
1948 /// This class formats a conversion sequence into a "Ty1 -> Ty2 -> Ty3" line
1949 /// that can be used in diagnostics.
1950 struct FormattedConversionSequence {
1951 std::string DiagnosticText;
1952
1953 /// The formatted sequence is trivial if it is "Ty1 -> Ty2", but Ty1 and
1954 /// Ty2 are the types that are shown in the code. A trivial diagnostic
1955 /// does not need to be printed.
1956 bool Trivial = true;
1957
FormattedConversionSequenceclang::tidy::bugprone::__anoneac3b3da0b11::FormattedConversionSequence1958 FormattedConversionSequence(const PrintingPolicy &PP,
1959 StringRef StartTypeAsDiagnosed,
1960 const model::ConversionSequence &Conv,
1961 StringRef DestinationTypeAsDiagnosed) {
1962 llvm::raw_string_ostream OS{DiagnosticText};
1963
1964 // Print the type name as it is printed in other places in the diagnostic.
1965 OS << '\'' << StartTypeAsDiagnosed << '\'';
1966 std::string LastAddedType = StartTypeAsDiagnosed.str();
1967 std::size_t NumElementsAdded = 1;
1968
1969 // However, the parameter's defined type might not be what the implicit
1970 // conversion started with, e.g. if a typedef is found to convert.
1971 std::string SeqBeginTypeStr = Conv.Begin.getAsString(PP);
1972 std::string SeqEndTypeStr = Conv.End.getAsString(PP);
1973 if (StartTypeAsDiagnosed != SeqBeginTypeStr) {
1974 OS << " (as '" << SeqBeginTypeStr << "')";
1975 LastAddedType = SeqBeginTypeStr;
1976 Trivial = false;
1977 }
1978
1979 auto AddType = [&](StringRef ToAdd) {
1980 if (LastAddedType != ToAdd && ToAdd != SeqEndTypeStr) {
1981 OS << " -> '" << ToAdd << "'";
1982 LastAddedType = ToAdd.str();
1983 ++NumElementsAdded;
1984 }
1985 };
1986 for (QualType InvolvedType : Conv.getInvolvedTypesInSequence())
1987 // Print every type that's unique in the sequence into the diagnosis.
1988 AddType(InvolvedType.getAsString(PP));
1989
1990 if (LastAddedType != DestinationTypeAsDiagnosed) {
1991 OS << " -> '" << DestinationTypeAsDiagnosed << "'";
1992 LastAddedType = DestinationTypeAsDiagnosed.str();
1993 ++NumElementsAdded;
1994 }
1995
1996 // Same reasoning as with the Begin, e.g. if the converted-to type is a
1997 // typedef, it will not be the same inside the conversion sequence (where
1998 // the model already tore off typedefs) as in the code.
1999 if (DestinationTypeAsDiagnosed != SeqEndTypeStr) {
2000 OS << " (as '" << SeqEndTypeStr << "')";
2001 LastAddedType = SeqEndTypeStr;
2002 Trivial = false;
2003 }
2004
2005 if (Trivial && NumElementsAdded > 2)
2006 // If the thing is still marked trivial but we have more than the
2007 // from and to types added, it should not be trivial, and elaborated
2008 // when printing the diagnostic.
2009 Trivial = false;
2010 }
2011 };
2012
2013 /// Retains the elements called with and returns whether the call is done with
2014 /// a new element.
2015 template <typename E, std::size_t N> class InsertOnce {
2016 llvm::SmallSet<E, N> CalledWith;
2017
2018 public:
operator ()(E El)2019 bool operator()(E El) { return CalledWith.insert(std::move(El)).second; }
2020
calledWith(const E & El) const2021 bool calledWith(const E &El) const { return CalledWith.contains(El); }
2022 };
2023
2024 struct SwappedEqualQualTypePair {
2025 QualType LHSType, RHSType;
2026
operator ==clang::tidy::bugprone::__anoneac3b3da0b11::SwappedEqualQualTypePair2027 bool operator==(const SwappedEqualQualTypePair &Other) const {
2028 return (LHSType == Other.LHSType && RHSType == Other.RHSType) ||
2029 (LHSType == Other.RHSType && RHSType == Other.LHSType);
2030 }
2031
operator <clang::tidy::bugprone::__anoneac3b3da0b11::SwappedEqualQualTypePair2032 bool operator<(const SwappedEqualQualTypePair &Other) const {
2033 return LHSType < Other.LHSType && RHSType < Other.RHSType;
2034 }
2035 };
2036
2037 struct TypeAliasDiagnosticTuple {
2038 QualType LHSType, RHSType, CommonType;
2039
operator ==clang::tidy::bugprone::__anoneac3b3da0b11::TypeAliasDiagnosticTuple2040 bool operator==(const TypeAliasDiagnosticTuple &Other) const {
2041 return CommonType == Other.CommonType &&
2042 ((LHSType == Other.LHSType && RHSType == Other.RHSType) ||
2043 (LHSType == Other.RHSType && RHSType == Other.LHSType));
2044 }
2045
operator <clang::tidy::bugprone::__anoneac3b3da0b11::TypeAliasDiagnosticTuple2046 bool operator<(const TypeAliasDiagnosticTuple &Other) const {
2047 return CommonType < Other.CommonType && LHSType < Other.LHSType &&
2048 RHSType < Other.RHSType;
2049 }
2050 };
2051
2052 /// Helper class to only emit a diagnostic related to MixFlags::TypeAlias once.
2053 class UniqueTypeAliasDiagnosticHelper
2054 : public InsertOnce<TypeAliasDiagnosticTuple, 8> {
2055 using Base = InsertOnce<TypeAliasDiagnosticTuple, 8>;
2056
2057 public:
2058 /// Returns whether the diagnostic for LHSType and RHSType which are both
2059 /// referring to CommonType being the same has not been emitted already.
operator ()(QualType LHSType,QualType RHSType,QualType CommonType)2060 bool operator()(QualType LHSType, QualType RHSType, QualType CommonType) {
2061 if (CommonType.isNull() || CommonType == LHSType || CommonType == RHSType)
2062 return Base::operator()({LHSType, RHSType, {}});
2063
2064 TypeAliasDiagnosticTuple ThreeTuple{LHSType, RHSType, CommonType};
2065 if (!Base::operator()(ThreeTuple))
2066 return false;
2067
2068 bool AlreadySaidLHSAndCommonIsSame = calledWith({LHSType, CommonType, {}});
2069 bool AlreadySaidRHSAndCommonIsSame = calledWith({RHSType, CommonType, {}});
2070 if (AlreadySaidLHSAndCommonIsSame && AlreadySaidRHSAndCommonIsSame) {
2071 // "SomeInt == int" && "SomeOtherInt == int" => "Common(SomeInt,
2072 // SomeOtherInt) == int", no need to diagnose it. Save the 3-tuple only
2073 // for shortcut if it ever appears again.
2074 return false;
2075 }
2076
2077 return true;
2078 }
2079 };
2080
2081 } // namespace
2082
EasilySwappableParametersCheck(StringRef Name,ClangTidyContext * Context)2083 EasilySwappableParametersCheck::EasilySwappableParametersCheck(
2084 StringRef Name, ClangTidyContext *Context)
2085 : ClangTidyCheck(Name, Context),
2086 MinimumLength(clampMinimumLength(
2087 Options.get("MinimumLength", DefaultMinimumLength))),
2088 IgnoredParameterNames(optutils::parseStringList(
2089 Options.get("IgnoredParameterNames", DefaultIgnoredParameterNames))),
2090 IgnoredParameterTypeSuffixes(optutils::parseStringList(
2091 Options.get("IgnoredParameterTypeSuffixes",
2092 DefaultIgnoredParameterTypeSuffixes))),
2093 QualifiersMix(Options.get("QualifiersMix", DefaultQualifiersMix)),
2094 ModelImplicitConversions(Options.get("ModelImplicitConversions",
2095 DefaultModelImplicitConversions)),
2096 SuppressParametersUsedTogether(
2097 Options.get("SuppressParametersUsedTogether",
2098 DefaultSuppressParametersUsedTogether)),
2099 NamePrefixSuffixSilenceDissimilarityTreshold(
2100 Options.get("NamePrefixSuffixSilenceDissimilarityTreshold",
2101 DefaultNamePrefixSuffixSilenceDissimilarityTreshold)) {}
2102
storeOptions(ClangTidyOptions::OptionMap & Opts)2103 void EasilySwappableParametersCheck::storeOptions(
2104 ClangTidyOptions::OptionMap &Opts) {
2105 Options.store(Opts, "MinimumLength", MinimumLength);
2106 Options.store(Opts, "IgnoredParameterNames",
2107 optutils::serializeStringList(IgnoredParameterNames));
2108 Options.store(Opts, "IgnoredParameterTypeSuffixes",
2109 optutils::serializeStringList(IgnoredParameterTypeSuffixes));
2110 Options.store(Opts, "QualifiersMix", QualifiersMix);
2111 Options.store(Opts, "ModelImplicitConversions", ModelImplicitConversions);
2112 Options.store(Opts, "SuppressParametersUsedTogether",
2113 SuppressParametersUsedTogether);
2114 Options.store(Opts, "NamePrefixSuffixSilenceDissimilarityTreshold",
2115 NamePrefixSuffixSilenceDissimilarityTreshold);
2116 }
2117
registerMatchers(MatchFinder * Finder)2118 void EasilySwappableParametersCheck::registerMatchers(MatchFinder *Finder) {
2119 const auto BaseConstraints = functionDecl(
2120 // Only report for definition nodes, as fixing the issues reported
2121 // requires the user to be able to change code.
2122 isDefinition(), parameterCountGE(MinimumLength),
2123 unless(isOverloadedUnaryOrBinaryOperator()));
2124
2125 Finder->addMatcher(
2126 functionDecl(BaseConstraints,
2127 unless(ast_matchers::isTemplateInstantiation()))
2128 .bind("func"),
2129 this);
2130 Finder->addMatcher(
2131 functionDecl(BaseConstraints, isExplicitTemplateSpecialization())
2132 .bind("func"),
2133 this);
2134 }
2135
check(const MatchFinder::MatchResult & Result)2136 void EasilySwappableParametersCheck::check(
2137 const MatchFinder::MatchResult &Result) {
2138 using namespace model;
2139 using namespace filter;
2140
2141 const auto *FD = Result.Nodes.getNodeAs<FunctionDecl>("func");
2142 assert(FD);
2143
2144 const PrintingPolicy &PP = FD->getASTContext().getPrintingPolicy();
2145 std::size_t NumParams = FD->getNumParams();
2146 std::size_t MixableRangeStartIndex = 0;
2147
2148 // Spawn one suppressor and if the user requested, gather information from
2149 // the AST for the parameters' usages.
2150 filter::SimilarlyUsedParameterPairSuppressor UsageBasedSuppressor{
2151 FD, SuppressParametersUsedTogether};
2152
2153 LLVM_DEBUG(llvm::dbgs() << "Begin analysis of " << getName(FD) << " with "
2154 << NumParams << " parameters...\n");
2155 while (MixableRangeStartIndex < NumParams) {
2156 if (isIgnoredParameter(*this, FD->getParamDecl(MixableRangeStartIndex))) {
2157 LLVM_DEBUG(llvm::dbgs()
2158 << "Parameter #" << MixableRangeStartIndex << " ignored.\n");
2159 ++MixableRangeStartIndex;
2160 continue;
2161 }
2162
2163 MixableParameterRange R = modelMixingRange(
2164 *this, FD, MixableRangeStartIndex, UsageBasedSuppressor);
2165 assert(R.NumParamsChecked > 0 && "Ensure forward progress!");
2166 MixableRangeStartIndex += R.NumParamsChecked;
2167 if (R.NumParamsChecked < MinimumLength) {
2168 LLVM_DEBUG(llvm::dbgs() << "Ignoring range of " << R.NumParamsChecked
2169 << " lower than limit.\n");
2170 continue;
2171 }
2172
2173 bool NeedsAnyTypeNote = llvm::any_of(R.Mixes, needsToPrintTypeInDiagnostic);
2174 bool HasAnyImplicits =
2175 llvm::any_of(R.Mixes, needsToElaborateImplicitConversion);
2176 const ParmVarDecl *First = R.getFirstParam(), *Last = R.getLastParam();
2177 std::string FirstParamTypeAsWritten = First->getType().getAsString(PP);
2178 {
2179 StringRef DiagText;
2180
2181 if (HasAnyImplicits)
2182 DiagText = "%0 adjacent parameters of %1 of convertible types are "
2183 "easily swapped by mistake";
2184 else if (NeedsAnyTypeNote)
2185 DiagText = "%0 adjacent parameters of %1 of similar type are easily "
2186 "swapped by mistake";
2187 else
2188 DiagText = "%0 adjacent parameters of %1 of similar type ('%2') are "
2189 "easily swapped by mistake";
2190
2191 auto Diag = diag(First->getOuterLocStart(), DiagText)
2192 << static_cast<unsigned>(R.NumParamsChecked) << FD;
2193 if (!NeedsAnyTypeNote)
2194 Diag << FirstParamTypeAsWritten;
2195
2196 CharSourceRange HighlightRange = CharSourceRange::getTokenRange(
2197 First->getBeginLoc(), Last->getEndLoc());
2198 Diag << HighlightRange;
2199 }
2200
2201 // There is a chance that the previous highlight did not succeed, e.g. when
2202 // the two parameters are on different lines. For clarity, show the user
2203 // the involved variable explicitly.
2204 diag(First->getLocation(), "the first parameter in the range is '%0'",
2205 DiagnosticIDs::Note)
2206 << getNameOrUnnamed(First)
2207 << CharSourceRange::getTokenRange(First->getLocation(),
2208 First->getLocation());
2209 diag(Last->getLocation(), "the last parameter in the range is '%0'",
2210 DiagnosticIDs::Note)
2211 << getNameOrUnnamed(Last)
2212 << CharSourceRange::getTokenRange(Last->getLocation(),
2213 Last->getLocation());
2214
2215 // Helper classes to silence elaborative diagnostic notes that would be
2216 // too verbose.
2217 UniqueTypeAliasDiagnosticHelper UniqueTypeAlias;
2218 InsertOnce<SwappedEqualQualTypePair, 8> UniqueBindPower;
2219 InsertOnce<SwappedEqualQualTypePair, 8> UniqueImplicitConversion;
2220
2221 for (const model::Mix &M : R.Mixes) {
2222 assert(M.mixable() && "Sentinel or false mix in result.");
2223 if (!needsToPrintTypeInDiagnostic(M) &&
2224 !needsToElaborateImplicitConversion(M))
2225 continue;
2226
2227 // Typedefs might result in the type of the variable needing to be
2228 // emitted to a note diagnostic, so prepare it.
2229 const ParmVarDecl *LVar = M.First;
2230 const ParmVarDecl *RVar = M.Second;
2231 QualType LType = LVar->getType();
2232 QualType RType = RVar->getType();
2233 QualType CommonType = M.commonUnderlyingType();
2234 std::string LTypeStr = LType.getAsString(PP);
2235 std::string RTypeStr = RType.getAsString(PP);
2236 std::string CommonTypeStr = CommonType.getAsString(PP);
2237
2238 if (hasFlag(M.flags(), MixFlags::TypeAlias) &&
2239 UniqueTypeAlias(LType, RType, CommonType)) {
2240 StringRef DiagText;
2241 bool ExplicitlyPrintCommonType = false;
2242 if (LTypeStr == CommonTypeStr || RTypeStr == CommonTypeStr) {
2243 if (hasFlag(M.flags(), MixFlags::Qualifiers))
2244 DiagText = "after resolving type aliases, '%0' and '%1' share a "
2245 "common type";
2246 else
2247 DiagText =
2248 "after resolving type aliases, '%0' and '%1' are the same";
2249 } else if (!CommonType.isNull()) {
2250 DiagText = "after resolving type aliases, the common type of '%0' "
2251 "and '%1' is '%2'";
2252 ExplicitlyPrintCommonType = true;
2253 }
2254
2255 auto Diag =
2256 diag(LVar->getOuterLocStart(), DiagText, DiagnosticIDs::Note)
2257 << LTypeStr << RTypeStr;
2258 if (ExplicitlyPrintCommonType)
2259 Diag << CommonTypeStr;
2260 }
2261
2262 if ((hasFlag(M.flags(), MixFlags::ReferenceBind) ||
2263 hasFlag(M.flags(), MixFlags::Qualifiers)) &&
2264 UniqueBindPower({LType, RType})) {
2265 StringRef DiagText = "'%0' and '%1' parameters accept and bind the "
2266 "same kind of values";
2267 diag(RVar->getOuterLocStart(), DiagText, DiagnosticIDs::Note)
2268 << LTypeStr << RTypeStr;
2269 }
2270
2271 if (needsToElaborateImplicitConversion(M) &&
2272 UniqueImplicitConversion({LType, RType})) {
2273 const model::ConversionSequence <R =
2274 M.leftToRightConversionSequence();
2275 const model::ConversionSequence &RTL =
2276 M.rightToLeftConversionSequence();
2277 FormattedConversionSequence LTRFmt{PP, LTypeStr, LTR, RTypeStr};
2278 FormattedConversionSequence RTLFmt{PP, RTypeStr, RTL, LTypeStr};
2279
2280 StringRef DiagText = "'%0' and '%1' may be implicitly converted";
2281 if (!LTRFmt.Trivial || !RTLFmt.Trivial)
2282 DiagText = "'%0' and '%1' may be implicitly converted: %2, %3";
2283
2284 {
2285 auto Diag =
2286 diag(RVar->getOuterLocStart(), DiagText, DiagnosticIDs::Note)
2287 << LTypeStr << RTypeStr;
2288
2289 if (!LTRFmt.Trivial || !RTLFmt.Trivial)
2290 Diag << LTRFmt.DiagnosticText << RTLFmt.DiagnosticText;
2291 }
2292
2293 StringRef ConversionFunctionDiagText =
2294 "the implicit conversion involves the "
2295 "%select{|converting constructor|conversion operator}0 "
2296 "declared here";
2297 if (const FunctionDecl *LFD = LTR.getUserDefinedConversionFunction())
2298 diag(LFD->getLocation(), ConversionFunctionDiagText,
2299 DiagnosticIDs::Note)
2300 << static_cast<unsigned>(LTR.UDConvKind)
2301 << LTR.getUserDefinedConversionHighlight();
2302 if (const FunctionDecl *RFD = RTL.getUserDefinedConversionFunction())
2303 diag(RFD->getLocation(), ConversionFunctionDiagText,
2304 DiagnosticIDs::Note)
2305 << static_cast<unsigned>(RTL.UDConvKind)
2306 << RTL.getUserDefinedConversionHighlight();
2307 }
2308 }
2309 }
2310 }
2311
2312 } // namespace clang::tidy::bugprone
2313