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

Reply via email to