Index: clang-tools-extra/clang-tidy/experimental/CMakeLists.txt =================================================================== --- clang-tools-extra/clang-tidy/experimental/CMakeLists.txt +++ clang-tools-extra/clang-tidy/experimental/CMakeLists.txt @@ -1,6 +1,7 @@ set(LLVM_LINK_COMPONENTS support) add_clang_library(clangTidyExperimentalModule + CppcoreguidelinesAvoidAdjacentParametersOfTheSameTypeCheck.cpp ExperimentalTidyModule.cpp LINK_LIBS Index: clang-tools-extra/clang-tidy/experimental/CppcoreguidelinesAvoidAdjacentParametersOfTheSameTypeCheck.h =================================================================== --- /dev/null +++ clang-tools-extra/clang-tidy/experimental/CppcoreguidelinesAvoidAdjacentParametersOfTheSameTypeCheck.h @@ -0,0 +1,57 @@ +//== CppcoreguidelinesAvoidAdjacentParametersOfTheSameTypeCheck.h clang-tidy =// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_EXPERIMENTAL_CPPCOREGUIDELINESAVOIDADJACENTPARAMETERSOFTHESAMETYPECHECK_H +#define LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_EXPERIMENTAL_CPPCOREGUIDELINESAVOIDADJACENTPARAMETERSOFTHESAMETYPECHECK_H + +#include "../ClangTidyCheck.h" + +namespace clang { +namespace tidy { +namespace experimental { + +/// Finds function definitions where parameters of the same type follow each +/// other directly, making call sites prone to calling the function with swapped +/// or badly ordered arguments. +/// +/// For the user-facing documentation see: +/// http://clang.llvm.org/extra/clang-tidy/checks/experimental-cppcoreguidelines-avoid-adjacent-parameters-of-the-same-type.html +class CppcoreguidelinesAvoidAdjacentParametersOfTheSameTypeCheck : public ClangTidyCheck { +public: + CppcoreguidelinesAvoidAdjacentParametersOfTheSameTypeCheck( + StringRef Name, ClangTidyContext *Context); + void registerMatchers(ast_matchers::MatchFinder *Finder) override; + void check(const ast_matchers::MatchFinder::MatchResult &Result) override; + void storeOptions(ClangTidyOptions::OptionMap &Opts) override; + + /// Returns whether the given parameter is ignored based on its type name + /// or parameter name. + bool isIgnored(const ParmVarDecl *Node) const; + + /// The minimum length of an adjacent range required to have to produce + /// a diagnostic. + const unsigned MinimumLength; + + /// The parameter names (as written in the code) to be ignored when + /// checking for adjacent parameter ranges. + const std::vector IgnoredParamNames; + + /// The parameter type name suffixes (as written in the code) to be ignored + /// when checking for adjacent parameter ranges. + const std::vector IgnoredParamTypes; + + /// Whether to consider 'T' and 'const T'/'volatile T'/etc. arguments to be + /// possible mixup. + const bool CVRMixPossible; +}; + +} // namespace experimental +} // namespace tidy +} // namespace clang + +#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_EXPERIMENTAL_CPPCOREGUIDELINESAVOIDADJACENTPARAMETERSOFTHESAMETYPECHECK_H Index: clang-tools-extra/clang-tidy/experimental/CppcoreguidelinesAvoidAdjacentParametersOfTheSameTypeCheck.cpp =================================================================== --- /dev/null +++ clang-tools-extra/clang-tidy/experimental/CppcoreguidelinesAvoidAdjacentParametersOfTheSameTypeCheck.cpp @@ -0,0 +1,619 @@ +// CppcoreguidelinesAvoidAdjacentParametersOfTheSameTypeCheck.cpp clang-tidy // +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#include "CppcoreguidelinesAvoidAdjacentParametersOfTheSameTypeCheck.h" +#include "../utils/OptionsUtils.h" +#include "clang/AST/ASTContext.h" +#include "clang/ASTMatchers/ASTMatchFinder.h" +#include "llvm/ADT/BitmaskEnum.h" +#include "llvm/ADT/SmallVector.h" + +#include +#include + +using namespace clang::ast_matchers; + +namespace clang { +namespace tidy { +namespace experimental { + +namespace { + +/// Annotates a possible mix of parameters based on which language construct +/// is responsible for allowing thee mix to happen. This is a bitflag. +enum MixupTag : unsigned char { + MIXUP_Invalid = 0, //< Sentinel 0 bit pattern value for masking. DO NOT USE! + +// Set the bit at index N to 1 as the enum constant. N = 0 is invalid. +#define BIT(Name, N) MIXUP_##Name = (1ull << (N##ull - 1ull)) + BIT(None, 1), //< Mixup is not possible. + BIT(Trivial, 2), //< No extra information needed. + BIT(Typedef, 3), //< Parameter of a typedef which resolves to an effective + //< desugared type same as the other arg. + BIT(RefBind, 4), //< Parameter mixes with another due to reference binding. + BIT(CVR, 5), //< Parameter mixes with another through implicit + //< qualification. +#undef BIT + + LLVM_MARK_AS_BITMASK_ENUM(/* LargestValue = */ MIXUP_CVR) +}; +LLVM_ENABLE_BITMASK_ENUMS_IN_NAMESPACE(); + +/// A named tuple that contains which parameter with which other parameter +/// can be mixed up in what fashion. +struct Mixup { + const ParmVarDecl *First, *Second; + MixupTag Flags; + + Mixup(const ParmVarDecl *A, const ParmVarDecl *B, MixupTag Flags) + : First(A), Second(B), Flags(Flags) {} + + Mixup operator|(MixupTag EnableFlags) const { + return {First, Second, Flags | EnableFlags}; + } + Mixup &operator|=(MixupTag EnableFlags) { + Flags |= EnableFlags; + return *this; + } + + /// Sanitises the Mixup's flags so it doesn't contain contradictory bits. + void sanitise() { + assert(Flags != MIXUP_Invalid && + "Mixup tag had full zero bit pattern value!"); + + if (Flags & MIXUP_None) { + // If at any point the checks mark the mixup impossible, it is just simply + // impossible. + Flags = MIXUP_None; + return; + } + + if (Flags == MIXUP_Trivial) + return; + + if (Flags ^ MIXUP_Trivial) + // If any other bits than Trivial is set, unset Trivial, so only the + // annotation bits warranting extra diagnostic are set. + Flags &= ~MIXUP_Trivial; + } +}; + +static_assert(std::is_trivially_copyable::value, + "keep Mixup trivially copyable!"); + +/// Represents a (closed) range of adjacent parameters that can be mixed up at +/// a call site. +struct MixableAdjacentParamRange { + std::size_t NumParamsChecked = 0; + llvm::SmallVector Mixups; + + const ParmVarDecl *getFirstParm() const { + // The first element added to the mixup vector has the left hand + // side set to the first argument in the range, if any is added. + assert(!Mixups.empty()); + return Mixups.front().First; + } + + const ParmVarDecl *getLastParm() const { + // There builder function paramEqualTypeRange breaks building an instance of + // this type if it finds something that can not be mixed up, by going + // *forward* in the list of arguments. So at the moment of break, the right + // hand side of the last conversion in the vector is the last argument in + // the adjacency range. + assert(!Mixups.empty()); + return Mixups.back().Second; + } +}; + +} // namespace + +/// Returns whether an lvalue reference refers to the same type as T. +static MixupTag RefBindsToSameType(const LValueReferenceType *LRef, + const Type *T, bool CVRMixPossible); + +/// Returns whether LType and RType refer to the same type in a sense that at a +/// call site it is possible to mix the types up if the actual arguments are +/// specified in opposite order. +/// \returns MixupTag indicating how a mixup between the arguments happens. +/// The final output of this potentially recursive function must be sanitised. +static MixupTag HowPossibleToMixUpAtCallSite(const QualType LType, + const QualType RType, + const ASTContext &Ctx, + const bool CVRMixPossible) { + if (LType == RType) + return MIXUP_Trivial; + + // Remove certain sugars that don't affect mixability from the types. + if (dyn_cast(LType.getTypePtr())) + return HowPossibleToMixUpAtCallSite(LType.getSingleStepDesugaredType(Ctx), + RType, Ctx, CVRMixPossible); + if (dyn_cast(RType.getTypePtr())) + return HowPossibleToMixUpAtCallSite( + LType, RType.getSingleStepDesugaredType(Ctx), Ctx, CVRMixPossible); + + // An argument of type 'T' and 'const T &' may bind with the same power. + // (Note this is a different case, as 'const T &' is a '&' on the top level, + // and only then a const.) + if (LType->isLValueReferenceType() || RType->isLValueReferenceType()) { + // (If both is the same reference type, earlier a return happened.) + + if (LType->isLValueReferenceType()) { + MixupTag RefBind = RefBindsToSameType(LType->getAs(), + RType.getTypePtr(), CVRMixPossible); + // RefBind may or may not have given us a tag (e.g. reference was to a + // typedef) via a recursive chain back to this function. Apply the + // "bind power" tag here to indicate a reference binding happened. + // (If RefBind was MIXUP_None, a later sanitise step will undo every bit + // except for None.) + return RefBind | MIXUP_RefBind; + } + if (RType->isLValueReferenceType()) { + MixupTag RefBind = RefBindsToSameType(RType->getAs(), + LType.getTypePtr(), CVRMixPossible); + return RefBind | MIXUP_RefBind; + } + } + + // A parameter of type 'T' and 'const T' may bind with the same power. + // Case for both types being const qualified (for the same type) is handled + // by LType == RType. + if (LType.getLocalCVRQualifiers() || RType.getLocalCVRQualifiers()) { + if (!CVRMixPossible) + return MIXUP_None; + + return HowPossibleToMixUpAtCallSite(LType.getUnqualifiedType(), + RType.getUnqualifiedType(), Ctx, + CVRMixPossible) | + MIXUP_CVR; + } + + { + const auto *LTypedef = LType->getAs(); + const auto *RTypedef = RType->getAs(); + if (LTypedef && RTypedef) + return MIXUP_Typedef | HowPossibleToMixUpAtCallSite(LTypedef->desugar(), + RTypedef->desugar(), + Ctx, CVRMixPossible); + if (LTypedef) + return MIXUP_Typedef | + HowPossibleToMixUpAtCallSite(LTypedef->desugar(), RType, Ctx, + CVRMixPossible); + if (RTypedef) + return MIXUP_Typedef | + HowPossibleToMixUpAtCallSite(LType, RTypedef->desugar(), Ctx, + CVRMixPossible); + } + + if (LType->isPointerType() && RType->isPointerType()) + // (Both types being the exact same pointer is handled by LType == RType.) + return HowPossibleToMixUpAtCallSite( + LType->getPointeeType(), RType->getPointeeType(), Ctx, CVRMixPossible); + + // A parameter of type 'T' and 'const T' may bind with the same power. + // Case for both types being const qualified (for the same type) is handled + // by LType == RType. + if (CVRMixPossible && + (LType.isLocalConstQualified() || LType.isLocalVolatileQualified())) + return MIXUP_CVR | HowPossibleToMixUpAtCallSite(LType.getUnqualifiedType(), + RType, Ctx, CVRMixPossible); + if (CVRMixPossible && + (RType.isLocalConstQualified() || RType.isLocalVolatileQualified())) + return MIXUP_CVR | + HowPossibleToMixUpAtCallSite(LType, RType.getUnqualifiedType(), Ctx, + CVRMixPossible); + + return MIXUP_None; +} + +static MixupTag RefBindsToSameType(const LValueReferenceType *LRef, + const Type *T, bool CVRMixPossible) { + const QualType ReferredType = LRef->getPointeeType(); + if (!ReferredType.isLocalConstQualified()) + // A non-const reference doesn't bind with the same power as a "normal" + // by-value parameter. + return MIXUP_None; + + if (const auto *TypedefTy = ReferredType.getTypePtr()->getAs()) + // If the referred type is a typedef, try checking the mixup-chance on the + // desugared type. + return HowPossibleToMixUpAtCallSite(TypedefTy->desugar(), QualType{T, 0}, + TypedefTy->getDecl()->getASTContext(), + CVRMixPossible); + + return ReferredType.getTypePtr() == T ? MIXUP_Trivial : MIXUP_None; +} + +/// Gets the mixable range of the parameters of F starting with the param at +/// index StartIdx. +static MixableAdjacentParamRange BuildMixableParameterRange( + const CppcoreguidelinesAvoidAdjacentParametersOfTheSameTypeCheck &Checker, + const FunctionDecl *F, unsigned int StartIdx) { + MixableAdjacentParamRange MixRange; + const unsigned int ParamCount = F->getNumParams(); + assert(StartIdx < ParamCount && "invalid start index given!"); + const ASTContext &Ctx = F->getASTContext(); + const ParmVarDecl *First = F->getParamDecl(StartIdx); + + // A parameter (the one at StartIdx) was checked. + MixRange.NumParamsChecked = 1; + + // Try checking parameters of the function from StartIdx until the range + // breaks. The range contains parameters that are mutually mixable with each + // other. + for (unsigned int I = StartIdx + 1; I < ParamCount; ++I) { + const ParmVarDecl *Ith = F->getParamDecl(I); + if (Checker.isIgnored(Ith)) + // If the next parameter in the range is ignored, break the range. + break; + + bool AnyMixupStored = false; + for (unsigned int J = StartIdx; J < I; ++J) { + const ParmVarDecl *Jth = F->getParamDecl(J); + Mixup M{Jth, Ith, + HowPossibleToMixUpAtCallSite(Jth->getType(), Ith->getType(), Ctx, + Checker.CVRMixPossible)}; + M.sanitise(); + assert(M.Flags != MIXUP_Invalid && + "Bits fell off, result is sentinel value."); + + if (M.Flags != MIXUP_None) { + MixRange.Mixups.emplace_back(M); + AnyMixupStored = true; + } + } + + if (!AnyMixupStored) + // If there is no "new" mixup possibility for the Ith param, it signifies + // the end of range. + break; + + // If the loop did not break earlier, another parameter was found to be + // mixable. + ++MixRange.NumParamsChecked; + } + + return MixRange; +} + +/// Prints the type's textual representation to the output stream. This printer +/// discards as many sugar as it can, for example removing typedefs and printing +/// the underlying type. +static void PutTypeName(const QualType QT, llvm::raw_ostream &OS, + const PrintingPolicy &PP) { + SplitQualType SQT = QT.split(); + const Type *Ty = SQT.Ty; + + if (const auto *DecayTy = dyn_cast(Ty)) + return PutTypeName(DecayTy->getPointeeType(), OS, PP); + + if (const auto *PointerTy = dyn_cast(Ty)) { + PutTypeName(PointerTy->getPointeeType(), OS, PP); + // Note: this might print types with weird grammar (function pointers, etc.) + // in a weird way. + OS << " *"; + + // Qualifications of a pointer has to be after the '*'. + // (The member is qualified in a subsequent recursive call.) + if (SQT.Quals.hasConst()) + OS << " const"; + if (SQT.Quals.hasVolatile()) + OS << " volatile"; + if (SQT.Quals.hasRestrict()) + OS << " restrict"; + return; + } + + // Qualifications of everything else can be written at the front. + if (SQT.Quals.hasConst()) + OS << "const "; + if (SQT.Quals.hasVolatile()) + OS << "volatile "; + if (SQT.Quals.hasRestrict()) + OS << "restrict "; + + if (const auto *RefTy = dyn_cast(Ty)) { + PutTypeName(RefTy->getPointeeType(), OS, PP); + if (RefTy->isLValueReferenceType()) + OS << " &"; + else if (RefTy->isRValueReferenceType()) + OS << " &&"; + } else if (const auto *BuiltinTy = dyn_cast(Ty)) + OS << BuiltinTy->getName(PP); + else if (const TagDecl *TagDeclTy = Ty->getAsTagDecl()) { + std::string Name = TagDeclTy->getName().str(); + if (!Name.empty() && !TagDeclTy->getASTContext().getLangOpts().CPlusPlus) + // Prepend "struct" before "T" in C mode. + Name = TagDeclTy->getKindName().str().append(" ").append(Name); + + if (Name.empty()) + Name = TagDeclTy->getTypedefNameForAnonDecl()->getName().str(); + + if (!Name.empty()) + OS << Name; + else + OS << "(unknown " + << static_cast(TagDeclTy)->getDeclKindName() + << ")"; + } else if (const auto *TypedefTy = dyn_cast(Ty)) + PutTypeName(TypedefTy->desugar(), OS, PP); + else if (const auto *TemplateTy = dyn_cast(Ty)) + OS << TemplateTy->getDecl()->getName(); + else if (const auto *DependentNTy = dyn_cast(Ty)) + OS << DependentNTy->getIdentifier()->getName(); + else if (const auto *FunctionPtrTy = dyn_cast(Ty)) { + PutTypeName(FunctionPtrTy->getReturnType(), OS, PP); + OS << " ("; + for (unsigned Idx = 0; Idx < FunctionPtrTy->getNumParams(); ++Idx) { + PutTypeName(FunctionPtrTy->getParamType(Idx), OS, PP); + if (Idx < FunctionPtrTy->getNumParams() - 1) + OS << ", "; + } + OS << ')'; + } else if (const auto *SpecialisationTy = + dyn_cast(Ty)) { + SpecialisationTy->getTemplateName().print(OS, PP, /* SuppressNNS =*/true); + OS << '<'; + for (const TemplateArgument &Arg : SpecialisationTy->template_arguments()) { + Arg.print(PP, OS); + } + OS << '>'; + } else if (const auto *ParenTy = dyn_cast(Ty)) + PutTypeName(ParenTy->getInnerType(), OS, PP); + else if (const auto *PackTy = dyn_cast(Ty)) { + PutTypeName(PackTy->getPattern(), OS, PP); + OS << "..."; + } else + // There are things like "GCC Vector type" and such that who knows how + // to print properly? + OS << "getTypeClassName() << '>'; +} + +static std::string TypeNameAsString(const QualType QT, + const PrintingPolicy &PP) { + std::string Ret; + { + llvm::raw_string_ostream OS{Ret}; + PutTypeName(QT, OS, PP); + } + + if (Ret.empty()) + Ret = ""; + return Ret; +} + +static const std::string DefaultIgnoredParamNames = + utils::options::serializeStringList({"iterator", "Iterator", "begin", + "Begin", "end", "End", "first", + "First", "last", "Last"}); + +static const std::string DefaultIgnoredParamTypes = + utils::options::serializeStringList( + {"it", "It", "iterator", "Iterator", "inputit", "InputIt", "forwardit", + "ForwardIt", "bidirit", "BidirIt", "constiterator", "const_iterator", + "Const_Iterator", "Constiterator", "ConstIterator"}); + +bool CppcoreguidelinesAvoidAdjacentParametersOfTheSameTypeCheck::isIgnored( + const ParmVarDecl *Node) const { + if (!Node->getIdentifier()) + return false; + + StringRef NodeName = Node->getName(); + if (llvm::any_of(IgnoredParamNames, + [&NodeName](const std::string &IgnoredName) { + return NodeName == IgnoredName; + })) + return true; + + const Type *NodeType = + Node->getType().getDesugaredType(Node->getASTContext()).getTypePtr(); + assert(NodeType && "parameter without a type?"); + + std::string NodeTypeName = Node->getType().getAsString(); + if (!NodeTypeName.empty()) { + if (llvm::any_of(IgnoredParamTypes, [&NodeName, &NodeTypeName]( + const std::string &IgnoredName) { + if (NodeTypeName == IgnoredName) + return true; + + std::size_t IgnoredNameLen = IgnoredName.length(); + if (NodeTypeName.length() > IgnoredNameLen && + NodeTypeName.compare(NodeTypeName.length() - IgnoredNameLen, + IgnoredNameLen, IgnoredName) == 0) + return true; + + return false; + })) + return true; + } + + return false; +} + +AST_MATCHER_P(FunctionDecl, hasAtLeastNumParams, unsigned int, N) { + return Node.getNumParams() >= N; +} + +static constexpr unsigned DefaultMinLength = 2; + +/// A range length of 0 or 1 is useless as it would simply report for every +/// parameter. Fix the user's input if bad value is given. +static unsigned ClampMinimumLength(const unsigned Option) { + return Option < 2 ? DefaultMinLength : Option; +} + +CppcoreguidelinesAvoidAdjacentParametersOfTheSameTypeCheck:: + CppcoreguidelinesAvoidAdjacentParametersOfTheSameTypeCheck( + StringRef Name, ClangTidyContext *Context) + : ClangTidyCheck(Name, Context), + MinimumLength( + ClampMinimumLength(Options.get("MinimumLength", DefaultMinLength))), + IgnoredParamNames(utils::options::parseStringList( + Options.get("IgnoredNames", DefaultIgnoredParamNames))), + IgnoredParamTypes(utils::options::parseStringList( + Options.get("IgnoredTypes", DefaultIgnoredParamTypes))), + CVRMixPossible(Options.get("CVRMixPossible", false)) {} + +void CppcoreguidelinesAvoidAdjacentParametersOfTheSameTypeCheck::storeOptions( + ClangTidyOptions::OptionMap &Opts) { + Options.store(Opts, "MinimumLength", MinimumLength); + Options.store(Opts, "IgnoredNames", + utils::options::serializeStringList(IgnoredParamNames)); + Options.store(Opts, "IgnoredTypes", + utils::options::serializeStringList(IgnoredParamTypes)); + Options.store(Opts, "CVRMixPossible", CVRMixPossible); +} + +void CppcoreguidelinesAvoidAdjacentParametersOfTheSameTypeCheck:: + registerMatchers(MatchFinder *Finder) { + const LangOptions &Opts = getLangOpts(); + if (Opts.ObjC) + // FIXME: Revise how this check could operate on ObjC code. + return; + + Finder->addMatcher(functionDecl( + // Only report for definitions as the user only has + // chance to fix if they can change the def. + isDefinition(), hasAtLeastNumParams(MinimumLength), + unless(ast_matchers::isTemplateInstantiation())) + .bind("fun"), + this); + + Finder->addMatcher(functionDecl( + // Only report for definitions as the user only has + // chance to fix if they can change the def. + isDefinition(), hasAtLeastNumParams(MinimumLength), + isExplicitTemplateSpecialization()) + .bind("fun"), + this); +} + +/// Returns whether the given Mixup, when diagnosed, should elaborate the type +/// of the arguments involved. +static bool NeedsToPrintType(const Mixup &M) { + return M.Flags & (MIXUP_Typedef | MIXUP_RefBind | MIXUP_CVR); +} + +void CppcoreguidelinesAvoidAdjacentParametersOfTheSameTypeCheck::check( + const MatchFinder::MatchResult &Result) { + const auto *Fun = Result.Nodes.getNodeAs("fun"); + + unsigned int ParamMixRangeStartIdx = 0; + const unsigned int NumArgs = Fun->getNumParams(); + + while (ParamMixRangeStartIdx < NumArgs) { + if (isIgnored(Fun->getParamDecl(ParamMixRangeStartIdx))) { + // If the current parameters's name or type name is ignored, don't try + // creating a range from it. + ++ParamMixRangeStartIdx; + continue; + } + + MixableAdjacentParamRange MixingRange = + BuildMixableParameterRange(*this, Fun, ParamMixRangeStartIdx); + assert(MixingRange.NumParamsChecked > 0 && "Ensure continuity!"); + ParamMixRangeStartIdx += MixingRange.NumParamsChecked; + if (MixingRange.NumParamsChecked < MinimumLength) + continue; + + // A function with an adjacent argument range of sufficient length found. + std::string FunName = Fun->getDeclName().getAsString(); + if (FunName.empty()) + FunName = ""; + + const PrintingPolicy &PP{Fun->getASTContext().getLangOpts()}; + std::string MainParmTypeAsWritten = + MixingRange.getFirstParm()->getType().getAsString(PP); + + bool HasAnyTypePrint = llvm::any_of(MixingRange.Mixups, NeedsToPrintType); + + { + const ParmVarDecl *RangeFirst = MixingRange.getFirstParm(); + const ParmVarDecl *RangeLast = MixingRange.getLastParm(); + + { + StringRef MainDiagnostic; + if (HasAnyTypePrint) + MainDiagnostic = "%0 adjacent parameters for '%1' of similar type " + "are easily swapped " + "by mistake"; + else + MainDiagnostic = "%0 adjacent parameters for '%1' of similar type " + "('%2') are easily " + "swapped by mistake"; + + auto Diag = diag(RangeFirst->getOuterLocStart(), MainDiagnostic) + << static_cast(MixingRange.NumParamsChecked) + << FunName; + if (!HasAnyTypePrint) + Diag << MainParmTypeAsWritten; + } + + // Unfortunately, undersquiggly highlights of ranges is not supported by + // ClangTidyDiagnosticConsumer without a fake fixit... + diag(RangeFirst->getLocation(), + "the first parameter in this range is '%0'", DiagnosticIDs::Note) + << (!RangeFirst->getName().empty() ? RangeFirst->getName() + : ""); + diag(RangeLast->getLocation(), "the last parameter in this range is '%0'", + DiagnosticIDs::Note) + << (!RangeLast->getName().empty() ? RangeLast->getName() + : ""); + } + + llvm::SmallPtrSet TypedefResolutionPrintedForParm; + llvm::SmallPtrSet CVRNotePrintedForParm; + for (const Mixup &M : MixingRange.Mixups) { + assert(M.Flags >= MIXUP_Trivial && "Too low bits in mixup type."); + // For MIXUP_Trivial no extra diagnostics required. + + std::string FirstParmType, SecondParmType; + if (NeedsToPrintType(M)) { + // Typedefs, and reference binds might result in the type of a variable + // printed in the diagnostic, so we have to prepare it. + FirstParmType = TypeNameAsString(M.First->getType(), PP); + SecondParmType = TypeNameAsString(M.Second->getType(), PP); + } + + if (NeedsToPrintType(M)) { + if (M.Flags & MIXUP_Typedef) { + // FIXME: Don't emit the typedef note for the parameter that isn't + // actually a typedef. + if (!TypedefResolutionPrintedForParm.count(M.First)) { + diag(M.First->getOuterLocStart(), + "after resolving type aliases, type of parameter '%0' is '%1'", + DiagnosticIDs::Note) + << M.First->getName() << FirstParmType; + TypedefResolutionPrintedForParm.insert(M.First); + } + + if (!TypedefResolutionPrintedForParm.count(M.Second)) { + diag(M.Second->getOuterLocStart(), + "after resolving type aliases, type of parameter '%0' is '%1'", + DiagnosticIDs::Note) + << M.Second->getName() << SecondParmType; + TypedefResolutionPrintedForParm.insert(M.Second); + } + } + + if (M.Flags & (MIXUP_RefBind | MIXUP_CVR)) { + if (!CVRNotePrintedForParm.count(M.Second)) { + diag(M.Second->getOuterLocStart(), + "at a call site, '%0' might bind with same force as '%1'", + DiagnosticIDs::Note) + << SecondParmType << MainParmTypeAsWritten; + CVRNotePrintedForParm.insert(M.Second); + } + } + } + } + } +} + +} // namespace experimental +} // namespace tidy +} // namespace clang Index: clang-tools-extra/clang-tidy/experimental/ExperimentalTidyModule.cpp =================================================================== --- clang-tools-extra/clang-tidy/experimental/ExperimentalTidyModule.cpp +++ clang-tools-extra/clang-tidy/experimental/ExperimentalTidyModule.cpp @@ -9,6 +9,7 @@ #include "../ClangTidy.h" #include "../ClangTidyModule.h" #include "../ClangTidyModuleRegistry.h" +#include "CppcoreguidelinesAvoidAdjacentParametersOfTheSameTypeCheck.h" namespace clang { namespace tidy { @@ -19,7 +20,10 @@ class ExperimentalModule : public ClangTidyModule { public: void addCheckFactories(ClangTidyCheckFactories &CheckFactories) override { - + CheckFactories.registerCheck< + CppcoreguidelinesAvoidAdjacentParametersOfTheSameTypeCheck>( + "experimental-cppcoreguidelines-avoid-adjacent-parameters-of-the-same-" + "type"); } }; Index: clang-tools-extra/docs/ReleaseNotes.rst =================================================================== --- clang-tools-extra/docs/ReleaseNotes.rst +++ clang-tools-extra/docs/ReleaseNotes.rst @@ -75,6 +75,7 @@ New checks ^^^^^^^^^^ + - New :doc:`cppcoreguidelines-avoid-non-const-global-variables ` check. Finds non-const global variables as described in check I.2 of C++ Core @@ -113,6 +114,14 @@ Flags use of the `C` standard library functions ``memset``, ``memcpy`` and ``memcmp`` and similar derivatives on non-trivial types. +- New :doc:`experimental-cppcoreguidelines-avoid-adjacent-parameters-of-the-same-type + ` + check. + + Finds function definitions where parameters of the same type follow each other + directly, making call sites prone to calling the function with swapped or badly + ordered arguments. + - New :doc:`llvmlibc-implementation-in-namespace ` check. Index: clang-tools-extra/docs/clang-tidy/checks/experimental-cppcoreguidelines-avoid-adjacent-parameters-of-the-same-type.rst =================================================================== --- /dev/null +++ clang-tools-extra/docs/clang-tidy/checks/experimental-cppcoreguidelines-avoid-adjacent-parameters-of-the-same-type.rst @@ -0,0 +1,150 @@ +.. title:: clang-tidy - experimental-cppcoreguidelines-avoid-adjacent-parameters-of-the-same-type + +experimental-cppcoreguidelines-avoid-adjacent-parameters-of-the-same-type +========================================================================= + +Finds function definitions where parameters of the same type follow each other +directly, making call sites prone to calling the function with swapped or badly +ordered arguments. + +The rule is part of the "Interfaces" profile of the C++ Core Guidelines, see +`I.24 `_. + +.. code-block:: c++ + + void draw_point(int X, int Y) {} + FILE *open_path(const char *Dir, const char *Name, Flags Mode) {} + +A potential call like ``draw(-2, 5)`` or +``open_path("a.txt", "/tmp", Read | Write)`` is perfectly legal from the +language's perspective, but might not be what the developer of the function +intended. + +The C++ Core Guidelines recommend using more elaborate types for parameters, +such as + +.. code-block:: c++ + + struct Coords2D { int X; int Y; }; + void draw_point(const Coords2D Point) {} + + FILE *open_path(const Path &Dir, const Filename &Name, Flags Mode) {} + +Due to the elaborate refactoring and API-breaking requirements posed by fixing +the issues diagnosed by this check, **no automatic fix-its** are proposed. + +Currently, functions that take pairs of iterators (where the suggested option +would be moving to, e.g., the +`Ranges `_ library) are hard-coded to +not produce a diagnostic. + + - The following *parameter names* and their Uppercase-initial variants are + ignored: + ``iterator``, ``begin``, ``end``, ``first``, ``last``. + - The following *type names* and their lowercase-initial variants (for types + of any parameters found) are ignored: + ``It``, ``Iterator``, ``InputIt``, ``ForwardIt``, ``BidirIt``, + ``const_iterator``, ``ConstIterator`` + +Options +------- + +.. option:: MinimumLength + + The minimum length of the adjacent parameter sequence required before a + diagnostic is emitted. + Defaults to `2`. + Should be any integer number that's at least 2. + If `0` or `1` is given, the check will run as if `2` was specified. + +.. option:: IgnoredNames + + The list of parameter names that should not be considered part of a mixable + parameter range by the checker. + Parameters bearing ignored names will be skipped. + The value should be a ``;``-separated list of names. + The option is case-sensitive! + + By default, the following, and their Uppercase-initial variants are ignored: + ``iterator``, ``begin``, ``end``, ``first``, ``last``. + +.. option:: IgnoredTypes + + The list of parameter type suffices that should not be considered part of a + mixable parameter range by the checker. + Parameters of type bearing an ignored type name (or which suffix is found in + the list) will be skipped. + The value should be a ``;``-separated list of names. + The option is case-sensitive! + + By default, the following, and their lowercase-initial variants are ignored: + ``It``, ``Iterator``, ``InputIt``, ``ForwardIt``, ``BidirIt``, + ``const_iterator``, ``ConstIterator``. + +.. option:: CVRMixPossible + + Whether to consider parameters of type ``T`` and the qualified + ``const T``/``volatile T`` counterpart forming a common "adjacent + parameter" sequence. + If `0` (default value), the check assumes such parameters cannot be mixed + up at a potential call site. + A non-zero value turns this assumption **off**. + A non-zero value is generally expected to produce a broader set of results. + + The following example will not produce a diagnostic unless + *CVRMixPossible* is set to a non-zero value. + + .. code-block:: c++ + + struct T {}; + void f(T *tp, const T *ctp) {} + +Limitations +----------- + +This check does not investigate functions that were instantiated from function +templates. +As such, only the primary template definitions and explicit specialisations are +checked. + +Furthermore, this check is not able to find if some parameters of +template-dependent types might end up referring to the same type in the end, +creating an adjacent range that could be mixed up. + +The following example will not be warned about. + +.. code-block:: c++ + + template + struct list_node { + T value; + list_node *prev, *next; + }; + + template + list_node *create(const T& value, + const list_node *prev, + const list_node *next) { /* ... */ } + +The following example will not be warned about, despite it should be, as the +last parameters to the function in the example are of type ``const T &``. + +.. code-block:: c++ + + template + struct vector { + typedef T element_type; + typedef const T const_element_type; + typedef T & reference_type; + typedef const T & const_reference_type; + }; + + // Finds the longest occurrence's length between elements "RightEnd" + // and "LeftBegin". For example, in a vector of + // <1, 2, 3, 4, 2, 8, 8, 8, 8, 4> + // findLongestOccurrenceLengthBetween(2, 4) should return 4 (the 4 8s). + template + unsigned int findLongestOccurrenceLengthBetween( + const vector & Vector, + typename vector::const_reference_type RightEnd, + const typename vector::element_type & LeftBegin) { /* ... */ } Index: clang-tools-extra/docs/clang-tidy/checks/list.rst =================================================================== --- clang-tools-extra/docs/clang-tidy/checks/list.rst +++ clang-tools-extra/docs/clang-tidy/checks/list.rst @@ -152,6 +152,7 @@ `cppcoreguidelines-special-member-functions `_, `darwin-avoid-spinlock `_, `darwin-dispatch-once-nonstatic `_, "Yes" + `experimental-cppcoreguidelines-avoid-adjacent-parameters-of-the-same-type `_, `fuchsia-default-arguments-calls `_, `fuchsia-default-arguments-declarations `_, "Yes" `fuchsia-multiple-inheritance `_, Index: clang-tools-extra/test/clang-tidy/checkers/experimental-cppcoreguidelines-avoid-adjacent-parameters-of-the-same-type-cvr-on.cpp =================================================================== --- /dev/null +++ clang-tools-extra/test/clang-tidy/checkers/experimental-cppcoreguidelines-avoid-adjacent-parameters-of-the-same-type-cvr-on.cpp @@ -0,0 +1,492 @@ +// RUN: %check_clang_tidy %s \ +// RUN: experimental-cppcoreguidelines-avoid-adjacent-parameters-of-the-same-type %t \ +// RUN: -config='{CheckOptions: [ \ +// RUN: {key: experimental-cppcoreguidelines-avoid-adjacent-parameters-of-the-same-type.CVRMixPossible, value: 1} \ +// RUN: ]}' -- + +void library(void *vp, void *vp2, void *vp3, int n, int m); +// NO-WARN: The user has no chance to change only declared (usually library) +// functions, so no diagnostic is made. + +struct T {}; + +void create(T **out_t) {} // NO-WARN + +void unnamed_param(T *p, T *) {} +// CHECK-MESSAGES: :[[@LINE-1]]:20: warning: 2 adjacent parameters for 'unnamed_param' of similar type ('T *') are easily swapped by mistake [experimental-cppcoreguidelines-avoid-adjacent-parameters-of-the-same-type] +// CHECK-MESSAGES: :[[@LINE-2]]:23: note: the first parameter in this range is 'p' +// CHECK-MESSAGES: :[[@LINE-3]]:29: note: the last parameter in this range is '' + +void copy(T *p, T *q, int n) {} +// CHECK-MESSAGES: :[[@LINE-1]]:11: warning: 2 adjacent parameters for 'copy' of similar type ('T *') are +// CHECK-MESSAGES: :[[@LINE-2]]:14: note: the first parameter in this range is 'p' +// CHECK-MESSAGES: :[[@LINE-3]]:20: note: the last parameter in this range is 'q' + +void mov(T *dst, const T *src) {} +// CHECK-MESSAGES: :[[@LINE-1]]:10: warning: 2 adjacent parameters for 'mov' of similar type are +// CHECK-MESSAGES: :[[@LINE-2]]:13: note: the first parameter in this range is 'dst' +// CHECK-MESSAGES: :[[@LINE-3]]:27: note: the last parameter in this range is 'src' +// CHECK-MESSAGES: :[[@LINE-4]]:18: note: at a call site, 'const T *' might bind with same force as 'T *' + +void compare01(T t1, T t2) {} +// CHECK-MESSAGES: :[[@LINE-1]]:16: warning: 2 adjacent parameters for 'compare01' of similar type ('T') are +// CHECK-MESSAGES: :[[@LINE-2]]:18: note: the first parameter in this range is 't1' +// CHECK-MESSAGES: :[[@LINE-3]]:24: note: the last parameter in this range is 't2' + +void compare02(const T t1, T t2) {} +// CHECK-MESSAGES: :[[@LINE-1]]:16: warning: 2 adjacent parameters for 'compare02' of similar type are +// CHECK-MESSAGES: :[[@LINE-2]]:24: note: the first parameter in this range is 't1' +// CHECK-MESSAGES: :[[@LINE-3]]:30: note: the last parameter in this range is 't2' +// CHECK-MESSAGES: :[[@LINE-4]]:28: note: at a call site, 'T' might bind with same force as 'const T' + +void compare03(T t1, const T t2) {} +// CHECK-MESSAGES: :[[@LINE-1]]:16: warning: 2 adjacent parameters for 'compare03' of similar type are +// CHECK-MESSAGES: :[[@LINE-2]]:18: note: the first parameter in this range is 't1' +// CHECK-MESSAGES: :[[@LINE-3]]:30: note: the last parameter in this range is 't2' +// CHECK-MESSAGES: :[[@LINE-4]]:22: note: at a call site, 'const T' might bind with same force as 'T' + +void compare04(const T &t1, T t2) {} +// CHECK-MESSAGES: :[[@LINE-1]]:16: warning: 2 adjacent parameters for 'compare04' of similar type are +// CHECK-MESSAGES: :[[@LINE-2]]:25: note: the first parameter in this range is 't1' +// CHECK-MESSAGES: :[[@LINE-3]]:31: note: the last parameter in this range is 't2' +// CHECK-MESSAGES: :[[@LINE-4]]:29: note: at a call site, 'T' might bind with same force as 'const T &' + +void compare05(T t1, const T &t2) {} +// CHECK-MESSAGES: :[[@LINE-1]]:16: warning: 2 adjacent parameters for 'compare05' of similar type are +// CHECK-MESSAGES: :[[@LINE-2]]:18: note: the first parameter in this range is 't1' +// CHECK-MESSAGES: :[[@LINE-3]]:31: note: the last parameter in this range is 't2' +// CHECK-MESSAGES: :[[@LINE-4]]:22: note: at a call site, 'const T &' might bind with same force as 'T' + +void compare06(const T &t1, const T &t2) {} +// CHECK-MESSAGES: :[[@LINE-1]]:16: warning: 2 adjacent parameters for 'compare06' of similar type ('const T &') are +// CHECK-MESSAGES: :[[@LINE-2]]:25: note: the first parameter in this range is 't1' +// CHECK-MESSAGES: :[[@LINE-3]]:38: note: the last parameter in this range is 't2' + +void compare07(const T &t1, const T t2) {} +// CHECK-MESSAGES: :[[@LINE-1]]:16: warning: 2 adjacent parameters for 'compare07' of similar type are +// CHECK-MESSAGES: :[[@LINE-2]]:25: note: the first parameter in this range is 't1' +// CHECK-MESSAGES: :[[@LINE-3]]:37: note: the last parameter in this range is 't2' +// CHECK-MESSAGES: :[[@LINE-4]]:29: note: at a call site, 'const T' might bind with same force as 'const T &' + +void compare08(const T t1, const T &t2) {} +// CHECK-MESSAGES: :[[@LINE-1]]:16: warning: 2 adjacent parameters for 'compare08' of similar type are +// CHECK-MESSAGES: :[[@LINE-2]]:24: note: the first parameter in this range is 't1' +// CHECK-MESSAGES: :[[@LINE-3]]:37: note: the last parameter in this range is 't2' +// CHECK-MESSAGES: :[[@LINE-4]]:28: note: at a call site, 'const T &' might bind with same force as 'const T' + +// In general, rvalue references and copies are hard to mess up at call sites. +void compare09(T &&t1, T t2) {} // NO-WARN. +void compare0A(T t1, T &&t2) {} // NO-WARN. + +void compare0B(T &&t1, T &&t2) {} +// CHECK-MESSAGES: :[[@LINE-1]]:16: warning: 2 adjacent parameters for 'compare0B' of similar type ('T &&') are +// CHECK-MESSAGES: :[[@LINE-2]]:20: note: the first parameter in this range is 't1' +// CHECK-MESSAGES: :[[@LINE-3]]:28: note: the last parameter in this range is 't2' + +struct U {}; +void compare0C(T t, U u) {} // NO-WARN. +void compare0D(const T t, U u) {} // NO-WARN. +void compare0E(T t, const U u) {} // NO-WARN. +void compare0F(const U &u, T t) {} // NO-WARN. +void compare10(T t, const U &u) {} // NO-WARN. +void compare11(const T &t, const U &u) {} // NO-WARN. +void compare12(const T &t, const U u) {} // NO-WARN. +void compare13(const U u, const T &t) {} // NO-WARN. +void compare14(T &&t, U u) {} // NO-WARN. +void compare15(T t, U &&u) {} // NO-WARN. + +void multi_bind(T t, const T t2, const T &t3, T &&t4) {} +// CHECK-MESSAGES: :[[@LINE-1]]:17: warning: 3 adjacent parameters for 'multi_bind' of similar type are +// CHECK-MESSAGES: :[[@LINE-2]]:19: note: the first parameter in this range is 't' +// CHECK-MESSAGES: :[[@LINE-3]]:43: note: the last parameter in this range is 't3' +// CHECK-MESSAGES: :[[@LINE-4]]:22: note: at a call site, 'const T' might bind with same force as 'T' +// CHECK-MESSAGES: :[[@LINE-5]]:34: note: at a call site, 'const T &' might bind with same force as 'T' + +void ptr_quals(int *ip, const int *cip, volatile int *vip, int *const ipc, volatile int *volatile vipv) {} +// CHECK-MESSAGES: :[[@LINE-1]]:16: warning: 5 adjacent parameters for 'ptr_quals' of similar type are +// CHECK-MESSAGES: :[[@LINE-2]]:21: note: the first parameter in this range is 'ip' +// CHECK-MESSAGES: :[[@LINE-3]]:99: note: the last parameter in this range is 'vipv' +// CHECK-MESSAGES: :[[@LINE-4]]:25: note: at a call site, 'const int *' might bind with same force as 'int *' +// CHECK-MESSAGES: :[[@LINE-5]]:41: note: at a call site, 'volatile int *' might bind with same force as 'int *' +// CHECK-MESSAGES: :[[@LINE-6]]:60: note: at a call site, 'int * const' might bind with same force as 'int *' +// CHECK-MESSAGES: :[[@LINE-7]]:76: note: at a call site, 'volatile int * volatile' might bind with same force as 'int *' + +void value_quals(T t, const T ct, volatile T vt, const volatile T cvt) {} +// CHECK-MESSAGES: :[[@LINE-1]]:18: warning: 4 adjacent parameters for 'value_quals' of similar type are +// CHECK-MESSAGES: :[[@LINE-2]]:20: note: the first parameter in this range is 't' +// CHECK-MESSAGES: :[[@LINE-3]]:67: note: the last parameter in this range is 'cvt' +// CHECK-MESSAGES: :[[@LINE-4]]:23: note: at a call site, 'const T' might bind with same force as 'T' +// CHECK-MESSAGES: :[[@LINE-5]]:35: note: at a call site, 'volatile T' might bind with same force as 'T' +// CHECK-MESSAGES: :[[@LINE-6]]:50: note: at a call site, 'const volatile T' might bind with same force as 'T' + +void value_ptr_quals(T *tp, const T *ctp, volatile T *vtp, const volatile T *cvtp, T *const tpc, T *volatile tpv) {} +// CHECK-MESSAGES: :[[@LINE-1]]:22: warning: 6 adjacent parameters for 'value_ptr_quals' of similar type are +// CHECK-MESSAGES: :[[@LINE-2]]:25: note: the first parameter in this range is 'tp' +// CHECK-MESSAGES: :[[@LINE-3]]:110: note: the last parameter in this range is 'tpv' +// CHECK-MESSAGES: :[[@LINE-4]]:29: note: at a call site, 'const T *' might bind with same force as 'T *' +// CHECK-MESSAGES: :[[@LINE-5]]:43: note: at a call site, 'volatile T *' might bind with same force as 'T *' +// CHECK-MESSAGES: :[[@LINE-6]]:60: note: at a call site, 'const volatile T *' might bind with same force as 'T *' +// CHECK-MESSAGES: :[[@LINE-7]]:84: note: at a call site, 'T * const' might bind with same force as 'T *' +// CHECK-MESSAGES: :[[@LINE-8]]:98: note: at a call site, 'T * volatile' might bind with same force as 'T *' + +typedef int Numeral; +void set_lerped(const Numeral *const min, + const Numeral *const max, + Numeral *const value) {} +// CHECK-MESSAGES: :[[@LINE-3]]:17: warning: 3 adjacent parameters for 'set_lerped' of similar type are +// CHECK-MESSAGES: :[[@LINE-4]]:38: note: the first parameter in this range is 'min' +// CHECK-MESSAGES: :[[@LINE-3]]:32: note: the last parameter in this range is 'value' +// CHECK-MESSAGES: :[[@LINE-4]]:17: note: at a call site, 'int * const' might bind with same force as 'const Numeral *const' + +typedef int I1; +typedef int I2; +void multi_through_typedef(I1 i1, I2 i2) {} +// CHECK-MESSAGES: :[[@LINE-1]]:28: warning: 2 adjacent parameters for 'multi_through_typedef' of similar type are +// CHECK-MESSAGES: :[[@LINE-2]]:31: note: the first parameter in this range is 'i1' +// CHECK-MESSAGES: :[[@LINE-3]]:38: note: the last parameter in this range is 'i2' +// CHECK-MESSAGES: :[[@LINE-4]]:28: note: after resolving type aliases, type of parameter 'i1' is 'int' +// CHECK-MESSAGES: :[[@LINE-5]]:35: note: after resolving type aliases, type of parameter 'i2' is 'int' + +void multi_ptr_through_typedef(I1 *p1, I2 *p2) {} +// CHECK-MESSAGES: :[[@LINE-1]]:32: warning: 2 adjacent parameters for 'multi_ptr_through_typedef' of similar type are +// CHECK-MESSAGES: :[[@LINE-2]]:36: note: the first parameter in this range is 'p1' +// CHECK-MESSAGES: :[[@LINE-3]]:44: note: the last parameter in this range is 'p2' +// CHECK-MESSAGES: :[[@LINE-4]]:32: note: after resolving type aliases, type of parameter 'p1' is 'int *' +// CHECK-MESSAGES: :[[@LINE-5]]:40: note: after resolving type aliases, type of parameter 'p2' is 'int *' + +void typedef_and_realtype_ptr_const(int *p1, const I1 *p2) {} +// CHECK-MESSAGES: :[[@LINE-1]]:37: warning: 2 adjacent parameters for 'typedef_and_realtype_ptr_const' of similar type are +// CHECK-MESSAGES: :[[@LINE-2]]:42: note: the first parameter in this range is 'p1' +// CHECK-MESSAGES: :[[@LINE-3]]:56: note: the last parameter in this range is 'p2' +// CHECK-MESSAGES: :[[@LINE-4]]:37: note: after resolving type aliases, type of parameter 'p1' is 'int *' +// CHECK-MESSAGES: :[[@LINE-5]]:46: note: after resolving type aliases, type of parameter 'p2' is 'const int *' +// CHECK-MESSAGES: :[[@LINE-6]]:46: note: at a call site, 'const int *' might bind with same force as 'int *' + +void typedef_ptr_const(I1 *p1, const I1 *p2) {} +// CHECK-MESSAGES: :[[@LINE-1]]:24: warning: 2 adjacent parameters for 'typedef_ptr_const' of similar type are +// CHECK-MESSAGES: :[[@LINE-2]]:28: note: the first parameter in this range is 'p1' +// CHECK-MESSAGES: :[[@LINE-3]]:42: note: the last parameter in this range is 'p2' +// CHECK-MESSAGES: :[[@LINE-4]]:32: note: at a call site, 'const int *' might bind with same force as 'I1 *' + +typedef I2 myInt; +typedef I2 myOtherInt; +void typedef_chain(myInt mi, myOtherInt moi) {} +// CHECK-MESSAGES: :[[@LINE-1]]:20: warning: 2 adjacent parameters for 'typedef_chain' of similar type are +// CHECK-MESSAGES: :[[@LINE-2]]:26: note: the first parameter in this range is 'mi' +// CHECK-MESSAGES: :[[@LINE-3]]:41: note: the last parameter in this range is 'moi' +// CHECK-MESSAGES: :[[@LINE-4]]:20: note: after resolving type aliases, type of parameter 'mi' is 'int' +// CHECK-MESSAGES: :[[@LINE-5]]:30: note: after resolving type aliases, type of parameter 'moi' is 'int' + +void bind_power_and_typedef(int Read, const myOtherInt &Write) {} +// CHECK-MESSAGES: :[[@LINE-1]]:29: warning: 2 adjacent parameters for 'bind_power_and_typedef' of similar type are +// CHECK-MESSAGES: :[[@LINE-2]]:33: note: the first parameter in this range is 'Read' +// CHECK-MESSAGES: :[[@LINE-3]]:57: note: the last parameter in this range is 'Write' +// CHECK-MESSAGES: :[[@LINE-4]]:39: note: after resolving type aliases, type of parameter 'Write' is 'const int &' +// CHECK-MESSAGES: :[[@LINE-5]]:39: note: at a call site, 'const int &' might bind with same force as 'int' + +typedef myOtherInt *IP; +typedef const IP &cipr; +void bind_power_and_multi_layer_typedef(int *RP, cipr WP) {} +// CHECK-MESSAGES: :[[@LINE-1]]:41: warning: 2 adjacent parameters for 'bind_power_and_multi_layer_typedef' of similar type are +// CHECK-MESSAGES: :[[@LINE-2]]:46: note: the first parameter in this range is 'RP' +// CHECK-MESSAGES: :[[@LINE-3]]:55: note: the last parameter in this range is 'WP' +// CHECK-MESSAGES: :[[@LINE-4]]:50: note: after resolving type aliases, type of parameter 'WP' is 'const int * &' +// CHECK-MESSAGES: :[[@LINE-5]]:50: note: at a call site, 'const int * &' might bind with same force as 'int *' + +void entirely_different(int i, long l) {} // NO-WARN. + +struct StringRef { + char *Data; +}; +typedef bool Toggle; + +void multi_pack(StringRef Path, StringRef Name, int Size, Toggle Read, Toggle Write) {} +// CHECK-MESSAGES: :[[@LINE-1]]:17: warning: 2 adjacent parameters for 'multi_pack' of similar type ('StringRef') are +// CHECK-MESSAGES: :[[@LINE-2]]:27: note: the first parameter in this range is 'Path' +// CHECK-MESSAGES: :[[@LINE-3]]:43: note: the last parameter in this range is 'Name' +// CHECK-MESSAGES: :[[@LINE-4]]:59: warning: 2 adjacent parameters for 'multi_pack' of similar type ('Toggle') are +// CHECK-MESSAGES: :[[@LINE-5]]:66: note: the first parameter in this range is 'Read' +// CHECK-MESSAGES: :[[@LINE-6]]:79: note: the last parameter in this range is 'Write' + +void protochain(int, int, int); +void protochain(int i, int, int); +void protochain(int, int j, int); +void protochain(int i, int j, int k) {} +// CHECK-MESSAGES: :[[@LINE-1]]:17: warning: 3 adjacent parameters for 'protochain' of similar type ('int') are +// CHECK-MESSAGES: :[[@LINE-2]]:21: note: the first parameter in this range is 'i' +// CHECK-MESSAGES: :[[@LINE-3]]:35: note: the last parameter in this range is 'k' + +void protochain2(I1, I2, myInt); +void protochain2(I1 i, I2, myInt); +void protochain2(I1, I2 j, int); +void protochain2(I1 i, I2 j, myOtherInt k) {} +// CHECK-MESSAGES: :[[@LINE-1]]:18: warning: 3 adjacent parameters for 'protochain2' of similar type are +// CHECK-MESSAGES: :[[@LINE-2]]:21: note: the first parameter in this range is 'i' +// CHECK-MESSAGES: :[[@LINE-3]]:41: note: the last parameter in this range is 'k' +// CHECK-MESSAGES: :[[@LINE-4]]:18: note: after resolving type aliases, type of parameter 'i' is 'int' +// CHECK-MESSAGES: :[[@LINE-5]]:24: note: after resolving type aliases, type of parameter 'j' is 'int' +// CHECK-MESSAGES: :[[@LINE-6]]:30: note: after resolving type aliases, type of parameter 'k' is 'int' + +void defined(int a, int b); +void defined(int i, int j) {} +// CHECK-MESSAGES: :[[@LINE-1]]:14: warning: 2 adjacent parameters for 'defined' of similar type ('int') are +// CHECK-MESSAGES: :[[@LINE-2]]:18: note: the first parameter in this range is 'i' +// CHECK-MESSAGES: :[[@LINE-3]]:25: note: the last parameter in this range is 'j' + +template +void ptr_pair(T *p, T *q) {} +// CHECK-MESSAGES: :[[@LINE-1]]:15: warning: 2 adjacent parameters for 'ptr_pair' of similar type ('T *') are +// CHECK-MESSAGES: :[[@LINE-2]]:18: note: the first parameter in this range is 'p' +// CHECK-MESSAGES: :[[@LINE-3]]:24: note: the last parameter in this range is 'q' + +template +void cptr_pair(const T *p, const T *q) {} +// CHECK-MESSAGES: :[[@LINE-1]]:16: warning: 2 adjacent parameters for 'cptr_pair' of similar type ('const T *') are +// CHECK-MESSAGES: :[[@LINE-2]]:25: note: the first parameter in this range is 'p' +// CHECK-MESSAGES: :[[@LINE-3]]:37: note: the last parameter in this range is 'q' + +void ref_and_typedef(int i, I2 i2) {} +// CHECK-MESSAGES: :[[@LINE-1]]:22: warning: 2 adjacent parameters for 'ref_and_typedef' of similar type are +// CHECK-MESSAGES: :[[@LINE-2]]:26: note: the first parameter in this range is 'i' +// CHECK-MESSAGES: :[[@LINE-3]]:32: note: the last parameter in this range is 'i2' +// CHECK-MESSAGES: :[[@LINE-4]]:22: note: after resolving type aliases, type of parameter 'i' is 'int' +// CHECK-MESSAGES: :[[@LINE-5]]:29: note: after resolving type aliases, type of parameter 'i2' is 'int' + +void ref_and_typedef2(I1 i, int i2) {} +// CHECK-MESSAGES: :[[@LINE-1]]:23: warning: 2 adjacent parameters for 'ref_and_typedef2' of similar type are +// CHECK-MESSAGES: :[[@LINE-2]]:26: note: the first parameter in this range is 'i' +// CHECK-MESSAGES: :[[@LINE-3]]:33: note: the last parameter in this range is 'i2' +// CHECK-MESSAGES: :[[@LINE-4]]:23: note: after resolving type aliases, type of parameter 'i' is 'int' +// CHECK-MESSAGES: :[[@LINE-5]]:29: note: after resolving type aliases, type of parameter 'i2' is 'int' + +int strcmp(const char *A, const char *B) {} +// CHECK-MESSAGES: :[[@LINE-1]]:12: warning: 2 adjacent parameters for 'strcmp' of similar type ('const char *') are +// CHECK-MESSAGES: :[[@LINE-2]]:24: note: the first parameter in this range is 'A' +// CHECK-MESSAGES: :[[@LINE-3]]:39: note: the last parameter in this range is 'B' + +using str = char *; +using strliteral = const char *; +int str_compare(strliteral a, strliteral b) {} +// CHECK-MESSAGES: :[[@LINE-1]]:17: warning: 2 adjacent parameters for 'str_compare' of similar type ('strliteral') are +// CHECK-MESSAGES: :[[@LINE-2]]:28: note: the first parameter in this range is 'a' +// CHECK-MESSAGES: :[[@LINE-3]]:42: note: the last parameter in this range is 'b' + +void append(str s, char *s2) {} +// CHECK-MESSAGES: :[[@LINE-1]]:13: warning: 2 adjacent parameters for 'append' of similar type are +// CHECK-MESSAGES: :[[@LINE-2]]:17: note: the first parameter in this range is 's' +// CHECK-MESSAGES: :[[@LINE-3]]:26: note: the last parameter in this range is 's2' +// CHECK-MESSAGES: :[[@LINE-4]]:13: note: after resolving type aliases, type of parameter 's' is 'char *' +// CHECK-MESSAGES: :[[@LINE-5]]:20: note: after resolving type aliases, type of parameter 's2' is 'char *' + +void append_lit(str s, strliteral s2) {} +// CHECK-MESSAGES: :[[@LINE-1]]:17: warning: 2 adjacent parameters for 'append_lit' of similar type are +// CHECK-MESSAGES: :[[@LINE-2]]:21: note: the first parameter in this range is 's' +// CHECK-MESSAGES: :[[@LINE-3]]:35: note: the last parameter in this range is 's2' +// CHECK-MESSAGES: :[[@LINE-4]]:17: note: after resolving type aliases, type of parameter 's' is 'char *' +// CHECK-MESSAGES: :[[@LINE-5]]:24: note: after resolving type aliases, type of parameter 's2' is 'const char *' + +void swap(str *a, char **b) {} +// CHECK-MESSAGES: :[[@LINE-1]]:11: warning: 2 adjacent parameters for 'swap' of similar type are +// CHECK-MESSAGES: :[[@LINE-2]]:16: note: the first parameter in this range is 'a' +// CHECK-MESSAGES: :[[@LINE-3]]:26: note: the last parameter in this range is 'b' +// CHECK-MESSAGES: :[[@LINE-4]]:11: note: after resolving type aliases, type of parameter 'a' is 'char * *' +// CHECK-MESSAGES: :[[@LINE-5]]:19: note: after resolving type aliases, type of parameter 'b' is 'char * *' + +template // 'I' as template name not on ignore list. +void shortthing(I a, I b) {} +// CHECK-MESSAGES: :[[@LINE-1]]:17: warning: 2 adjacent parameters for 'shortthing' of similar type ('I') are +// CHECK-MESSAGES: :[[@LINE-2]]:19: note: the first parameter in this range is 'a' +// CHECK-MESSAGES: :[[@LINE-3]]:24: note: the last parameter in this range is 'b' + +// FIXME: Suggest Ranges library alternative? +// NO-WARN: Ignore common type packs, like iterators. +template +void something(InputIt a, InputIt b) {} + +// NO-WARN: Ignore common names, like for iterators. +template +void find(InputIt first, InputIt last, const E &Element) {} + +template +struct vector { + T *Data; + unsigned N; + T operator[](unsigned Idx) { return *(Data + N); } + + typedef T *iterator; + typedef T element; + typedef const T &element_cref; +}; + +// NO-WARN: parameter has "iterator" as type. +template +typename vector::iterator find2( + typename vector::iterator from, + typename vector::iterator to, + const T &E) {} + +template +struct map { + K *Keys; + V *Values; + + struct map_iterator { + K *first; + V *second; + }; + typedef map_iterator iterator; +}; + +// NO-WARN: parameter has "iterator" as type. +template +typename map::iterator find_m( + typename map::iterator left, // Purposefully named as such so param + typename map::iterator right, // name doesn't match, only type name. + const K &k, const V &v) {} + +// NO-WARN: parameters are dependent types, it could be that for a certain +// 'T' they refer to (essentially) the same thing, and for other T, they don't. +// (Think of std::vector, perhaps?) +// It would be very expensive and tangy logic to deduce that in this small +// test example, one parameter is 'T' and the 'const T &'. +template +vector::iterator> findOccurrenceCases( + typename vector::element E, typename vector::element_cref ER) {} + +// FIXME: This case should be diagnosed (are `DependentNameTypes` equatable?) +template +vector> equalRangesBetween( + const typename vector::element &F, + typename vector::element L) {} + +// NO-WARN: Totally different types. +struct Decl; +struct Expr; +struct Stmt; +void HandleASTNodes(Decl *D, Expr *E, Stmt *S) {} + +// NO-WARN: Different template specialisations of the same template. +void concatenate_many_strings(StringRef Out, + vector InRef, + vector InStr, + vector InLiterals) {} + +// NO-WARN: Different template parameters. +template +int compare(const X &x, const Y &y, Comp C) { return C(x, y); } + +// WARN: Explicit specialisation's definition with similar parameters. +template +int compare(int x, int y, Comp C) { return C(x, y); } +// CHECK-MESSAGES: :[[@LINE-1]]:13: warning: 2 adjacent parameters for 'compare' of similar type ('int') are +// CHECK-MESSAGES: :[[@LINE-2]]:17: note: the first parameter in this range is 'x' +// CHECK-MESSAGES: :[[@LINE-3]]:24: note: the last parameter in this range is 'y' + +void compare_test() { + // NO-WARN: Function call, not defintion. + compare("A", "B", &strcmp); +} + +int definition_inside_another(vector V) { + struct Sum { + Sum(int _i, int _j) : i(_i), j(_j) {} + // CHECK-MESSAGES: :[[@LINE-1]]:9: warning: 2 adjacent parameters for 'Sum' of similar type ('int') are + // CHECK-MESSAGES: :[[@LINE-2]]:13: note: the first parameter in this range is '_i' + // CHECK-MESSAGES: :[[@LINE-3]]:21: note: the last parameter in this range is '_j' + + int operator()() const { return i + j; } + + int i, j; + }; + Sum S{V[0], V[1]}; + + return S(); +} + +void len3(const StringRef Drive, const StringRef Dirs, const StringRef FileName) {} +// CHECK-MESSAGES: :[[@LINE-1]]:11: warning: 3 adjacent parameters for 'len3' of similar type ('const StringRef') are +// CHECK-MESSAGES: :[[@LINE-2]]:27: note: the first parameter in this range is 'Drive' +// CHECK-MESSAGES: :[[@LINE-3]]:72: note: the last parameter in this range is 'FileName' + +void len4(const StringRef Drive, const StringRef Dirs, const StringRef FileName, const StringRef Extension) {} +// CHECK-MESSAGES: :[[@LINE-1]]:11: warning: 4 adjacent parameters for 'len4' of similar type ('const StringRef') are +// CHECK-MESSAGES: :[[@LINE-2]]:27: note: the first parameter in this range is 'Drive' +// CHECK-MESSAGES: :[[@LINE-3]]:98: note: the last parameter in this range is 'Extension' + +using MainT = int(int argc, strliteral argv, strliteral envp); +int call_two_programs(MainT one, MainT two) {} +// CHECK-MESSAGES: :[[@LINE-1]]:23: warning: 2 adjacent parameters for 'call_two_programs' of similar type ('MainT *') are +// CHECK-MESSAGES: :[[@LINE-2]]:29: note: the first parameter in this range is 'one' +// CHECK-MESSAGES: :[[@LINE-3]]:40: note: the last parameter in this range is 'two' + +using MainT2 = int(int argc, strliteral argv, strliteral envp); +int call_two_programs2(MainT p1, MainT2 p2) {} +// CHECK-MESSAGES: :[[@LINE-1]]:24: warning: 2 adjacent parameters for 'call_two_programs2' of similar type are +// CHECK-MESSAGES: :[[@LINE-2]]:30: note: the first parameter in this range is 'p1' +// CHECK-MESSAGES: :[[@LINE-3]]:41: note: the last parameter in this range is 'p2' +// CHECK-MESSAGES: :[[@LINE-4]]:24: note: after resolving type aliases, type of parameter 'p1' is 'int (int, const char *, const char *)' +// CHECK-MESSAGES: :[[@LINE-5]]:34: note: after resolving type aliases, type of parameter 'p2' is 'int (int, const char *, const char *)' + +typedef struct { + int i; +} Integer; +bool int_equals(Integer i1, Integer i2) { return i1.i == i2.i; } +// CHECK-MESSAGES: :[[@LINE-1]]:17: warning: 2 adjacent parameters for 'int_equals' of similar type ('Integer') are +// CHECK-MESSAGES: :[[@LINE-2]]:25: note: the first parameter in this range is 'i1' +// CHECK-MESSAGES: :[[@LINE-3]]:37: note: the last parameter in this range is 'i2' + +typedef enum { + Exclamation, + Question, + Dot +} Token; +bool tok_equals(Token t1, Token t2) {} +// CHECK-MESSAGES: :[[@LINE-1]]:17: warning: 2 adjacent parameters for 'tok_equals' of similar type ('Token') are +// CHECK-MESSAGES: :[[@LINE-2]]:23: note: the first parameter in this range is 't1' +// CHECK-MESSAGES: :[[@LINE-3]]:33: note: the last parameter in this range is 't2' + +enum E : char { + A = 'A', + B = 'B', + C = 'C' +}; + +// QUESTION: Should this warn? +// Conversion checks in general may not belong to this check in particular. +bool isSameLetter(E e, const char &c) {} + +template +struct doubly_linked_list_node { + T val; + doubly_linked_list_node *prev; + doubly_linked_list_node *next; +}; + +// FIXME: doubly_linked_list_node is a dependent type, and thus doesn't warn. +template +doubly_linked_list_node *list_insert(const T &element, + const doubly_linked_list_node *prev, + const doubly_linked_list_node *next) {} + +template +void fwd(Args &&... args, Args &&... args2) {} +// CHECK-MESSAGES: :[[@LINE-1]]:10: warning: 2 adjacent parameters for 'fwd' of similar type ('Args &&...') are +// CHECK-MESSAGES: :[[@LINE-2]]:21: note: the first parameter in this range is 'args' +// CHECK-MESSAGES: :[[@LINE-3]]:38: note: the last parameter in this range is 'args2' + +// Make sure the logic for the check doesn't crash... + +// Matched case from '/usr/include/c++/7/bits/gthr-default.h'. +typedef unsigned long int gthread_t; +static inline int gthread_create(gthread_t *threadid, + void *(*func)(void *), + void *args) {} + +// A modification of the above function that should warn. +int gthread_create_random(gthread_t *threadid, + void *(*func_1)(void *), + void *(*func_2)(void *), + void *args) {} +// CHECK-MESSAGES: :[[@LINE-3]]:27: warning: 2 adjacent parameters for 'gthread_create_random' of similar type ('void *(*)(void *)') are +// CHECK-MESSAGES: :[[@LINE-4]]:35: note: the first parameter in this range is 'func_1' +// CHECK-MESSAGES: :[[@LINE-4]]:35: note: the last parameter in this range is 'func_2' Index: clang-tools-extra/test/clang-tidy/checkers/experimental-cppcoreguidelines-avoid-adjacent-parameters-of-the-same-type-ignores.cpp =================================================================== --- /dev/null +++ clang-tools-extra/test/clang-tidy/checkers/experimental-cppcoreguidelines-avoid-adjacent-parameters-of-the-same-type-ignores.cpp @@ -0,0 +1,40 @@ +// RUN: %check_clang_tidy %s \ +// RUN: experimental-cppcoreguidelines-avoid-adjacent-parameters-of-the-same-type %t \ +// RUN: -config='{CheckOptions: [ \ +// RUN: {key: experimental-cppcoreguidelines-avoid-adjacent-parameters-of-the-same-type.IgnoredNames, value: "a;b"}, \ +// RUN: {key: experimental-cppcoreguidelines-avoid-adjacent-parameters-of-the-same-type.IgnoredTypes, value: "foo;bar"} \ +// RUN: ]}' -- + +struct iterator {}; + +// These names are not ignored. +void copy(iterator begin, iterator end) {} +// CHECK-MESSAGES: :[[@LINE-1]]:11: warning: 2 adjacent parameters for 'copy' of similar type ('iterator') are easily swapped by mistake [experimental-cppcoreguidelines-avoid-adjacent-parameters-of-the-same-type] +// CHECK-MESSAGES: :[[@LINE-2]]:20: note: the first parameter in this range is 'begin' +// CHECK-MESSAGES: :[[@LINE-3]]:36: note: the last parameter in this range is 'end' + +struct foo {}; +struct bar {}; + +// NO-WARN: Ignored parameter types. +void test01(foo x, foo y) {} +void test02(bar x, bar y) {} + +struct crowbar {}; +void test03(crowbar x, crowbar y) {} // NO-WARN: Ignored parameter type suffix. + +// NO-WARN: Ignored parameter names. +void test04(int a, int b) {} + +void len05(int x, int y, int z, int u, int v) {} +// CHECK-MESSAGES: :[[@LINE-1]]:12: warning: 5 adjacent parameters for 'len05' of similar type ('int') are +// CHECK-MESSAGES: :[[@LINE-2]]:16: note: the first parameter in this range is 'x' +// CHECK-MESSAGES: :[[@LINE-3]]:44: note: the last parameter in this range is 'v' + +void ignored_inside_range(int x, int y, int a, int u, int v) {} +// CHECK-MESSAGES: :[[@LINE-1]]:27: warning: 2 adjacent parameters for 'ignored_inside_range' of similar type ('int') are +// CHECK-MESSAGES: :[[@LINE-2]]:31: note: the first parameter in this range is 'x' +// CHECK-MESSAGES: :[[@LINE-3]]:38: note: the last parameter in this range is 'y' +// CHECK-MESSAGES: :[[@LINE-4]]:48: warning: 2 adjacent parameters for 'ignored_inside_range' of similar type ('int') are +// CHECK-MESSAGES: :[[@LINE-5]]:52: note: the first parameter in this range is 'u' +// CHECK-MESSAGES: :[[@LINE-6]]:59: note: the last parameter in this range is 'v' Index: clang-tools-extra/test/clang-tidy/checkers/experimental-cppcoreguidelines-avoid-adjacent-parameters-of-the-same-type-minlen3.cpp =================================================================== --- /dev/null +++ clang-tools-extra/test/clang-tidy/checkers/experimental-cppcoreguidelines-avoid-adjacent-parameters-of-the-same-type-minlen3.cpp @@ -0,0 +1,205 @@ +// RUN: %check_clang_tidy %s \ +// RUN: experimental-cppcoreguidelines-avoid-adjacent-parameters-of-the-same-type %t \ +// RUN: -config='{CheckOptions: [ \ +// RUN: {key: experimental-cppcoreguidelines-avoid-adjacent-parameters-of-the-same-type.MinimumLength, value: 3} \ +// RUN: ]}' -- + +void library(void *vp, void *vp2, void *vp3, int n, int m); +// NO-WARN: The user has no chance to change only declared (usually library) +// functions, so no diagnostic is made. + +// NO-WARN for any of these: only 2 parameters present. + +struct T {}; + +void create(T **out_t) {} +void copy(T *p, T *q, int n) {} +void mov(T *dst, const T *src) {} + +void compare01(T t1, T t2) {} +void compare02(const T t1, T t2) {} +void compare03(T t1, const T t2) {} +void compare04(const T &t1, T t2) {} +void compare05(T t1, const T &t2) {} +void compare06(const T &t1, const T &t2) {} +void compare07(const T &t1, const T t2) {} +void compare08(const T t1, const T &t2) {} + +void compare09(T &&t1, T t2) {} +void compare0A(T t1, T &&t2) {} +void compare0B(T &&t1, T &&t2) {} + +struct U {}; +void compare0C(T t, U u) {} +void compare0D(const T t, U u) {} +void compare0E(T t, const U u) {} +void compare0F(const U &u, T t) {} +void compare10(T t, const U &u) {} +void compare11(const T &t, const U &u) {} +void compare12(const T &t, const U u) {} +void compare13(const U u, const T &t) {} +void compare14(T &&t, U u) {} +void compare15(T t, U &&u) {} + +typedef int I1; +typedef int I2; +void multi_through_typedef(I1 i1, I2 i2) {} + +void multi_ptr_through_typedef(I1 *p1, I2 *p2) {} + +typedef I2 myInt; +typedef I2 myOtherInt; +void typedef_chain(myInt mi, myOtherInt moi) {} + +void bind_power_and_typedef(int Read, const myOtherInt &Write) {} + +typedef myOtherInt *IP; +typedef const IP &cipr; +void bind_power_and_multi_layer_typedef(int *RP, cipr WP) {} + +void entirely_different(int i, long l) {} + +struct StringRef { + char *Data; +}; +typedef bool Toggle; + +void multi_pack(StringRef Path, StringRef Name, int Size, Toggle Read, Toggle Write) {} + +void defined(int a, int b); +void defined(int i, int j) {} + +template +void ptr_pair(T *p, T *q) {} + +template +void cptr_pair(const T *p, const T *q) {} + +void ref_and_typedef(int i, I2 i2) {} +void ref_and_typedef2(I1 i, int i2) {} + +int strcmp(const char *A, const char *B) {} + +using str = char *; +using strlit = const str; +int str_compare(strlit a, strlit b) {} + +void append(str s, char *s2) {} + +void append_lit(str s, strlit s2) {} + +void swap(str *a, char **b) {} + +// END of NO-WARN. + +// NO-WARN: CVR-qualification-mixup is turned off in this test file. + +void merge(T *dst, const T *src1, const T *src2) {} + +void multi_bind(T t, const T t2, const T &t3, T &&t4) {} + +void ptr_quals(int *ip, const int *cip, volatile int *vip, int *const ipc, volatile int *volatile vipv) {} + +void value_quals(T t, const T ct, volatile T vt, const volatile T cvt) {} + +void value_ptr_quals(T *tp, const T *ctp, volatile T *vtp, const volatile T *cvtp, T *const tpc, T *volatile tpv) {} + +void typedef_and_realtype_ptr_const(int *p1, I1 *p2, const I1 *p3) {} + +void typedef_ptr_const(I1 *p1, I1 *p2, const I1 *p3) {} + +// END of NO-WARN. + +// NO-WARN: const T* and T* should not mix, even if coming through a typedef. +// (min and max could be mixed up, but length requirement in this test is 3.) +typedef int Numeral; +void set_lerped(const Numeral *const min, + const Numeral *const max, + Numeral *const value) {} + +void protochain(int, int, int); +void protochain(int i, int, int); +void protochain(int, int j, int); +void protochain(int i, int j, int k) {} +// CHECK-MESSAGES: :[[@LINE-1]]:17: warning: 3 adjacent parameters for 'protochain' of similar type ('int') are +// CHECK-MESSAGES: :[[@LINE-2]]:21: note: the first parameter in this range is 'i' +// CHECK-MESSAGES: :[[@LINE-3]]:35: note: the last parameter in this range is 'k' + +void protochain2(I1, I2, myInt); +void protochain2(I1 i, I2, myInt); +void protochain2(I1, I2 j, int); +void protochain2(I1 i, I2 j, myOtherInt k) {} +// CHECK-MESSAGES: :[[@LINE-1]]:18: warning: 3 adjacent parameters for 'protochain2' of similar type are +// CHECK-MESSAGES: :[[@LINE-2]]:21: note: the first parameter in this range is 'i' +// CHECK-MESSAGES: :[[@LINE-3]]:41: note: the last parameter in this range is 'k' +// CHECK-MESSAGES: :[[@LINE-4]]:18: note: after resolving type aliases, type of parameter 'i' is 'int' +// CHECK-MESSAGES: :[[@LINE-5]]:24: note: after resolving type aliases, type of parameter 'j' is 'int' +// CHECK-MESSAGES: :[[@LINE-6]]:30: note: after resolving type aliases, type of parameter 'k' is 'int' + +void len3(const StringRef Drive, const StringRef Dirs, const StringRef FileName) {} +// CHECK-MESSAGES: :[[@LINE-1]]:11: warning: 3 adjacent parameters for 'len3' of similar type ('const StringRef') are +// CHECK-MESSAGES: :[[@LINE-2]]:27: note: the first parameter in this range is 'Drive' +// CHECK-MESSAGES: :[[@LINE-3]]:72: note: the last parameter in this range is 'FileName' + +void len4(const StringRef Drive, const StringRef Dirs, const StringRef FileName, const StringRef Extension) {} +// CHECK-MESSAGES: :[[@LINE-1]]:11: warning: 4 adjacent parameters for 'len4' of similar type ('const StringRef') are +// CHECK-MESSAGES: :[[@LINE-2]]:27: note: the first parameter in this range is 'Drive' +// CHECK-MESSAGES: :[[@LINE-3]]:98: note: the last parameter in this range is 'Extension' + +void multi_packs(StringRef Path, StringRef Name, StringRef Occupation, + int Size, int Width, + Toggle Read, Toggle Write, Toggle Execute) {} +// CHECK-MESSAGES: :[[@LINE-3]]:18: warning: 3 adjacent parameters for 'multi_packs' of similar type ('StringRef') are +// CHECK-MESSAGES: :[[@LINE-4]]:28: note: the first parameter in this range is 'Path' +// CHECK-MESSAGES: :[[@LINE-5]]:60: note: the last parameter in this range is 'Occupation' +// CHECK-MESSAGES: :[[@LINE-4]]:18: warning: 3 adjacent parameters for 'multi_packs' of similar type ('Toggle') are +// CHECK-MESSAGES: :[[@LINE-5]]:25: note: the first parameter in this range is 'Read' +// CHECK-MESSAGES: :[[@LINE-6]]:52: note: the last parameter in this range is 'Execute' + +// CVRMixupPossible only concerns 'T' and 'c/v/r T', or pointers thereof, not +// references. +void lifetime_extension(T t, const T &tr1, const T &tr2) {} +// CHECK-MESSAGES: :[[@LINE-1]]:25: warning: 3 adjacent parameters for 'lifetime_extension' of similar type are +// CHECK-MESSAGES: :[[@LINE-2]]:27: note: the first parameter in this range is 't' +// CHECK-MESSAGES: :[[@LINE-3]]:53: note: the last parameter in this range is 'tr2' +// CHECK-MESSAGES: :[[@LINE-4]]:30: note: at a call site, 'const T &' might bind with same force as 'T' +// CHECK-MESSAGES: :[[@LINE-5]]:44: note: at a call site, 'const T &' might bind with same force as 'T' + +enum OldSchoolTermColour { + Black, + Blue, + Green, + Cyan, + Red, + Purple, + BrownOrange, + LightGreyWhite, + Gray, + LightBlue, + LightGreen, + LightCyan, + LightRed, + LightPurple, + YellowLightOrange, + WhiteLightWhite +}; + +void setColouredOutput(OldSchoolTermColour Background, + OldSchoolTermColour Foreground, + OldSchoolTermColour Border) {} +// CHECK-MESSAGES: :[[@LINE-3]]:24: warning: 3 adjacent parameters for 'setColouredOutput' of similar type ('OldSchoolTermColour') are +// CHECK-MESSAGES: :[[@LINE-4]]:44: note: the first parameter in this range is 'Background' +// CHECK-MESSAGES: :[[@LINE-3]]:44: note: the last parameter in this range is 'Border' + +typedef OldSchoolTermColour BackColour; +typedef OldSchoolTermColour ForeColour; +typedef OldSchoolTermColour BorderColour; +void setColouredOutput2(BackColour Background, + ForeColour Foreground, + BorderColour Border) {} +// CHECK-MESSAGES: :[[@LINE-3]]:25: warning: 3 adjacent parameters for 'setColouredOutput2' of similar type are +// CHECK-MESSAGES: :[[@LINE-4]]:36: note: the first parameter in this range is 'Background' +// CHECK-MESSAGES: :[[@LINE-3]]:38: note: the last parameter in this range is 'Border' +// CHECK-MESSAGES: :[[@LINE-6]]:25: note: after resolving type aliases, type of parameter 'Background' is 'OldSchoolTermColour' +// CHECK-MESSAGES: :[[@LINE-6]]:25: note: after resolving type aliases, type of parameter 'Foreground' is 'OldSchoolTermColour' +// CHECK-MESSAGES: :[[@LINE-6]]:25: note: after resolving type aliases, type of parameter 'Border' is 'OldSchoolTermColour' Index: clang-tools-extra/test/clang-tidy/checkers/experimental-cppcoreguidelines-avoid-adjacent-parameters-of-the-same-type.c =================================================================== --- /dev/null +++ clang-tools-extra/test/clang-tidy/checkers/experimental-cppcoreguidelines-avoid-adjacent-parameters-of-the-same-type.c @@ -0,0 +1,34 @@ +// RUN: %check_clang_tidy %s \ +// RUN: experimental-cppcoreguidelines-avoid-adjacent-parameters-of-the-same-type %t \ +// RUN: -config='{CheckOptions: [ \ +// RUN: {key: experimental-cppcoreguidelines-avoid-adjacent-parameters-of-the-same-type.CVRMixPossible, value: 1} \ +// RUN: ]}' -- + +struct T {}; + +void memcpy(struct T *restrict dest, const struct T *restrict src) {} +// CHECK-MESSAGES: :[[@LINE-1]]:13: warning: 2 adjacent parameters for 'memcpy' of similar type are +// CHECK-MESSAGES: :[[@LINE-2]]:32: note: the first parameter in this range is 'dest' +// CHECK-MESSAGES: :[[@LINE-3]]:63: note: the last parameter in this range is 'src' +// CHECK-MESSAGES: :[[@LINE-4]]:38: note: at a call site, 'const struct T * restrict' might bind with same force as 'struct T *restrict' + +void merge(struct T *dst, const struct T *src1, const struct T *src2) {} +// CHECK-MESSAGES: :[[@LINE-1]]:12: warning: 3 adjacent parameters for 'merge' of similar type are +// CHECK-MESSAGES: :[[@LINE-2]]:22: note: the first parameter in this range is 'dst' +// CHECK-MESSAGES: :[[@LINE-3]]:65: note: the last parameter in this range is 'src2' +// CHECK-MESSAGES: :[[@LINE-4]]:27: note: at a call site, 'const struct T *' might bind with same force as 'struct T *' +// CHECK-MESSAGES: :[[@LINE-5]]:49: note: at a call site, 'const struct T *' might bind with same force as 'struct T *' + +int equals(struct T a, struct T b) {} +// CHECK-MESSAGES: :[[@LINE-1]]:12: warning: 2 adjacent parameters for 'equals' of similar type ('struct T') are +// CHECK-MESSAGES: :[[@LINE-2]]:21: note: the first parameter in this range is 'a' +// CHECK-MESSAGES: :[[@LINE-3]]:33: note: the last parameter in this range is 'b' + +typedef struct { + int x; +} S; + +int equals2(S l, S r) { return l.x == r.x; } +// CHECK-MESSAGES: :[[@LINE-1]]:13: warning: 2 adjacent parameters for 'equals2' of similar type ('S') are +// CHECK-MESSAGES: :[[@LINE-2]]:15: note: the first parameter in this range is 'l' +// CHECK-MESSAGES: :[[@LINE-3]]:20: note: the last parameter in this range is 'r'