[clang-tools-extra] r361418 - [clang-tidy] Add support for writing a check as a Transformer rewrite rule.

Yitzhak Mandelbaum via cfe-commits cfe-commits at lists.llvm.org
Wed May 22 11:56:18 PDT 2019


Author: ymandel
Date: Wed May 22 11:56:18 2019
New Revision: 361418

URL: http://llvm.org/viewvc/llvm-project?rev=361418&view=rev
Log:
[clang-tidy] Add support for writing a check as a Transformer rewrite rule.

This revision introduces an adaptor from Transformer's rewrite rules
(`clang::tooling::RewriteRule`) to `ClangTidyCheck`.  For example, given a
RewriteRule `MyCheckAsRewriteRule`, it lets one define a tidy check as follows:

```
class MyTidyCheck : public TransformerClangTidyCheck {
 public:
  MyTidyCheck(StringRef Name, ClangTidyContext *Context)
      : TransformerClangTidyCheck(MyCheckAsRewriteRule, Name, Context) {}
};
```

Reviewers: aaron.ballman

Subscribers: mgorny, xazax.hun, cfe-commits, ilya-biryukov

Tags: #clang

Differential Revision: https://reviews.llvm.org/D61386

Added:
    clang-tools-extra/trunk/clang-tidy/utils/TransformerClangTidyCheck.cpp
    clang-tools-extra/trunk/clang-tidy/utils/TransformerClangTidyCheck.h
    clang-tools-extra/trunk/unittests/clang-tidy/TransformerClangTidyCheckTest.cpp
Modified:
    clang-tools-extra/trunk/clang-tidy/utils/CMakeLists.txt
    clang-tools-extra/trunk/unittests/clang-tidy/CMakeLists.txt

