piotrdz updated this revision to Diff 34267. piotrdz marked 10 inline comments as done. piotrdz added a comment.
I hope this is the final re-write of my code. In this version, I addressed most recent review comments, while also refactoring code to better handle template specializations and correctly display diagnostics in case when function definition is visible. In the end I had to change over half of the code, but perhaps it was worth it. http://reviews.llvm.org/D12462 Files: clang-tidy/readability/CMakeLists.txt clang-tidy/readability/InconsistentDeclarationParameterNameCheck.cpp clang-tidy/readability/InconsistentDeclarationParameterNameCheck.h clang-tidy/readability/ReadabilityTidyModule.cpp docs/clang-tidy/checks/list.rst docs/clang-tidy/checks/readability-inconsistent-declaration-parameter-name.rst test/clang-tidy/readability-inconsistent-declaration-parameter-name.cpp
Index: test/clang-tidy/readability-inconsistent-declaration-parameter-name.cpp =================================================================== --- test/clang-tidy/readability-inconsistent-declaration-parameter-name.cpp +++ 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 differently named parameters [readability-inconsistent-declaration-parameter-name] +void inconsistentFunction(int a, int b, int c); +// CHECK-MESSAGES: :[[@LINE+2]]:6: note: 1st inconsistent declaration seen here +// CHECK-MESSAGES: :[[@LINE+1]]:6: note: differing parameters are named here: ('d', 'e', 'f'), while in other declaration: ('a', 'b', 'c') +void inconsistentFunction(int d, int e, int f); +// CHECK-MESSAGES: :[[@LINE+2]]:6: note: 2nd inconsistent declaration seen here +// CHECK-MESSAGES: :[[@LINE+1]]:6: note: differing parameters are named here: ('x', 'y', 'z'), while in 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 differently named parameters [readability-inconsistent-declaration-parameter-name] +// CHECK-MESSAGES: :[[@LINE+9]]:6: note: definition seen here +// CHECK-MESSAGES: :[[@LINE+2]]:6: note: differing parameters are named here: ('a'), while 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: definition seen here +// CHECK-MESSAGES: :[[@LINE+2]]:6: note: differing parameters are named here: ('b'), while 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: definition seen here +// CHECK-MESSAGES: :[[@LINE+1]]:6: note: differing parameters are named here: ('a'), while 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: definition seen here +// CHECK-MESSAGES: :[[@LINE+2]]:8: note: differing parameters are named here: ('a'), while 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: definition seen here +// CHECK-MESSAGES: :[[@LINE+2]]:3: note: differing parameters are named here: ('a'), while 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: definition seen here +// CHECK-MESSAGES: :[[@LINE+2]]:21: note: differing parameters are named here: ('a'), while 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: definition seen here +// CHECK-MESSAGES: :[[@LINE+3]]:6: note: differing parameters are named here: ('a'), while in definition: ('b') +// CHECK-FIXES: void templateFunctionWithSeparateDeclarationAndDefinition(T b); +template<typename T> +void templateFunctionWithSeparateDeclarationAndDefinition(T a); + +template<typename T> +void templateFunctionWithSeparateDeclarationAndDefinition(T b) { b; } + +////////////////////////////////////////////////////// + +template<typename T> +void templateFunctionWithSpecializations(T a) { a; } + +template<> +// CHECK-MESSAGES: :[[@LINE+3]]:6: warning: function template specialization 'templateFunctionWithSpecializations<int>' has a primary template declaration with differently named parameters [readability-inconsistent-declaration-parameter-name] +// CHECK-MESSAGES: :[[@LINE-4]]:6: note: primary template declaration seen here +// CHECK-MESSAGES: :[[@LINE+1]]:6: note: differing parameters are named here: ('b'), while in primary template declaration: ('a') +void templateFunctionWithSpecializations(int b) { b; } + +template<> +// CHECK-MESSAGES: :[[@LINE+3]]:6: warning: function template specialization 'templateFunctionWithSpecializations<float>' has a primary template +// CHECK-MESSAGES: :[[@LINE-10]]:6: note: primary template declaration seen here +// CHECK-MESSAGES: :[[@LINE+1]]:6: note: differing parameters are named here: ('c'), while in primary template declaration: ('a') +void templateFunctionWithSpecializations(float c) { c; } + +////////////////////////////////////////////////////// + +template<typename T> +void templateFunctionWithoutDefinitionButWithSpecialization(T a); + +template<> +// CHECK-MESSAGES: :[[@LINE+3]]:6: warning: function template specialization 'templateFunctionWithoutDefinitionButWithSpecialization<int>' has a primary template +// CHECK-MESSAGES: :[[@LINE-4]]:6: note: primary template declaration seen here +// CHECK-MESSAGES: :[[@LINE+1]]:6: note: differing parameters are named here: ('b'), while in primary template declaration: ('a') +void templateFunctionWithoutDefinitionButWithSpecialization(int b) { b; } + +////////////////////////////////////////////////////// + +template<typename T> +void templateFunctionWithSeparateSpecializationDeclarationAndDefinition(T a); + +template<> +// CHECK-MESSAGES: :[[@LINE+3]]:6: warning: function template specialization 'templateFunctionWithSeparateSpecializationDeclarationAndDefinition<int>' has a primary template +// CHECK-MESSAGES: :[[@LINE-4]]:6: note: primary template declaration seen here +// CHECK-MESSAGES: :[[@LINE+1]]:6: note: differing parameters are named here: ('b'), while in primary template declaration: ('a') +void templateFunctionWithSeparateSpecializationDeclarationAndDefinition(int b); + +template<> +// CHECK-MESSAGES: :[[@LINE+3]]:6: warning: function template specialization 'templateFunctionWithSeparateSpecializationDeclarationAndDefinition<int>' has a primary template +// CHECK-MESSAGES: :[[@LINE-10]]:6: note: primary template declaration seen here +// CHECK-MESSAGES: :[[@LINE+1]]:6: note: differing parameters are named here: ('c'), while in primary template declaration: ('a') +void templateFunctionWithSeparateSpecializationDeclarationAndDefinition(int c) { c; } + +////////////////////////////////////////////////////// + +template<typename T> +class ClassTemplate +{ +public: +// CHECK-MESSAGES: :[[@LINE+4]]:10: warning: function 'ClassTemplate::functionInClassTemplateWithSeparateDeclarationAndDefinition' has a definition +// CHECK-MESSAGES: :[[@LINE+7]]:24: note: definition seen here +// CHECK-MESSAGES: :[[@LINE+2]]:10: note: differing parameters are named here: ('a'), while in definition: ('b') +// CHECK-FIXES: void functionInClassTemplateWithSeparateDeclarationAndDefinition(int b); + void functionInClassTemplateWithSeparateDeclarationAndDefinition(int a); +}; + +template<typename T> +void ClassTemplate<T>::functionInClassTemplateWithSeparateDeclarationAndDefinition(int b) { b; } + +////////////////////////////////////////////////////// + +class Class +{ +public: + template<typename T> +// CHECK-MESSAGES: :[[@LINE+4]]:8: warning: function 'Class::memberFunctionTemplateWithSeparateDeclarationAndDefinition' has a definition +// CHECK-MESSAGES: :[[@LINE+12]]:13: note: definition seen here +// CHECK-MESSAGES: :[[@LINE+2]]:8: note: differing parameters are named here: ('a'), while in definition: ('b') +// CHECK-FIXES: void memberFunctionTemplateWithSeparateDeclarationAndDefinition(T b); + void memberFunctionTemplateWithSeparateDeclarationAndDefinition(T a); + + template<typename T> + void memberFunctionTemplateWithSpecializations(T a) { a; } +}; + +////////////////////////////////////////////////////// + +template<typename T> +void Class::memberFunctionTemplateWithSeparateDeclarationAndDefinition(T b) { b; } + +////////////////////////////////////////////////////// + +template<> +// CHECK-MESSAGES: :[[@LINE+3]]:13: warning: function template specialization 'Class::memberFunctionTemplateWithSpecializations<int>' has a primary template +// CHECK-MESSAGES: :[[@LINE-12]]:8: note: primary template declaration seen here +// CHECK-MESSAGES: :[[@LINE+1]]:13: note: differing parameters are named here: ('b'), while in primary template declaration: ('a') +void Class::memberFunctionTemplateWithSpecializations(int b) { b; } + +template<> +// CHECK-MESSAGES: :[[@LINE+3]]:13: warning: function template specialization 'Class::memberFunctionTemplateWithSpecializations<float>' has a primary template +// CHECK-MESSAGES: :[[@LINE-18]]:8: note: primary template declaration seen here +// CHECK-MESSAGES: :[[@LINE+1]]:13: note: differing parameters are named here: ('c'), while 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 differently named parameters [readability-inconsistent-declaration-parameter-name] +DECLARE_FUNCTION_WITH_PARAM_NAME(macroFunction, a); +// CHECK-MESSAGES: :[[@LINE+2]]:34: note: 1st inconsistent declaration seen here +// CHECK-MESSAGES: :[[@LINE+1]]:34: note: differing parameters are named here: ('b'), while in other declaration: ('a') +DECLARE_FUNCTION_WITH_PARAM_NAME(macroFunction, b); Index: docs/clang-tidy/checks/readability-inconsistent-declaration-parameter-name.rst =================================================================== --- docs/clang-tidy/checks/readability-inconsistent-declaration-parameter-name.rst +++ docs/clang-tidy/checks/readability-inconsistent-declaration-parameter-name.rst @@ -0,0 +1,29 @@ +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. \ No newline at end of file Index: docs/clang-tidy/checks/list.rst =================================================================== --- docs/clang-tidy/checks/list.rst +++ docs/clang-tidy/checks/list.rst @@ -47,8 +47,9 @@ 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 readability-shrink-to-fit readability-simplify-boolean-expr \ No newline at end of file Index: clang-tidy/readability/ReadabilityTidyModule.cpp =================================================================== --- clang-tidy/readability/ReadabilityTidyModule.cpp +++ 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" @@ -38,6 +39,8 @@ "readability-function-size"); CheckFactories.registerCheck<IdentifierNamingCheck>( "readability-identifier-naming"); + CheckFactories.registerCheck<InconsistentDeclarationParameterNameCheck>( + "readability-inconsistent-declaration-parameter-name"); CheckFactories.registerCheck<readability::NamedParameterCheck>( "readability-named-parameter"); CheckFactories.registerCheck<RedundantSmartptrGetCheck>( Index: clang-tidy/readability/InconsistentDeclarationParameterNameCheck.h =================================================================== --- clang-tidy/readability/InconsistentDeclarationParameterNameCheck.h +++ clang-tidy/readability/InconsistentDeclarationParameterNameCheck.h @@ -0,0 +1,57 @@ +//===- 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: + class FormatDiagnosticContext; + void formatDiagnosticsInCaseOfOnlyFunctionDeclarationsPresent( + const FormatDiagnosticContext &Context); + void formatDiagnosticsInOtherCases(const FormatDiagnosticContext &Context, + StringRef FunctionDescription, + StringRef ParameterSourceDescription); + + struct FormatParamsDiagnosticContext; + void + formatDifferingParamsDiagnostic( + const FormatParamsDiagnosticContext &Context); + + void markRedeclarationsAsVisited(const FunctionDecl *FunctionDeclaration); + + llvm::DenseSet<const FunctionDecl *> VisitedDeclarations; +}; + +} // namespace readability +} // namespace tidy +} // namespace clang + +#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_READABILITY_INCONSISTENT_DECLARATION_PARAMETER_NAME_H Index: clang-tidy/readability/InconsistentDeclarationParameterNameCheck.cpp =================================================================== --- clang-tidy/readability/InconsistentDeclarationParameterNameCheck.cpp +++ clang-tidy/readability/InconsistentDeclarationParameterNameCheck.cpp @@ -0,0 +1,343 @@ +//===--- 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 <algorithm> +#include <functional> +#include <sstream> + +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<DifferingParamInfo, 10>; + +struct InconsistentDeclarationInfo { + InconsistentDeclarationInfo(SourceLocation DeclarationLocation, + DifferingParamsContainer &&DifferingParams) + : DeclarationLocation(DeclarationLocation), + DifferingParams(std::move(DifferingParams)) {} + + SourceLocation DeclarationLocation; + DifferingParamsContainer DifferingParams; +}; + +using InconsistentDeclarationsContainer = + llvm::SmallVector<InconsistentDeclarationInfo, 2>; + +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<StringRef(const DifferingParamInfo &)> ChooseParamName) { + llvm::SmallVector<char, 40> 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(); +} + +} // anonymous namespace + +struct InconsistentDeclarationParameterNameCheck::FormatDiagnosticContext { + const FunctionDecl *ParameterSourceDeclaration; + const FunctionDecl *OriginalDeclaration; + const InconsistentDeclarationsContainer &InconsistentDeclarations; +}; + +struct InconsistentDeclarationParameterNameCheck:: + FormatParamsDiagnosticContext { + const SourceLocation &Location; + StringRef OtherDeclarationDescription; + const DifferingParamsContainer &DifferingParams; +}; + +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>("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; + } + + FormatDiagnosticContext Context{ParameterSourceDeclaration, + OriginalDeclaration, + InconsistentDeclarations}; + if (OriginalDeclaration->getTemplatedKind() == + FunctionDecl::TK_FunctionTemplateSpecialization) { + formatDiagnosticsInOtherCases(Context, "function template specialization", + "primary template declaration"); + } else if (ParameterSourceDeclaration->isThisDeclarationADefinition()) { + formatDiagnosticsInOtherCases(Context, "function", "definition"); + } else { + formatDiagnosticsInCaseOfOnlyFunctionDeclarationsPresent(Context); + } + + markRedeclarationsAsVisited(OriginalDeclaration); +} + +void InconsistentDeclarationParameterNameCheck::markRedeclarationsAsVisited( + const FunctionDecl *OriginalDeclaration) { + for (const FunctionDecl *Redecl : OriginalDeclaration->redecls()) { + VisitedDeclarations.insert(Redecl); + } +} + +void InconsistentDeclarationParameterNameCheck:: + formatDiagnosticsInCaseOfOnlyFunctionDeclarationsPresent( + const FormatDiagnosticContext &Context) { + { + diag(Context.OriginalDeclaration->getLocation(), + "function %q0 has %1 other declaration%s1 with differently named " + "parameters") + << Context.OriginalDeclaration + << static_cast<int>(Context.InconsistentDeclarations.size()); + } + + int Count = 1; + for (const InconsistentDeclarationInfo &InconsistentDeclaration : + Context.InconsistentDeclarations) { + diag(InconsistentDeclaration.DeclarationLocation, + "%ordinal0 inconsistent declaration seen here", + DiagnosticIDs::Level::Note) + << Count; + + formatDifferingParamsDiagnostic(FormatParamsDiagnosticContext{ + InconsistentDeclaration.DeclarationLocation, "other declaration", + InconsistentDeclaration.DifferingParams}); + + ++Count; + } +} + +void InconsistentDeclarationParameterNameCheck::formatDiagnosticsInOtherCases( + const FormatDiagnosticContext &Context, StringRef FunctionDescription, + StringRef ParameterSourceDescription) { + for (const InconsistentDeclarationInfo &InconsistentDeclaration : + Context.InconsistentDeclarations) { + diag(InconsistentDeclaration.DeclarationLocation, + "%0 %q1 has a %2 with differently named parameters") + << FunctionDescription << Context.OriginalDeclaration + << ParameterSourceDescription; + + diag(Context.ParameterSourceDeclaration->getLocation(), "%0 seen here", + DiagnosticIDs::Level::Note) + << ParameterSourceDescription; + + formatDifferingParamsDiagnostic(FormatParamsDiagnosticContext{ + InconsistentDeclaration.DeclarationLocation, ParameterSourceDescription, + InconsistentDeclaration.DifferingParams}); + } +} + +void InconsistentDeclarationParameterNameCheck::formatDifferingParamsDiagnostic( + const FormatParamsDiagnosticContext &Context) { + auto ChooseOtherName = + [](const DifferingParamInfo &ParamInfo) { return ParamInfo.OtherName; }; + auto ChooseSourceName = + [](const DifferingParamInfo &ParamInfo) { return ParamInfo.SourceName; }; + + auto ParamDiag = + diag(Context.Location, + "differing parameters are named here: (%0), while in %1: (%2)", + DiagnosticIDs::Level::Note) + << joinParameterNames(Context.DifferingParams, ChooseOtherName) + << Context.OtherDeclarationDescription + << joinParameterNames(Context.DifferingParams, ChooseSourceName); + + for (const DifferingParamInfo &ParamInfo : Context.DifferingParams) { + if (ParamInfo.GenerateFixItHint) { + ParamDiag << FixItHint::CreateReplacement( + CharSourceRange::getTokenRange(ParamInfo.OtherNameRange), + ParamInfo.SourceName); + } + } +} + +} // namespace readability +} // namespace tidy +} // namespace clang Index: clang-tidy/readability/CMakeLists.txt =================================================================== --- clang-tidy/readability/CMakeLists.txt +++ clang-tidy/readability/CMakeLists.txt @@ -6,6 +6,7 @@ ElseAfterReturnCheck.cpp FunctionSizeCheck.cpp IdentifierNamingCheck.cpp + InconsistentDeclarationParameterNameCheck.cpp NamedParameterCheck.cpp NamespaceCommentCheck.cpp ReadabilityTidyModule.cpp
_______________________________________________ cfe-commits mailing list cfe-commits@lists.llvm.org http://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits