From ea966c1bc05bca8353db0c28751b4f804a62bd42 Mon Sep 17 00:00:00 2001 From: Yitzhak Mandelbaum Date: Thu, 26 Sep 2019 00:53:56 +0000 Subject: [PATCH] [libTooling] Add `run` combinator to Stencils. Summary: This revision adds `run`, a StencilPart that runs a user-defined function that computes a result over `MatchFinder::MatchResult`. Reviewers: gribozavr Subscribers: cfe-commits Tags: #clang Differential Revision: https://reviews.llvm.org/D67969 llvm-svn: 372936 --- clang/include/clang/Tooling/Refactoring/Stencil.h | 5 +++++ clang/lib/Tooling/Refactoring/Stencil.cpp | 22 ++++++++++++++++++++++ clang/unittests/Tooling/StencilTest.cpp | 18 ++++++++++++++++++ 3 files changed, 45 insertions(+) diff --git a/clang/include/clang/Tooling/Refactoring/Stencil.h b/clang/include/clang/Tooling/Refactoring/Stencil.h index 43b0429..3bd66e5 100644 --- a/clang/include/clang/Tooling/Refactoring/Stencil.h +++ b/clang/include/clang/Tooling/Refactoring/Stencil.h @@ -23,6 +23,7 @@ #include "clang/AST/ASTContext.h" #include "clang/AST/ASTTypeTraits.h" #include "clang/ASTMatchers/ASTMatchFinder.h" +#include "clang/Tooling/Refactoring/MatchConsumer.h" #include "clang/Tooling/Refactoring/RangeSelector.h" #include "llvm/ADT/StringRef.h" #include "llvm/Support/Error.h" @@ -175,6 +176,10 @@ inline StencilPart ifBound(llvm::StringRef Id, llvm::StringRef TrueText, return ifBound(Id, text(TrueText), text(FalseText)); } +/// Wraps a MatchConsumer in a StencilPart, so that it can be used in a Stencil. +/// This supports user-defined extensions to the Stencil language. +StencilPart run(MatchConsumer C); + /// For debug use only; semantics are not guaranteed. /// /// \returns the string resulting from calling the node's print() method. diff --git a/clang/lib/Tooling/Refactoring/Stencil.cpp b/clang/lib/Tooling/Refactoring/Stencil.cpp index 0e5eacc..78239b9 100644 --- a/clang/lib/Tooling/Refactoring/Stencil.cpp +++ b/clang/lib/Tooling/Refactoring/Stencil.cpp @@ -26,6 +26,7 @@ using namespace tooling; using ast_matchers::MatchFinder; using llvm::errc; using llvm::Error; +using llvm::Expected; using llvm::StringError; // A down_cast function to safely down cast a StencilPartInterface to a subclass @@ -102,6 +103,12 @@ bool isEqualData(const IfBoundData &A, const IfBoundData &B) { return A.Id == B.Id && A.TruePart == B.TruePart && A.FalsePart == B.FalsePart; } +// Equality is not defined over MatchConsumers, which are opaque. +bool isEqualData(const MatchConsumer &A, + const MatchConsumer &B) { + return false; +} + // The `evalData()` overloads evaluate the given stencil data to a string, given // the match result, and append it to `Result`. We define an overload for each // type of stencil data. @@ -159,6 +166,15 @@ Error evalData(const IfBoundData &Data, const MatchFinder::MatchResult &Match, .eval(Match, Result); } +Error evalData(const MatchConsumer &Fn, + const MatchFinder::MatchResult &Match, std::string *Result) { + Expected Value = Fn(Match); + if (!Value) + return Value.takeError(); + *Result += *Value; + return Error::success(); +} + template class StencilPartImpl : public StencilPartInterface { T Data; @@ -233,3 +249,9 @@ StencilPart stencil::ifBound(StringRef Id, StencilPart TruePart, return StencilPart(std::make_shared>( Id, std::move(TruePart), std::move(FalsePart))); } + +StencilPart stencil::run(MatchConsumer Fn) { + return StencilPart( + std::make_shared>>( + std::move(Fn))); +} diff --git a/clang/unittests/Tooling/StencilTest.cpp b/clang/unittests/Tooling/StencilTest.cpp index cc0e8fa..2202693 100644 --- a/clang/unittests/Tooling/StencilTest.cpp +++ b/clang/unittests/Tooling/StencilTest.cpp @@ -29,6 +29,7 @@ using stencil::access; using stencil::cat; using stencil::dPrint; using stencil::ifBound; +using stencil::run; using stencil::text; // Create a valid translation-unit from a statement. @@ -283,6 +284,15 @@ TEST_F(StencilTest, AccessOpImplicitThis) { EXPECT_THAT_EXPECTED(Stencil.eval(StmtMatch->Result), HasValue("field")); } +TEST_F(StencilTest, RunOp) { + StringRef Id = "id"; + auto SimpleFn = [Id](const MatchResult &R) { + return std::string(R.Nodes.getNodeAs(Id) != nullptr ? "Bound" + : "Unbound"); + }; + testExpr(Id, "3;", cat(run(SimpleFn)), "Bound"); +} + TEST(StencilEqualityTest, Equality) { auto Lhs = cat("foo", dPrint("dprint_id")); auto Rhs = cat("foo", dPrint("dprint_id")); @@ -307,4 +317,12 @@ TEST(StencilEqualityTest, InEqualitySelection) { auto S2 = cat(node("node")); EXPECT_NE(S1, S2); } + +// `run` is opaque. +TEST(StencilEqualityTest, InEqualityRun) { + auto F = [](const MatchResult &R) { return "foo"; }; + auto S1 = cat(run(F)); + auto S2 = cat(run(F)); + EXPECT_NE(S1, S2); +} } // namespace -- 2.7.4