Skip to content

Commit

Permalink
[libTooling] Add run combinator to Stencils.
Browse files Browse the repository at this point in the history
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
  • Loading branch information
ymand committed Sep 26, 2019
1 parent 3c8c667 commit ea966c1
Show file tree
Hide file tree
Showing 3 changed files with 45 additions and 0 deletions.
5 changes: 5 additions & 0 deletions clang/include/clang/Tooling/Refactoring/Stencil.h
Expand Up @@ -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"
Expand Down Expand Up @@ -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<std::string> C);

/// For debug use only; semantics are not guaranteed.
///
/// \returns the string resulting from calling the node's print() method.
Expand Down
22 changes: 22 additions & 0 deletions clang/lib/Tooling/Refactoring/Stencil.cpp
Expand Up @@ -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
Expand Down Expand Up @@ -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<std::string> &A,
const MatchConsumer<std::string> &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.
Expand Down Expand Up @@ -159,6 +166,15 @@ Error evalData(const IfBoundData &Data, const MatchFinder::MatchResult &Match,
.eval(Match, Result);
}

Error evalData(const MatchConsumer<std::string> &Fn,
const MatchFinder::MatchResult &Match, std::string *Result) {
Expected<std::string> Value = Fn(Match);
if (!Value)
return Value.takeError();
*Result += *Value;
return Error::success();
}

template <typename T>
class StencilPartImpl : public StencilPartInterface {
T Data;
Expand Down Expand Up @@ -233,3 +249,9 @@ StencilPart stencil::ifBound(StringRef Id, StencilPart TruePart,
return StencilPart(std::make_shared<StencilPartImpl<IfBoundData>>(
Id, std::move(TruePart), std::move(FalsePart)));
}

StencilPart stencil::run(MatchConsumer<std::string> Fn) {
return StencilPart(
std::make_shared<StencilPartImpl<MatchConsumer<std::string>>>(
std::move(Fn)));
}
18 changes: 18 additions & 0 deletions clang/unittests/Tooling/StencilTest.cpp
Expand Up @@ -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.
Expand Down Expand Up @@ -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<Stmt>(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"));
Expand All @@ -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

0 comments on commit ea966c1

Please sign in to comment.