Address review comments.

Hi alexfh,

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

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

Files:
  clang-tidy/misc/MiscTidyModule.cpp
  clang-tidy/misc/MiscTidyModule.h
  test/clang-tidy/arg-comments.cpp
Index: clang-tidy/misc/MiscTidyModule.cpp
===================================================================
--- clang-tidy/misc/MiscTidyModule.cpp
+++ clang-tidy/misc/MiscTidyModule.cpp
@@ -7,16 +7,168 @@
 //
 //===----------------------------------------------------------------------===//
 
+#include "MiscTidyModule.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;
+
+    // Other parameters must be an edit distance at least 2 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 + 2);
+    if (OtherED < ThisED + 2)
+      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;
+
+    auto Comments =
+        getCommentsInRange(Ctx, SourceRange(BeginSLoc, EndSLoc));
+    for (auto Comment : Comments) {
+      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::ArrayRef<const Expr *>(Call->getArgs(), Call->getNumArgs()));
+  } else {
+    auto Construct = cast<CXXConstructExpr>(E);
+    checkCallArgs(Result.Context, Construct->getConstructor(),
+                  Construct->getParenOrBraceRange().getBegin(),
+                  llvm::ArrayRef<const Expr *>(Construct->getArgs(),
+                                               Construct->getNumArgs()));
+  }
+}
+
 class MiscModule : public ClangTidyModule {
 public:
   void addCheckFactories(ClangTidyCheckFactories &CheckFactories) override {
+     CheckFactories.addCheckFactory(
+       "misc-argument-comment",
+       new ClangTidyCheckFactory<ArgumentCommentCheck>());
   }
 };
 
Index: clang-tidy/misc/MiscTidyModule.h
===================================================================
--- /dev/null
+++ clang-tidy/misc/MiscTidyModule.h
@@ -0,0 +1,40 @@
+//===--- MiscTidyModule.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_MISC_TIDY_MODULE_H
+#define LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_MISC_MISC_TIDY_MODULE_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_MISC_TIDY_MODULE_H
Index: test/clang-tidy/arg-comments.cpp
===================================================================
--- /dev/null
+++ test/clang-tidy/arg-comments.cpp
@@ -0,0 +1,27 @@
+// 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: [[@LINE+4]]:8: warning: argument name 'xxxy' in comment does not match parameter name 'xxxx'
+  // CHECK-NEXT: ffff
+  // CHECK-NEXT: ^
+  // CHECK-NEXT: /*xxxx=*/
+  ffff(/*xxxy=*/0, 0);
+}
+
+// CHECK-NOT: warning
_______________________________________________
cfe-commits mailing list
[email protected]
http://lists.cs.uiuc.edu/mailman/listinfo/cfe-commits

Reply via email to