[clang] Allow usage of applyFirst with rewriteDescendants (PR #117658)

via cfe-commits cfe-commits at lists.llvm.org
Sun Dec 1 10:45:22 PST 2024


https://github.com/SherAndrei updated https://github.com/llvm/llvm-project/pull/117658

>From 5eea9669c455e2b0dacbb4dca34be7dc100711cf Mon Sep 17 00:00:00 2001
From: SherAndrei <welldryus at gmail.com>
Date: Tue, 26 Nov 2024 03:30:46 +0300
Subject: [PATCH] [clang][transformer] Allow usage of applyFirst with
 rewriteDescendants

Without these changes a clash appears between a Tag, which is
bound to enclosing match, and a Tag, which is associated with first
Case of applyFirst in rewriteDescendands.

We fix this by making sure that associated Tags are unique and
deterministic as they are intend to be.
---
 clang/lib/Tooling/Transformer/RewriteRule.cpp |  6 ++-
 clang/unittests/Tooling/TransformerTest.cpp   | 43 +++++++++++++++++++
 2 files changed, 47 insertions(+), 2 deletions(-)

diff --git a/clang/lib/Tooling/Transformer/RewriteRule.cpp b/clang/lib/Tooling/Transformer/RewriteRule.cpp
index eefddc34940487..196249260ec8b4 100644
--- a/clang/lib/Tooling/Transformer/RewriteRule.cpp
+++ b/clang/lib/Tooling/Transformer/RewriteRule.cpp
@@ -382,9 +382,10 @@ static std::vector<DynTypedMatcher> taggedMatchers(
   std::vector<DynTypedMatcher> Matchers;
   Matchers.reserve(Cases.size());
   for (const auto &Case : Cases) {
-    std::string Tag = (TagBase + Twine(Case.first)).str();
     // HACK: Many matchers are not bindable, so ensure that tryBind will work.
     DynTypedMatcher BoundMatcher(Case.second.Matcher);
+    const auto [_, ID] = BoundMatcher.getID();
+    std::string Tag = (TagBase + Twine(ID)).str();
     BoundMatcher.setAllowBind(true);
     auto M = *BoundMatcher.tryBind(Tag);
     Matchers.push_back(!M.getTraversalKind()
@@ -469,7 +470,8 @@ size_t transformer::detail::findSelectedCase(const MatchResult &Result,
 
   auto &NodesMap = Result.Nodes.getMap();
   for (size_t i = 0, N = Rule.Cases.size(); i < N; ++i) {
-    std::string Tag = ("Tag" + Twine(i)).str();
+    const auto [_, ID] = Rule.Cases[i].Matcher.getID();
+    std::string Tag = ("Tag" + Twine(ID)).str();
     if (NodesMap.find(Tag) != NodesMap.end())
       return i;
   }
diff --git a/clang/unittests/Tooling/TransformerTest.cpp b/clang/unittests/Tooling/TransformerTest.cpp
index cbd84ab794a49a..0404c81862468f 100644
--- a/clang/unittests/Tooling/TransformerTest.cpp
+++ b/clang/unittests/Tooling/TransformerTest.cpp
@@ -605,6 +605,49 @@ TEST_F(TransformerTest, RewriteDescendantsReferToParentBinding) {
            Input, Expected);
 }
 
+TEST_F(TransformerTest, RewriteDescendantsApplyFirstOrderedRuleUnrelated) {
+  std::string Input = "int f(int x) { int y = x; return x; }";
+  std::string Expected = "int f(int x) { char y = 3; return 3; }";
+  auto IntToChar = makeRule(typeLoc(loc(qualType(isInteger(), builtinType()))),
+                            changeTo(cat("char")));
+  auto InlineX =
+      makeRule(declRefExpr(to(varDecl(hasName("x")))), changeTo(cat("3")));
+  testRule(
+      makeRule(functionDecl(hasName("f"), hasBody(stmt().bind("body"))),
+               rewriteDescendants("body", applyFirst({InlineX, IntToChar}))),
+      Input, Expected);
+}
+
+TEST_F(TransformerTest, RewriteDescendantsApplyFirstOrderedRuleRelated) {
+  std::string Input = "int f(int x) { int y = x; return x; }";
+  std::string Expected = "int f(int x) { int y = 3; return y; }";
+  auto ReturnY = makeRule(
+      traverse(TK_IgnoreUnlessSpelledInSource,
+               declRefExpr(to(varDecl(hasName("x"))), hasParent(returnStmt()))),
+      changeTo(cat("y")));
+  auto InlineX = makeRule(traverse(TK_IgnoreUnlessSpelledInSource,
+                                   declRefExpr(to(varDecl(hasName("x"))))),
+                          changeTo(cat("3")));
+  testRule(makeRule(functionDecl(hasName("f"), hasBody(stmt().bind("body"))),
+                    rewriteDescendants("body", applyFirst({ReturnY, InlineX}))),
+           Input, Expected);
+}
+
+TEST_F(TransformerTest, RewriteDescendantsApplyFirstOrderedRuleRelatedSwapped) {
+  std::string Input = "int f(int x) { int y = x; return x; }";
+  std::string Expected = "int f(int x) { int y = 3; return 3; }";
+  auto ReturnY = makeRule(
+      traverse(TK_IgnoreUnlessSpelledInSource,
+               declRefExpr(to(varDecl(hasName("x"))), hasParent(returnStmt()))),
+      changeTo(cat("y")));
+  auto InlineX = makeRule(traverse(TK_IgnoreUnlessSpelledInSource,
+                                   declRefExpr(to(varDecl(hasName("x"))))),
+                          changeTo(cat("3")));
+  testRule(makeRule(functionDecl(hasName("f"), hasBody(stmt().bind("body"))),
+                    rewriteDescendants("body", applyFirst({InlineX, ReturnY}))),
+           Input, Expected);
+}
+
 TEST_F(TransformerTest, RewriteDescendantsUnboundNode) {
   std::string Input =
       "int f(int x) { int y = x; { int z = x * x; } return x; }";



More information about the cfe-commits mailing list