Address review comments.

Hi alexfh,

http://llvm-reviews.chandlerc.com/D2914

CHANGE SINCE LAST DIFF
  http://llvm-reviews.chandlerc.com/D2914?vs=7584&id=7871#toc

Files:
  clang-tidy/misc/ArgumentCommentCheck.cpp
  clang-tidy/misc/ArgumentCommentCheck.h
  clang-tidy/misc/CMakeLists.txt
  clang-tidy/misc/MiscTidyModule.cpp
  test/clang-tidy/arg-comments.cpp
  unittests/clang-tidy/CMakeLists.txt
  unittests/clang-tidy/MiscModuleTest.cpp
Index: clang-tidy/misc/ArgumentCommentCheck.cpp
===================================================================
--- /dev/null
+++ clang-tidy/misc/ArgumentCommentCheck.cpp
@@ -0,0 +1,166 @@
+//===--- ArgumentCommentCheck.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 "ArgumentCommentCheck.h"
+#include "../ClangTidy.h"
+#include "../ClangTidyModule.h"
+#include "../ClangTidyModuleRegistry.h"
+#include "clang/AST/ASTContext.h"
+#include "clang/ASTMatchers/ASTMatchFinder.h"
+#include "clang/Lex/Lexer.h"
+#include "clang/Lex/Token.h"
+
+using namespace clang::ast_matchers;
+
+namespace clang {
+namespace tidy {
+
+void ArgumentCommentCheck::registerMatchers(MatchFinder *Finder) {
+  Finder->addMatcher(callExpr(unless(operatorCallExpr())).bind("expr"), this);
+  Finder->addMatcher(constructExpr().bind("expr"), this);
+}
+
+std::vector<std::pair<SourceLocation, StringRef>>
+ArgumentCommentCheck::getCommentsInRange(ASTContext *Ctx, SourceRange Range) {
+  std::vector<std::pair<SourceLocation, StringRef>> Comments;
+  auto &SM = Ctx->getSourceManager();
+  std::pair<FileID, unsigned> BeginLoc = SM.getDecomposedLoc(Range.getBegin()),
+                              EndLoc = SM.getDecomposedLoc(Range.getEnd());
+
+  if (BeginLoc.first != EndLoc.first)
+    return Comments;
+
+  bool Invalid = false;
+  StringRef Buffer = SM.getBufferData(BeginLoc.first, &Invalid);
+  if (Invalid)
+    return Comments;
+
+  const char *StrData = Buffer.data() + BeginLoc.second;
+
+  Lexer TheLexer(SM.getLocForStartOfFile(BeginLoc.first), Ctx->getLangOpts(),
+                 Buffer.begin(), StrData, Buffer.end());
+  TheLexer.SetCommentRetentionState(true);
+
+  while (true) {
+    Token Tok;
+    if (TheLexer.LexFromRawLexer(Tok))
+      break;
+    if (Tok.getLocation() == Range.getEnd() || Tok.getKind() == tok::eof)
+      break;
+
+    if (Tok.getKind() == tok::comment) {
+      std::pair<FileID, unsigned> CommentLoc =
+          SM.getDecomposedLoc(Tok.getLocation());
+      assert(CommentLoc.first == BeginLoc.first);
+      Comments.emplace_back(
+          Tok.getLocation(),
+          StringRef(Buffer.begin() + CommentLoc.second, Tok.getLength()));
+    }
+  }
+
+  return Comments;
+}
+
+bool
+ArgumentCommentCheck::isLikelyTypo(llvm::ArrayRef<ParmVarDecl *> Params,
+                                   StringRef ArgName, unsigned ArgIndex) {
+  std::string ArgNameLower = ArgName.lower();
+  unsigned UpperBound = (ArgName.size() + 2) / 3 + 1;
+  unsigned ThisED = StringRef(ArgNameLower).edit_distance(
+      Params[ArgIndex]->getIdentifier()->getName().lower(),
+      /*AllowReplacements=*/true, UpperBound);
+  if (ThisED >= UpperBound)
+    return false;
+
+  for (const auto &Param : Params) {
+    if (&Param - Params.begin() == ArgIndex)
+      continue;
+    IdentifierInfo *II = Param->getIdentifier();
+    if (!II)
+      continue;
+
+    const unsigned Threshold = 2;
+    // Other parameters must be an edit distance at least Threshold more away
+    // from this parameter. This gives us greater confidence that this is a typo
+    // of this parameter and not one with a similar name.
+    unsigned OtherED = StringRef(ArgNameLower).edit_distance(
+        II->getName().lower(),
+        /*AllowReplacements=*/true, ThisED + Threshold);
+    if (OtherED < ThisED + Threshold)
+      return false;
+  }
+
+  return true;
+}
+
+void ArgumentCommentCheck::checkCallArgs(ASTContext *Ctx,
+                                         const FunctionDecl *Callee,
+                                         SourceLocation ArgBeginLoc,
+                                         llvm::ArrayRef<const Expr *> Args) {
+  Callee = Callee->getFirstDecl();
+  for (unsigned i = 0,
+                e = std::min<unsigned>(Args.size(), Callee->getNumParams());
+       i != e; ++i) {
+    const ParmVarDecl *PVD = Callee->getParamDecl(i);
+    IdentifierInfo *II = PVD->getIdentifier();
+    if (!II)
+      continue;
+
+    SourceLocation BeginSLoc, EndSLoc = Args[i]->getLocStart();
+    if (i == 0)
+      BeginSLoc = ArgBeginLoc;
+    else
+      BeginSLoc = Args[i - 1]->getLocEnd();
+    if (BeginSLoc.isMacroID() || EndSLoc.isMacroID())
+      continue;
+
+    for (auto Comment :
+         getCommentsInRange(Ctx, SourceRange(BeginSLoc, EndSLoc))) {
+      llvm::SmallVector<StringRef, 2> Matches;
+      if (IdentRE.match(Comment.second, &Matches)) {
+        if (Matches[2] != II->getName()) {
+          {
+            DiagnosticBuilder Diag =
+                diag(Comment.first, "argument name '%0' in comment does not "
+                                    "match parameter name %1")
+                << Matches[2] << II;
+            if (isLikelyTypo(Callee->parameters(), Matches[2], i)) {
+              Diag << FixItHint::CreateReplacement(
+                          Comment.first,
+                          (Matches[1] + II->getName() + Matches[3]).str());
+            }
+          }
+          diag(PVD->getLocation(), "%0 declared here", DiagnosticIDs::Note)
+              << II;
+        }
+      }
+    }
+  }
+}
+
+void ArgumentCommentCheck::check(const MatchFinder::MatchResult &Result) {
+  const Expr *E = Result.Nodes.getNodeAs<Expr>("expr");
+  if (auto Call = dyn_cast<CallExpr>(E)) {
+    const FunctionDecl *Callee = Call->getDirectCallee();
+    if (!Callee)
+      return;
+
+    checkCallArgs(Result.Context, Callee, Call->getCallee()->getLocEnd(),
+                  llvm::makeArrayRef(Call->getArgs(), Call->getNumArgs()));
+  } else {
+    auto Construct = cast<CXXConstructExpr>(E);
+    checkCallArgs(
+        Result.Context, Construct->getConstructor(),
+        Construct->getParenOrBraceRange().getBegin(),
+        llvm::makeArrayRef(Construct->getArgs(), Construct->getNumArgs()));
+  }
+}
+
+} // namespace tidy
+} // namespace clang
Index: clang-tidy/misc/ArgumentCommentCheck.h
===================================================================
--- /dev/null
+++ clang-tidy/misc/ArgumentCommentCheck.h
@@ -0,0 +1,40 @@
+//===--- ArgumentCommentCheck.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_MISC_ARG_COMMENT_CHECK_H
+#define LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_MISC_ARG_COMMENT_CHECK_H
+
+#include "../ClangTidy.h"
+#include "llvm/Support/Regex.h"
+
+namespace clang {
+namespace tidy {
+
+/// \brief Checks that argument comments match parameter names.
+class ArgumentCommentCheck : public ClangTidyCheck {
+public:
+  void registerMatchers(ast_matchers::MatchFinder *Finder) override;
+  void check(const ast_matchers::MatchFinder::MatchResult &Result) override;
+
+private:
+  llvm::Regex IdentRE{ "^(/\\* *)([_A-Za-z][_A-Za-z0-9]*)( *= *\\*/)$" };
+
+  bool isLikelyTypo(llvm::ArrayRef<ParmVarDecl *> Params, StringRef ArgName,
+                    unsigned ArgIndex);
+  std::vector<std::pair<SourceLocation, StringRef>>
+  getCommentsInRange(ASTContext *Ctx, SourceRange Range);
+  void checkCallArgs(ASTContext *Ctx, const FunctionDecl *Callee,
+                     SourceLocation ArgBeginLoc,
+                     llvm::ArrayRef<const Expr *> Args);
+};
+
+} // namespace tidy
+} // namespace clang
+
+#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_MISC_ARG_COMMENT_CHECK_H
Index: clang-tidy/misc/CMakeLists.txt
===================================================================
--- clang-tidy/misc/CMakeLists.txt
+++ clang-tidy/misc/CMakeLists.txt
@@ -1,6 +1,7 @@
 set(LLVM_LINK_COMPONENTS support)
 
 add_clang_library(clangTidyMiscModule
+  ArgumentCommentCheck.cpp
   MiscTidyModule.cpp
 
   LINK_LIBS
Index: clang-tidy/misc/MiscTidyModule.cpp
===================================================================
--- clang-tidy/misc/MiscTidyModule.cpp
+++ clang-tidy/misc/MiscTidyModule.cpp
@@ -10,13 +10,17 @@
 #include "../ClangTidy.h"
 #include "../ClangTidyModule.h"
 #include "../ClangTidyModuleRegistry.h"
+#include "ArgumentCommentCheck.h"
 
 namespace clang {
 namespace tidy {
 
 class MiscModule : public ClangTidyModule {
 public:
   void addCheckFactories(ClangTidyCheckFactories &CheckFactories) override {
+     CheckFactories.addCheckFactory(
+       "misc-argument-comment",
+       new ClangTidyCheckFactory<ArgumentCommentCheck>());
   }
 };
 
Index: test/clang-tidy/arg-comments.cpp
===================================================================
--- /dev/null
+++ test/clang-tidy/arg-comments.cpp
@@ -0,0 +1,21 @@
+// RUN: clang-tidy --checks=misc-argument-comment %s -- | FileCheck %s
+
+// FIXME: clang-tidy should provide a -verify mode to make writing these checks
+// easier and more accurate.
+
+// CHECK-NOT: warning
+
+void f(int x, int y);
+
+void ffff(int xxxx, int yyyy);
+
+void g() {
+  // CHECK: [[@LINE+5]]:5: warning: argument name 'y' in comment does not match parameter name 'x'
+  // CHECK: :8:12: note: 'x' declared here
+  // CHECK: [[@LINE+3]]:14: warning: argument name 'z' in comment does not match parameter name 'y'
+  // CHECK: :8:19: note: 'y' declared here
+  // CHECK-NOT: warning
+  f(/*y=*/0, /*z=*/0);
+}
+
+// CHECK-NOT: warning
Index: unittests/clang-tidy/CMakeLists.txt
===================================================================
--- unittests/clang-tidy/CMakeLists.txt
+++ unittests/clang-tidy/CMakeLists.txt
@@ -8,7 +8,8 @@
 
 add_extra_unittest(ClangTidyTests
   LLVMModuleTest.cpp
-  GoogleModuleTest.cpp)
+  GoogleModuleTest.cpp
+  MiscModuleTest.cpp)
 
 target_link_libraries(ClangTidyTests
   clangAST
@@ -18,5 +19,6 @@
   clangTidy
   clangTidyGoogleModule
   clangTidyLLVMModule
+  clangTidyMiscModule
   clangTooling
   )
Index: unittests/clang-tidy/MiscModuleTest.cpp
===================================================================
--- /dev/null
+++ unittests/clang-tidy/MiscModuleTest.cpp
@@ -0,0 +1,41 @@
+#include "ClangTidyTest.h"
+#include "google/GoogleTidyModule.h"
+#include "misc/ArgumentCommentCheck.h"
+#include "gtest/gtest.h"
+
+namespace clang {
+namespace tidy {
+namespace test {
+
+#define EXPECT_NO_CHANGES(Check, Code)                                         \
+  EXPECT_EQ(Code, runCheckOnCode<Check>(Code))
+
+TEST(ArgumentCommentCheckTest, CorrectComments) {
+  EXPECT_NO_CHANGES(ArgumentCommentCheck,
+                    "void f(int x, int y); void g() { f(/*x=*/0, /*y=*/0); }");
+  EXPECT_NO_CHANGES(ArgumentCommentCheck,
+                    "struct C { C(int x, int y); }; C c(/*x=*/0, /*y=*/0);");
+}
+
+TEST(ArgumentCommentCheckTest, ThisEditDistanceAboveThreshold) {
+  EXPECT_NO_CHANGES(ArgumentCommentCheck,
+                    "void f(int xxx); void g() { f(/*xyz=*/0); }");
+}
+
+TEST(ArgumentCommentCheckTest, OtherEditDistanceAboveThreshold) {
+  EXPECT_EQ("void f(int xxx, int yyy); void g() { f(/*xxx=*/0, 0); }",
+            runCheckOnCode<ArgumentCommentCheck>(
+                "void f(int xxx, int yyy); void g() { f(/*Xxx=*/0, 0); }"));
+  EXPECT_EQ("struct C { C(int xxx, int yyy); }; C c(/*xxx=*/0, 0);",
+            runCheckOnCode<ArgumentCommentCheck>(
+                "struct C { C(int xxx, int yyy); }; C c(/*Xxx=*/0, 0);"));
+}
+
+TEST(ArgumentCommentCheckTest, OtherEditDistanceBelowThreshold) {
+  EXPECT_NO_CHANGES(ArgumentCommentCheck,
+                    "void f(int xxx, int yyy); void g() { f(/*xxy=*/0, 0); }");
+}
+
+} // namespace test
+} // namespace tidy
+} // namespace clang
_______________________________________________
cfe-commits mailing list
[email protected]
http://lists.cs.uiuc.edu/mailman/listinfo/cfe-commits

Reply via email to