Modified: clang-tools-extra/trunk/clang-tidy/utils/CMakeLists.txt
URL: http://llvm.org/viewvc/llvm-project/clang-tools-extra/trunk/clang-tidy/utils/CMakeLists.txt?rev=361418&r1=361417&r2=361418&view=diff
==============================================================================
--- clang-tools-extra/trunk/clang-tidy/utils/CMakeLists.txt (original)
+++ clang-tools-extra/trunk/clang-tidy/utils/CMakeLists.txt Wed May 22 11:56:18 2019
@@ -13,6 +13,7 @@ add_clang_library(clangTidyUtils
   LexerUtils.cpp
   NamespaceAliaser.cpp
   OptionsUtils.cpp
+  TransformerClangTidyCheck.cpp
   TypeTraits.cpp
   UsingInserter.cpp
 
@@ -22,4 +23,5 @@ add_clang_library(clangTidyUtils
   clangBasic
   clangLex
   clangTidy
+  clangToolingRefactor
   )

Added: clang-tools-extra/trunk/clang-tidy/utils/TransformerClangTidyCheck.cpp
URL: http://llvm.org/viewvc/llvm-project/clang-tools-extra/trunk/clang-tidy/utils/TransformerClangTidyCheck.cpp?rev=361418&view=auto
==============================================================================
--- clang-tools-extra/trunk/clang-tidy/utils/TransformerClangTidyCheck.cpp (added)
+++ clang-tools-extra/trunk/clang-tidy/utils/TransformerClangTidyCheck.cpp Wed May 22 11:56:18 2019
@@ -0,0 +1,63 @@
+//===---------- TransformerClangTidyCheck.cpp - clang-tidy ----------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "TransformerClangTidyCheck.h"
+
+namespace clang {
+namespace tidy {
+namespace utils {
+using tooling::RewriteRule;
+
+void TransformerClangTidyCheck::registerMatchers(
+    ast_matchers::MatchFinder *Finder) {
+  Finder->addDynamicMatcher(tooling::detail::buildMatcher(Rule), this);
+}
+
+void TransformerClangTidyCheck::check(
+    const ast_matchers::MatchFinder::MatchResult &Result) {
+  if (Result.Context->getDiagnostics().hasErrorOccurred())
+    return;
+
+  // Verify the existence and validity of the AST node that roots this rule.
+  const ast_matchers::BoundNodes::IDToNodeMap &NodesMap = Result.Nodes.getMap();
+  auto Root = NodesMap.find(RewriteRule::RootID);
+  assert(Root != NodesMap.end() && "Transformation failed: missing root node.");
+  SourceLocation RootLoc = Result.SourceManager->getExpansionLoc(
+      Root->second.getSourceRange().getBegin());
+  assert(RootLoc.isValid() && "Invalid location for Root node of match.");
+
+  RewriteRule::Case Case = tooling::detail::findSelectedCase(Result, Rule);
+  Expected<SmallVector<tooling::detail::Transformation, 1>> Transformations =
+      tooling::detail::translateEdits(Result, Case.Edits);
+  if (!Transformations) {
+    llvm::errs() << "Rewrite failed: "
+                 << llvm::toString(Transformations.takeError()) << "\n";
+    return;
+  }
+
+  // No rewrite applied, but no error encountered either.
+  if (Transformations->empty())
+    return;
+
+  StringRef Message = "no explanation";
+  if (Case.Explanation) {
+    if (Expected<std::string> E = Case.Explanation(Result))
+      Message = *E;
+    else
+      llvm::errs() << "Error in explanation: " << llvm::toString(E.takeError())
+                   << "\n";
+  }
+  DiagnosticBuilder Diag = diag(RootLoc, Message);
+  for (const auto &T : *Transformations) {
+    Diag << FixItHint::CreateReplacement(T.Range, T.Replacement);
+  }
+}
+
+} // namespace utils
+} // namespace tidy
+} // namespace clang

Added: clang-tools-extra/trunk/clang-tidy/utils/TransformerClangTidyCheck.h
URL: http://llvm.org/viewvc/llvm-project/clang-tools-extra/trunk/clang-tidy/utils/TransformerClangTidyCheck.h?rev=361418&view=auto
==============================================================================
--- clang-tools-extra/trunk/clang-tidy/utils/TransformerClangTidyCheck.h (added)
+++ clang-tools-extra/trunk/clang-tidy/utils/TransformerClangTidyCheck.h Wed May 22 11:56:18 2019
@@ -0,0 +1,49 @@
+//===---------- TransformerClangTidyCheck.h - clang-tidy ------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_TRANSFORMER_CLANG_TIDY_CHECK_H
+#define LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_TRANSFORMER_CLANG_TIDY_CHECK_H
+
+#include "../ClangTidy.h"
+#include "clang/ASTMatchers/ASTMatchFinder.h"
+#include "clang/Tooling/Refactoring/Transformer.h"
+#include <deque>
+#include <vector>
+
+namespace clang {
+namespace tidy {
+namespace utils {
+
+// A base class for defining a ClangTidy check based on a `RewriteRule`.
+//
+// For example, given a rule `MyCheckAsRewriteRule`, one can define a tidy check
+// as follows:
+//
+// class MyCheck : public TransformerClangTidyCheck {
+//  public:
+//   MyCheck(StringRef Name, ClangTidyContext *Context)
+//       : TransformerClangTidyCheck(MyCheckAsRewriteRule, Name, Context) {}
+// };
+class TransformerClangTidyCheck : public ClangTidyCheck {
+public:
+  TransformerClangTidyCheck(tooling::RewriteRule R, StringRef Name,
+                            ClangTidyContext *Context)
+      : ClangTidyCheck(Name, Context), Rule(std::move(R)) {}
+
+  void registerMatchers(ast_matchers::MatchFinder *Finder) final;
+  void check(const ast_matchers::MatchFinder::MatchResult &Result) final;
+
+private:
+  tooling::RewriteRule Rule;
+};
+
+} // namespace utils
+} // namespace tidy
+} // namespace clang
+
+#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_TRANSFORMER_CLANG_TIDY_CHECK_H

Modified: clang-tools-extra/trunk/unittests/clang-tidy/CMakeLists.txt
URL: http://llvm.org/viewvc/llvm-project/clang-tools-extra/trunk/unittests/clang-tidy/CMakeLists.txt?rev=361418&r1=361417&r2=361418&view=diff
==============================================================================
--- clang-tools-extra/trunk/unittests/clang-tidy/CMakeLists.txt (original)
+++ clang-tools-extra/trunk/unittests/clang-tidy/CMakeLists.txt Wed May 22 11:56:18 2019
@@ -17,6 +17,7 @@ add_extra_unittest(ClangTidyTests
   OverlappingReplacementsTest.cpp
   UsingInserterTest.cpp
   ReadabilityModuleTest.cpp
+  TransformerClangTidyCheckTest.cpp
   )
 
 target_link_libraries(ClangTidyTests
@@ -36,4 +37,5 @@ target_link_libraries(ClangTidyTests
   clangTidyUtils
   clangTooling
   clangToolingCore
+  clangToolingRefactor
   )

Added: clang-tools-extra/trunk/unittests/clang-tidy/TransformerClangTidyCheckTest.cpp
URL: http://llvm.org/viewvc/llvm-project/clang-tools-extra/trunk/unittests/clang-tidy/TransformerClangTidyCheckTest.cpp?rev=361418&view=auto
==============================================================================
--- clang-tools-extra/trunk/unittests/clang-tidy/TransformerClangTidyCheckTest.cpp (added)
+++ clang-tools-extra/trunk/unittests/clang-tidy/TransformerClangTidyCheckTest.cpp Wed May 22 11:56:18 2019
@@ -0,0 +1,68 @@
+//===---- TransformerClangTidyCheckTest.cpp - clang-tidy ------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "../clang-tidy/utils/TransformerClangTidyCheck.h"
+#include "ClangTidyTest.h"
+#include "clang/ASTMatchers/ASTMatchers.h"
+#include "clang/Tooling/Refactoring/RangeSelector.h"
+#include "clang/Tooling/Refactoring/Stencil.h"
+#include "clang/Tooling/Refactoring/Transformer.h"
+#include "gtest/gtest.h"
+
+namespace clang {
+namespace tidy {
+namespace utils {
+namespace {
+using tooling::RewriteRule;
+
+// Invert the code of an if-statement, while maintaining its semantics.
+RewriteRule invertIf() {
+  using namespace ::clang::ast_matchers;
+  using tooling::change;
+  using tooling::node;
+  using tooling::statement;
+  using tooling::stencil::cat;
+
+  StringRef C = "C", T = "T", E = "E";
+  return tooling::makeRule(ifStmt(hasCondition(expr().bind(C)),
+                                  hasThen(stmt().bind(T)),
+                                  hasElse(stmt().bind(E))),
+                           change(statement(RewriteRule::RootID),
+                                  cat("if(!(", node(C), ")) ", statement(E),
+                                      " else ", statement(T))));
+}
+
+class IfInverterCheck : public TransformerClangTidyCheck {
+public:
+  IfInverterCheck(StringRef Name, ClangTidyContext *Context)
+      : TransformerClangTidyCheck(invertIf(), Name, Context) {}
+};
+
+// Basic test of using a rewrite rule as a ClangTidy.
+TEST(TransformerClangTidyCheckTest, Basic) {
+  const std::string Input = R"cc(
+    void log(const char* msg);
+    void foo() {
+      if (10 > 1.0)
+        log("oh no!");
+      else
+        log("ok");
+    }
+  )cc";
+  const std::string Expected = R"(
+    void log(const char* msg);
+    void foo() {
+      if(!(10 > 1.0)) log("ok"); else log("oh no!");
+    }
+  )";
+  EXPECT_EQ(Expected, test::runCheckOnCode<IfInverterCheck>(Input));
+}
+} // namespace
+} // namespace utils
+} // namespace tidy
+} // namespace clang




More information about the cfe-commits mailing list