Skip to content

Commit

Permalink
[libTooling] In Clang Transformer, change Metadata field to deferre…
Browse files Browse the repository at this point in the history
…d evalutaion

`Metadata` is being changed from an `llvm::Any` to a `MatchConsumer<llvm;:Any>`, so that it's evaluation can be be dependent on `MatchResult`s passed in.

Reviewed By: ymandel, gribozavr2

Differential Revision: https://reviews.llvm.org/D83820
  • Loading branch information
ymand committed Jul 20, 2020
1 parent 917f842 commit c0b8954
Show file tree
Hide file tree
Showing 3 changed files with 44 additions and 8 deletions.
32 changes: 28 additions & 4 deletions clang/include/clang/Tooling/Transformer/RewriteRule.h
Expand Up @@ -47,6 +47,8 @@ using EditGenerator = MatchConsumer<llvm::SmallVector<Edit, 1>>;

using TextGenerator = std::shared_ptr<MatchComputation<std::string>>;

using AnyGenerator = MatchConsumer<llvm::Any>;

// Description of a source-code edit, expressed in terms of an AST node.
// Includes: an ID for the (bound) node, a selector for source related to the
// node, a replacement and, optionally, an explanation for the edit.
Expand Down Expand Up @@ -87,7 +89,11 @@ struct ASTEdit {
RangeSelector TargetRange;
TextGenerator Replacement;
TextGenerator Note;
llvm::Any Metadata;
// Not all transformations will want or need to attach metadata and therefore
// should not be required to do so.
AnyGenerator Metadata = [](const ast_matchers::MatchFinder::MatchResult &) {
return llvm::Any();
};
};

/// Lifts a list of `ASTEdit`s into an `EditGenerator`.
Expand Down Expand Up @@ -261,9 +267,27 @@ inline ASTEdit insertAfter(RangeSelector S, TextGenerator Replacement) {
/// Removes the source selected by \p S.
ASTEdit remove(RangeSelector S);

inline ASTEdit withMetadata(ASTEdit edit, llvm::Any Metadata) {
edit.Metadata = std::move(Metadata);
return edit;
// FIXME: If `Metadata` returns an `llvm::Expected<T>` the `AnyGenerator` will
// construct an `llvm::Expected<llvm::Any>` where no error is present but the
// `llvm::Any` holds the error. This is unlikely but potentially surprising.
// Perhaps the `llvm::Expected` should be unwrapped, or perhaps this should be a
// compile-time error. No solution here is perfect.
//
// Note: This function template accepts any type callable with a MatchResult
// rather than a `std::function` because the return-type needs to be deduced. If
// it accepted a `std::function<R(MatchResult)>`, lambdas or other callable
// types would not be able to deduce `R`, and users would be forced to specify
// explicitly the type they intended to return by wrapping the lambda at the
// call-site.
template <typename Callable>
inline ASTEdit withMetadata(ASTEdit Edit, Callable Metadata) {
Edit.Metadata =
[Gen = std::move(Metadata)](
const ast_matchers::MatchFinder::MatchResult &R) -> llvm::Any {
return Gen(R);
};

return Edit;
}

/// The following three functions are a low-level part of the RewriteRule
Expand Down
5 changes: 4 additions & 1 deletion clang/lib/Tooling/Transformer/RewriteRule.cpp
Expand Up @@ -44,10 +44,13 @@ translateEdits(const MatchResult &Result, ArrayRef<ASTEdit> ASTEdits) {
auto Replacement = E.Replacement->eval(Result);
if (!Replacement)
return Replacement.takeError();
auto Metadata = E.Metadata(Result);
if (!Metadata)
return Metadata.takeError();
transformer::Edit T;
T.Range = *EditRange;
T.Replacement = std::move(*Replacement);
T.Metadata = E.Metadata;
T.Metadata = std::move(*Metadata);
Edits.push_back(std::move(T));
}
return Edits;
Expand Down
15 changes: 12 additions & 3 deletions clang/unittests/Tooling/TransformerTest.cpp
Expand Up @@ -440,6 +440,12 @@ TEST_F(TransformerTest, RemoveEdit) {
}

TEST_F(TransformerTest, WithMetadata) {
auto makeMetadata = [](const MatchFinder::MatchResult &R) -> llvm::Any {
int N =
R.Nodes.getNodeAs<IntegerLiteral>("int")->getValue().getLimitedValue();
return N;
};

std::string Input = R"cc(
int f() {
int x = 5;
Expand All @@ -448,8 +454,11 @@ TEST_F(TransformerTest, WithMetadata) {
)cc";

Transformer T(
makeRule(declStmt().bind("decl"),
withMetadata(remove(statement(std::string("decl"))), 17)),
makeRule(
declStmt(containsDeclaration(0, varDecl(hasInitializer(
integerLiteral().bind("int")))))
.bind("decl"),
withMetadata(remove(statement(std::string("decl"))), makeMetadata)),
consumer());
T.registerMatchers(&MatchFinder);
auto Factory = newFrontendActionFactory(&MatchFinder);
Expand All @@ -459,7 +468,7 @@ TEST_F(TransformerTest, WithMetadata) {
ASSERT_EQ(Changes.size(), 1u);
const llvm::Any &Metadata = Changes[0].getMetadata();
ASSERT_TRUE(llvm::any_isa<int>(Metadata));
EXPECT_THAT(llvm::any_cast<int>(Metadata), 17);
EXPECT_THAT(llvm::any_cast<int>(Metadata), 5);
}

TEST_F(TransformerTest, MultiChange) {
Expand Down

0 comments on commit c0b8954

Please sign in to comment.