diff --git a/clang-tidy/readability/CMakeLists.txt b/clang-tidy/readability/CMakeLists.txt index 0584168..8ed1018 100644 --- a/clang-tidy/readability/CMakeLists.txt +++ b/clang-tidy/readability/CMakeLists.txt @@ -6,6 +6,7 @@ add_clang_library(clangTidyReadabilityModule ElseAfterReturnCheck.cpp FunctionSizeCheck.cpp IdentifierNamingCheck.cpp + InconsistentDeclarationParameterNameCheck.cpp NamedParameterCheck.cpp NamespaceCommentCheck.cpp ReadabilityTidyModule.cpp diff --git a/clang-tidy/readability/InconsistentDeclarationParameterNameCheck.cpp b/clang-tidy/readability/InconsistentDeclarationParameterNameCheck.cpp new file mode 100644 index 0000000..ab09124 --- /dev/null +++ b/clang-tidy/readability/InconsistentDeclarationParameterNameCheck.cpp @@ -0,0 +1,336 @@ +//===--- InconsistentDeclarationParameterNameCheck.cpp - clang-tidy-------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "InconsistentDeclarationParameterNameCheck.h" +#include "clang/AST/ASTContext.h" +#include "clang/ASTMatchers/ASTMatchFinder.h" + +#include +#include +#include + +namespace clang { +namespace tidy { +namespace readability { + +using namespace ast_matchers; + +namespace { + +AST_MATCHER(FunctionDecl, hasOtherDeclarations) { + auto It = Node.redecls_begin(); + auto EndIt = Node.redecls_end(); + + if (It == EndIt) + return false; + + ++It; + return It != EndIt; +} + +struct DifferingParamInfo { + DifferingParamInfo(StringRef SourceName, StringRef OtherName, + SourceRange OtherNameRange, bool GenerateFixItHint) + : SourceName(SourceName), OtherName(OtherName), + OtherNameRange(OtherNameRange), GenerateFixItHint(GenerateFixItHint) {} + + StringRef SourceName; + StringRef OtherName; + SourceRange OtherNameRange; + bool GenerateFixItHint; +}; + +using DifferingParamsContainer = llvm::SmallVector; + +struct InconsistentDeclarationInfo { + InconsistentDeclarationInfo(SourceLocation DeclarationLocation, + DifferingParamsContainer &&DifferingParams) + : DeclarationLocation(DeclarationLocation), + DifferingParams(std::move(DifferingParams)) {} + + SourceLocation DeclarationLocation; + DifferingParamsContainer DifferingParams; +}; + +using InconsistentDeclarationsContainer = + llvm::SmallVector; + +bool checkIfFixItHintIsApplicable( + const FunctionDecl *ParameterSourceDeclaration, + const ParmVarDecl *SourceParam, const FunctionDecl *OriginalDeclaration) { + // Assumptions with regard to function declarations/definition: + // * If both function declaration and definition are seen, assume that + // definition is most up-to-date, and use it to generate replacements. + // * If only function declarations are seen, there is no easy way to tell + // which is up-to-date and which is not, so don't do anything. + // TODO: This may be changed later, but for now it seems the reasonable + // solution. + if (!ParameterSourceDeclaration->isThisDeclarationADefinition()) + return false; + + // Assumption: if parameter is not referenced in function defintion body, it + // may indicate that it's outdated, so don't touch it. + if (!SourceParam->isReferenced()) + return false; + + // In case there is the primary template definition and (possibly several) + // template specializations (and each with possibly several redeclarations), + // it is not at all clear what to change. + if (OriginalDeclaration->getTemplatedKind() == + FunctionDecl::TK_FunctionTemplateSpecialization) + return false; + + // Other cases seem OK to allow replacements. + return true; +} + +DifferingParamsContainer +findDifferingParamsInDeclaration(const FunctionDecl *ParameterSourceDeclaration, + const FunctionDecl *OtherDeclaration, + const FunctionDecl *OriginalDeclaration) { + DifferingParamsContainer DifferingParams; + + auto SourceParamIt = ParameterSourceDeclaration->param_begin(); + auto OtherParamIt = OtherDeclaration->param_begin(); + + while (SourceParamIt != ParameterSourceDeclaration->param_end() && + OtherParamIt != OtherDeclaration->param_end()) { + auto SourceParamName = (*SourceParamIt)->getName(); + auto OtherParamName = (*OtherParamIt)->getName(); + + // FIXME: Provide a way to extract commented out parameter name from comment + // next to it. + if (!SourceParamName.empty() && !OtherParamName.empty() && + SourceParamName != OtherParamName) { + SourceRange OtherParamNameRange = + DeclarationNameInfo((*OtherParamIt)->getDeclName(), + (*OtherParamIt)->getLocation()).getSourceRange(); + + bool GenerateFixItHint = checkIfFixItHintIsApplicable( + ParameterSourceDeclaration, *SourceParamIt, OriginalDeclaration); + + DifferingParams.emplace_back(SourceParamName, OtherParamName, + OtherParamNameRange, GenerateFixItHint); + } + + ++SourceParamIt; + ++OtherParamIt; + } + + return DifferingParams; +} + +InconsistentDeclarationsContainer +findInconsitentDeclarations(const FunctionDecl *OriginalDeclaration, + const FunctionDecl *ParameterSourceDeclaration, + SourceManager &SM) { + InconsistentDeclarationsContainer InconsistentDeclarations; + SourceLocation ParameterSourceLocation = + ParameterSourceDeclaration->getLocation(); + + for (const FunctionDecl *OtherDeclaration : OriginalDeclaration->redecls()) { + SourceLocation OtherLocation = OtherDeclaration->getLocation(); + if (OtherLocation != ParameterSourceLocation) { // Skip self. + DifferingParamsContainer DifferingParams = + findDifferingParamsInDeclaration(ParameterSourceDeclaration, + OtherDeclaration, + OriginalDeclaration); + if (!DifferingParams.empty()) { + InconsistentDeclarations.emplace_back(OtherDeclaration->getLocation(), + std::move(DifferingParams)); + } + } + } + + // Sort in order of appearance in translation unit to generate clear + // diagnostics. + std::sort(InconsistentDeclarations.begin(), InconsistentDeclarations.end(), + [&SM](const InconsistentDeclarationInfo &Info1, + const InconsistentDeclarationInfo &Info2) { + return SM.isBeforeInTranslationUnit(Info1.DeclarationLocation, + Info2.DeclarationLocation); + }); + return InconsistentDeclarations; +} + +const FunctionDecl * +getParameterSourceDeclaration(const FunctionDecl *OriginalDeclaration) { + const FunctionTemplateDecl *PrimaryTemplate = + OriginalDeclaration->getPrimaryTemplate(); + if (PrimaryTemplate != nullptr) { + // In case of template specializations, use primary template declaration as + // the source of parameter names. + return PrimaryTemplate->getTemplatedDecl(); + } + + // In other cases, try to change to function definition, if available. + + if (OriginalDeclaration->isThisDeclarationADefinition()) + return OriginalDeclaration; + + for (const FunctionDecl *OtherDeclaration : OriginalDeclaration->redecls()) { + if (OtherDeclaration->isThisDeclarationADefinition()) { + return OtherDeclaration; + } + } + + // No definition found, so return original declaration. + return OriginalDeclaration; +} + +std::string joinParameterNames( + const DifferingParamsContainer &DifferingParams, + std::function ChooseParamName) { + llvm::SmallVector Buffer; + llvm::raw_svector_ostream Str(Buffer); + bool First = true; + for (const DifferingParamInfo &ParamInfo : DifferingParams) { + if (First) + First = false; + else + Str << ", "; + + Str << "'" << ChooseParamName(ParamInfo).str() << "'"; + } + return Str.str().str(); +} + +void formatDifferingParamsDiagnostic( + InconsistentDeclarationParameterNameCheck *Check, + const SourceLocation &Location, StringRef OtherDeclarationDescription, + const DifferingParamsContainer &DifferingParams) { + auto ChooseOtherName = + [](const DifferingParamInfo &ParamInfo) { return ParamInfo.OtherName; }; + auto ChooseSourceName = + [](const DifferingParamInfo &ParamInfo) { return ParamInfo.SourceName; }; + + auto ParamDiag = + Check->diag(Location, + "differing parameters are named here: (%0), in %1: (%2)", + DiagnosticIDs::Level::Note) + << joinParameterNames(DifferingParams, ChooseOtherName) + << OtherDeclarationDescription + << joinParameterNames(DifferingParams, ChooseSourceName); + + for (const DifferingParamInfo &ParamInfo : DifferingParams) { + if (ParamInfo.GenerateFixItHint) { + ParamDiag << FixItHint::CreateReplacement( + CharSourceRange::getTokenRange(ParamInfo.OtherNameRange), + ParamInfo.SourceName); + } + } +} + +void formatDiagnosticsForDeclarations( + InconsistentDeclarationParameterNameCheck *Check, + const FunctionDecl *ParameterSourceDeclaration, + const FunctionDecl *OriginalDeclaration, + const InconsistentDeclarationsContainer &InconsistentDeclarations) { + Check->diag( + OriginalDeclaration->getLocation(), + "function %q0 has %1 other declaration%s1 with different parameter names") + << OriginalDeclaration + << static_cast(InconsistentDeclarations.size()); + int Count = 1; + for (const InconsistentDeclarationInfo &InconsistentDeclaration : + InconsistentDeclarations) { + Check->diag(InconsistentDeclaration.DeclarationLocation, + "the %ordinal0 inconsistent declaration seen here", + DiagnosticIDs::Level::Note) + << Count; + + formatDifferingParamsDiagnostic( + Check, InconsistentDeclaration.DeclarationLocation, + "the other declaration", InconsistentDeclaration.DifferingParams); + + ++Count; + } +} + +void formatDiagnostics( + InconsistentDeclarationParameterNameCheck *Check, + const FunctionDecl *ParameterSourceDeclaration, + const FunctionDecl *OriginalDeclaration, + const InconsistentDeclarationsContainer &InconsistentDeclarations, + StringRef FunctionDescription, StringRef ParameterSourceDescription) { + for (const InconsistentDeclarationInfo &InconsistentDeclaration : + InconsistentDeclarations) { + Check->diag(InconsistentDeclaration.DeclarationLocation, + "%0 %q1 has a %2 with different parameter names") + << FunctionDescription << OriginalDeclaration + << ParameterSourceDescription; + + Check->diag(ParameterSourceDeclaration->getLocation(), "the %0 seen here", + DiagnosticIDs::Level::Note) + << ParameterSourceDescription; + + formatDifferingParamsDiagnostic( + Check, InconsistentDeclaration.DeclarationLocation, + ParameterSourceDescription, InconsistentDeclaration.DifferingParams); + } +} + +} // anonymous namespace + +void InconsistentDeclarationParameterNameCheck::registerMatchers( + MatchFinder *Finder) { + Finder->addMatcher(functionDecl(unless(isImplicit()), hasOtherDeclarations()) + .bind("functionDecl"), + this); +} + +void InconsistentDeclarationParameterNameCheck::check( + const MatchFinder::MatchResult &Result) { + const auto *OriginalDeclaration = + Result.Nodes.getNodeAs("functionDecl"); + + if (VisitedDeclarations.count(OriginalDeclaration) > 0) + return; // Avoid multiple warnings. + + const FunctionDecl *ParameterSourceDeclaration = + getParameterSourceDeclaration(OriginalDeclaration); + + InconsistentDeclarationsContainer InconsistentDeclarations = + findInconsitentDeclarations(OriginalDeclaration, + ParameterSourceDeclaration, + *Result.SourceManager); + if (InconsistentDeclarations.empty()) { + // Avoid unnecessary further visits. + markRedeclarationsAsVisited(OriginalDeclaration); + return; + } + + if (OriginalDeclaration->getTemplatedKind() == + FunctionDecl::TK_FunctionTemplateSpecialization) { + formatDiagnostics(this, ParameterSourceDeclaration, OriginalDeclaration, + InconsistentDeclarations, + "function template specialization", + "primary template declaration"); + } else if (ParameterSourceDeclaration->isThisDeclarationADefinition()) { + formatDiagnostics(this, ParameterSourceDeclaration, OriginalDeclaration, + InconsistentDeclarations, "function", "definition"); + } else { + formatDiagnosticsForDeclarations(this, ParameterSourceDeclaration, + OriginalDeclaration, + InconsistentDeclarations); + } + + markRedeclarationsAsVisited(OriginalDeclaration); +} + +void InconsistentDeclarationParameterNameCheck::markRedeclarationsAsVisited( + const FunctionDecl *OriginalDeclaration) { + for (const FunctionDecl *Redecl : OriginalDeclaration->redecls()) { + VisitedDeclarations.insert(Redecl); + } +} + +} // namespace readability +} // namespace tidy +} // namespace clang diff --git a/clang-tidy/readability/InconsistentDeclarationParameterNameCheck.h b/clang-tidy/readability/InconsistentDeclarationParameterNameCheck.h new file mode 100644 index 0000000..5486031 --- /dev/null +++ b/clang-tidy/readability/InconsistentDeclarationParameterNameCheck.h @@ -0,0 +1,45 @@ +//===- InconsistentDeclarationParameterNameCheck.h - clang-tidy-*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_READABILITY_INCONSISTENT_DECLARATION_PARAMETER_NAME_H +#define LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_READABILITY_INCONSISTENT_DECLARATION_PARAMETER_NAME_H + +#include "../ClangTidy.h" + +#include "llvm/ADT/DenseSet.h" + +namespace clang { +namespace tidy { +namespace readability { + +/// \brief Checks for declarations of functions which differ in parameter names. +/// +/// For detailed documentation see: +/// http://clang.llvm.org/extra/clang-tidy/checks/readability-inconsistent-declaration-parameter-name.html +/// +class InconsistentDeclarationParameterNameCheck : public ClangTidyCheck { +public: + InconsistentDeclarationParameterNameCheck(StringRef Name, + ClangTidyContext *Context) + : ClangTidyCheck(Name, Context) {} + + void registerMatchers(ast_matchers::MatchFinder *Finder) override; + void check(const ast_matchers::MatchFinder::MatchResult &Result) override; + +private: + void markRedeclarationsAsVisited(const FunctionDecl *FunctionDeclaration); + + llvm::DenseSet VisitedDeclarations; +}; + +} // namespace readability +} // namespace tidy +} // namespace clang + +#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_READABILITY_INCONSISTENT_DECLARATION_PARAMETER_NAME_H diff --git a/clang-tidy/readability/ReadabilityTidyModule.cpp b/clang-tidy/readability/ReadabilityTidyModule.cpp index 6efcbfa..40c00d4 100644 --- a/clang-tidy/readability/ReadabilityTidyModule.cpp +++ b/clang-tidy/readability/ReadabilityTidyModule.cpp @@ -15,6 +15,7 @@ #include "ElseAfterReturnCheck.h" #include "FunctionSizeCheck.h" #include "IdentifierNamingCheck.h" +#include "InconsistentDeclarationParameterNameCheck.h" #include "NamedParameterCheck.h" #include "RedundantSmartptrGetCheck.h" #include "RedundantStringCStrCheck.h" @@ -37,6 +38,8 @@ public: "readability-function-size"); CheckFactories.registerCheck( "readability-identifier-naming"); + CheckFactories.registerCheck( + "readability-inconsistent-declaration-parameter-name"); CheckFactories.registerCheck( "readability-named-parameter"); CheckFactories.registerCheck( diff --git a/docs/clang-tidy/checks/list.rst b/docs/clang-tidy/checks/list.rst index abd5b8e..be7bf3b 100644 --- a/docs/clang-tidy/checks/list.rst +++ b/docs/clang-tidy/checks/list.rst @@ -50,6 +50,7 @@ List of clang-tidy Checks readability-else-after-return readability-function-size readability-identifier-naming + readability-inconsistent-declaration-parameter-name readability-named-parameter readability-redundant-smartptr-get readability-redundant-string-cstr diff --git a/docs/clang-tidy/checks/readability-inconsistent-declaration-parameter-name.rst b/docs/clang-tidy/checks/readability-inconsistent-declaration-parameter-name.rst new file mode 100644 index 0000000..872fee7 --- /dev/null +++ b/docs/clang-tidy/checks/readability-inconsistent-declaration-parameter-name.rst @@ -0,0 +1,32 @@ +readability-inconsistent-declaration-parameter-name +=================================================== + + +Find function declarations which differ in parameter names. + +Example: + +.. code:: c++ + + // in foo.hpp: + void foo(int a, int b, int c); + + // in foo.cpp: + void foo(int d, int e, int f); // warning + +This check should help to enforce consistency in large projects, where it often +happens that a definition of function is refactored, changing the parameter +names, but its declaration in header file is not updated. With this check, we +can easily find and correct such inconsistencies, keeping declaration and +definition always in sync. + +Unnamed parameters are allowed and are not taken into account when comparing +function declarations, for example: + +.. code:: c++ + + void foo(int a); + void foo(int); // no warning + +If there are multiple declarations of same function, only one warning will be +generated. diff --git a/test/clang-tidy/readability-inconsistent-declaration-parameter-name.cpp b/test/clang-tidy/readability-inconsistent-declaration-parameter-name.cpp new file mode 100644 index 0000000..f631317 --- /dev/null +++ b/test/clang-tidy/readability-inconsistent-declaration-parameter-name.cpp @@ -0,0 +1,188 @@ +// RUN: %python %S/check_clang_tidy.py %s readability-inconsistent-declaration-parameter-name %t + +void consistentFunction(int a, int b, int c); +void consistentFunction(int a, int b, int c); +void consistentFunction(int a, int b, int /*c*/); +void consistentFunction(int /*c*/, int /*c*/, int /*c*/); + +////////////////////////////////////////////////////// + +// CHECK-MESSAGES: :[[@LINE+1]]:6: warning: function 'inconsistentFunction' has 2 other declarations with different parameter names [readability-inconsistent-declaration-parameter-name] +void inconsistentFunction(int a, int b, int c); +// CHECK-MESSAGES: :[[@LINE+2]]:6: note: the 1st inconsistent declaration seen here +// CHECK-MESSAGES: :[[@LINE+1]]:6: note: differing parameters are named here: ('d', 'e', 'f'), in the other declaration: ('a', 'b', 'c') +void inconsistentFunction(int d, int e, int f); +// CHECK-MESSAGES: :[[@LINE+2]]:6: note: the 2nd inconsistent declaration seen here +// CHECK-MESSAGES: :[[@LINE+1]]:6: note: differing parameters are named here: ('x', 'y', 'z'), in the other declaration: ('a', 'b', 'c') +void inconsistentFunction(int x, int y, int z); + +////////////////////////////////////////////////////// + +// CHECK-MESSAGES: :[[@LINE+4]]:6: warning: function 'inconsistentFunctionWithVisibleDefinition' has a definition with different parameter names [readability-inconsistent-declaration-parameter-name] +// CHECK-MESSAGES: :[[@LINE+9]]:6: note: the definition seen here +// CHECK-MESSAGES: :[[@LINE+2]]:6: note: differing parameters are named here: ('a'), in definition: ('c') +// CHECK-FIXES: void inconsistentFunctionWithVisibleDefinition(int c); +void inconsistentFunctionWithVisibleDefinition(int a); +// CHECK-MESSAGES: :[[@LINE+4]]:6: warning: function 'inconsistentFunctionWithVisibleDefinition' has a definition +// CHECK-MESSAGES: :[[@LINE+4]]:6: note: the definition seen here +// CHECK-MESSAGES: :[[@LINE+2]]:6: note: differing parameters are named here: ('b'), in definition: ('c') +// CHECK-FIXES: void inconsistentFunctionWithVisibleDefinition(int c); +void inconsistentFunctionWithVisibleDefinition(int b); +void inconsistentFunctionWithVisibleDefinition(int c) { c; } + +// CHECK-MESSAGES: :[[@LINE+3]]:6: warning: function 'inconsidentFunctionWithUnreferencedParameterInDefinition' has a definition +// CHECK-MESSAGES: :[[@LINE+3]]:6: note: the definition seen here +// CHECK-MESSAGES: :[[@LINE+1]]:6: note: differing parameters are named here: ('a'), in definition: ('b') +void inconsidentFunctionWithUnreferencedParameterInDefinition(int a); +void inconsidentFunctionWithUnreferencedParameterInDefinition(int b) {} + +////////////////////////////////////////////////////// + +struct Struct { +// CHECK-MESSAGES: :[[@LINE+4]]:8: warning: function 'Struct::inconsistentFunction' has a definition +// CHECK-MESSAGES: :[[@LINE+6]]:14: note: the definition seen here +// CHECK-MESSAGES: :[[@LINE+2]]:8: note: differing parameters are named here: ('a'), in definition: ('b') +// CHECK-FIXES: void inconsistentFunction(int b); + void inconsistentFunction(int a); +}; + +void Struct::inconsistentFunction(int b) { b = 0; } + +////////////////////////////////////////////////////// + +struct SpecialFunctions { +// CHECK-MESSAGES: :[[@LINE+4]]:3: warning: function 'SpecialFunctions::SpecialFunctions' has a definition +// CHECK-MESSAGES: :[[@LINE+12]]:19: note: the definition seen here +// CHECK-MESSAGES: :[[@LINE+2]]:3: note: differing parameters are named here: ('a'), in definition: ('b') +// CHECK-FIXES: SpecialFunctions(int b); + SpecialFunctions(int a); + +// CHECK-MESSAGES: :[[@LINE+4]]:21: warning: function 'SpecialFunctions::operator=' has a definition +// CHECK-MESSAGES: :[[@LINE+8]]:37: note: the definition seen here +// CHECK-MESSAGES: :[[@LINE+2]]:21: note: differing parameters are named here: ('a'), in definition: ('b') +// CHECK-FIXES: SpecialFunctions& operator=(const SpecialFunctions& b); + SpecialFunctions& operator=(const SpecialFunctions& a); +}; + +SpecialFunctions::SpecialFunctions(int b) { b; } + +SpecialFunctions& SpecialFunctions::operator=(const SpecialFunctions& b) { b; return *this; } + +////////////////////////////////////////////////////// + +// CHECK-MESSAGES: :[[@LINE+5]]:6: warning: function 'templateFunctionWithSeparateDeclarationAndDefinition' has a definition +// CHECK-MESSAGES: :[[@LINE+7]]:6: note: the definition seen here +// CHECK-MESSAGES: :[[@LINE+3]]:6: note: differing parameters are named here: ('a'), in definition: ('b') +// CHECK-FIXES: void templateFunctionWithSeparateDeclarationAndDefinition(T b); +template +void templateFunctionWithSeparateDeclarationAndDefinition(T a); + +template +void templateFunctionWithSeparateDeclarationAndDefinition(T b) { b; } + +////////////////////////////////////////////////////// + +template +void templateFunctionWithSpecializations(T a) { a; } + +template<> +// CHECK-MESSAGES: :[[@LINE+3]]:6: warning: function template specialization 'templateFunctionWithSpecializations' has a primary template declaration with different parameter names [readability-inconsistent-declaration-parameter-name] +// CHECK-MESSAGES: :[[@LINE-4]]:6: note: the primary template declaration seen here +// CHECK-MESSAGES: :[[@LINE+1]]:6: note: differing parameters are named here: ('b'), in primary template declaration: ('a') +void templateFunctionWithSpecializations(int b) { b; } + +template<> +// CHECK-MESSAGES: :[[@LINE+3]]:6: warning: function template specialization 'templateFunctionWithSpecializations' has a primary template +// CHECK-MESSAGES: :[[@LINE-10]]:6: note: the primary template declaration seen here +// CHECK-MESSAGES: :[[@LINE+1]]:6: note: differing parameters are named here: ('c'), in primary template declaration: ('a') +void templateFunctionWithSpecializations(float c) { c; } + +////////////////////////////////////////////////////// + +template +void templateFunctionWithoutDefinitionButWithSpecialization(T a); + +template<> +// CHECK-MESSAGES: :[[@LINE+3]]:6: warning: function template specialization 'templateFunctionWithoutDefinitionButWithSpecialization' has a primary template +// CHECK-MESSAGES: :[[@LINE-4]]:6: note: the primary template declaration seen here +// CHECK-MESSAGES: :[[@LINE+1]]:6: note: differing parameters are named here: ('b'), in primary template declaration: ('a') +void templateFunctionWithoutDefinitionButWithSpecialization(int b) { b; } + +////////////////////////////////////////////////////// + +template +void templateFunctionWithSeparateSpecializationDeclarationAndDefinition(T a); + +template<> +// CHECK-MESSAGES: :[[@LINE+3]]:6: warning: function template specialization 'templateFunctionWithSeparateSpecializationDeclarationAndDefinition' has a primary template +// CHECK-MESSAGES: :[[@LINE-4]]:6: note: the primary template declaration seen here +// CHECK-MESSAGES: :[[@LINE+1]]:6: note: differing parameters are named here: ('b'), in primary template declaration: ('a') +void templateFunctionWithSeparateSpecializationDeclarationAndDefinition(int b); + +template<> +// CHECK-MESSAGES: :[[@LINE+3]]:6: warning: function template specialization 'templateFunctionWithSeparateSpecializationDeclarationAndDefinition' has a primary template +// CHECK-MESSAGES: :[[@LINE-10]]:6: note: the primary template declaration seen here +// CHECK-MESSAGES: :[[@LINE+1]]:6: note: differing parameters are named here: ('c'), in primary template declaration: ('a') +void templateFunctionWithSeparateSpecializationDeclarationAndDefinition(int c) { c; } + +////////////////////////////////////////////////////// + +template +class ClassTemplate +{ +public: +// CHECK-MESSAGES: :[[@LINE+4]]:10: warning: function 'ClassTemplate::functionInClassTemplateWithSeparateDeclarationAndDefinition' has a definition +// CHECK-MESSAGES: :[[@LINE+7]]:24: note: the definition seen here +// CHECK-MESSAGES: :[[@LINE+2]]:10: note: differing parameters are named here: ('a'), in definition: ('b') +// CHECK-FIXES: void functionInClassTemplateWithSeparateDeclarationAndDefinition(int b); + void functionInClassTemplateWithSeparateDeclarationAndDefinition(int a); +}; + +template +void ClassTemplate::functionInClassTemplateWithSeparateDeclarationAndDefinition(int b) { b; } + +////////////////////////////////////////////////////// + +class Class +{ +public: + template +// CHECK-MESSAGES: :[[@LINE+4]]:8: warning: function 'Class::memberFunctionTemplateWithSeparateDeclarationAndDefinition' has a definition +// CHECK-MESSAGES: :[[@LINE+12]]:13: note: the definition seen here +// CHECK-MESSAGES: :[[@LINE+2]]:8: note: differing parameters are named here: ('a'), in definition: ('b') +// CHECK-FIXES: void memberFunctionTemplateWithSeparateDeclarationAndDefinition(T b); + void memberFunctionTemplateWithSeparateDeclarationAndDefinition(T a); + + template + void memberFunctionTemplateWithSpecializations(T a) { a; } +}; + +////////////////////////////////////////////////////// + +template +void Class::memberFunctionTemplateWithSeparateDeclarationAndDefinition(T b) { b; } + +////////////////////////////////////////////////////// + +template<> +// CHECK-MESSAGES: :[[@LINE+3]]:13: warning: function template specialization 'Class::memberFunctionTemplateWithSpecializations' has a primary template +// CHECK-MESSAGES: :[[@LINE-12]]:8: note: the primary template declaration seen here +// CHECK-MESSAGES: :[[@LINE+1]]:13: note: differing parameters are named here: ('b'), in primary template declaration: ('a') +void Class::memberFunctionTemplateWithSpecializations(int b) { b; } + +template<> +// CHECK-MESSAGES: :[[@LINE+3]]:13: warning: function template specialization 'Class::memberFunctionTemplateWithSpecializations' has a primary template +// CHECK-MESSAGES: :[[@LINE-18]]:8: note: the primary template declaration seen here +// CHECK-MESSAGES: :[[@LINE+1]]:13: note: differing parameters are named here: ('c'), in primary template declaration: ('a') +void Class::memberFunctionTemplateWithSpecializations(float c) { c; } + +////////////////////////////////////////////////////// + +#define DECLARE_FUNCTION_WITH_PARAM_NAME(function_name, param_name) \ + void function_name(int param_name) + +// CHECK-MESSAGES: :[[@LINE+1]]:34: warning: function 'macroFunction' has 1 other declaration with different parameter names [readability-inconsistent-declaration-parameter-name] +DECLARE_FUNCTION_WITH_PARAM_NAME(macroFunction, a); +// CHECK-MESSAGES: :[[@LINE+2]]:34: note: the 1st inconsistent declaration seen here +// CHECK-MESSAGES: :[[@LINE+1]]:34: note: differing parameters are named here: ('b'), in the other declaration: ('a') +DECLARE_FUNCTION_WITH_PARAM_NAME(macroFunction, b);