Support cloning of nodes in the AST.

This will be useful for sharding targets.

Change-Id: Ibc4f4f1cf84bf91a2cde8ead520eb4c76a6a6964
Reviewed-on: https://gn-review.googlesource.com/c/gn/+/25600
Reviewed-by: Takuto Ikuta <tikuta@google.com>
Commit-Queue: Matt Stark <msta@google.com>
diff --git a/src/gn/operators_unittest.cc b/src/gn/operators_unittest.cc
index b812aa9..cdbbb7c 100644
--- a/src/gn/operators_unittest.cc
+++ b/src/gn/operators_unittest.cc
@@ -42,6 +42,11 @@
   }
   base::Value GetJSONNode() const override { return base::Value(); }
 
+ protected:
+  std::unique_ptr<ParseNode> CloneImpl() const override {
+    return std::make_unique<TestParseNode>(value_);
+  }
+
  private:
   Value value_;
 };
diff --git a/src/gn/parse_tree.cc b/src/gn/parse_tree.cc
index 03fec79..7328b21 100644
--- a/src/gn/parse_tree.cc
+++ b/src/gn/parse_tree.cc
@@ -184,6 +184,10 @@
 
 Comments::~Comments() = default;
 
+std::unique_ptr<Comments> Comments::Clone() const {
+  return std::unique_ptr<Comments>(new Comments(*this));
+}
+
 void Comments::ReverseSuffix() {
   for (int i = 0, j = static_cast<int>(suffix_.size() - 1); i < j; ++i, --j)
     std::swap(suffix_[i], suffix_[j]);
@@ -278,6 +282,14 @@
   }
 }
 
+std::unique_ptr<ParseNode> ParseNode::Clone() const {
+  std::unique_ptr<ParseNode> clone = CloneImpl();
+  if (comments_) {
+    clone->comments_ = comments_->Clone();
+  }
+  return clone;
+}
+
 // static
 std::unique_ptr<ParseNode> ParseNode::BuildFromJSON(const base::Value& value) {
   const std::string& str_type = value.FindKey(kJsonNodeType)->GetString();
@@ -354,6 +366,16 @@
   return dict;
 }
 
+std::unique_ptr<ParseNode> AccessorNode::CloneImpl() const {
+  auto node = std::make_unique<AccessorNode>();
+  node->set_base(base_);
+  if (subscript_)
+    node->set_subscript(subscript_->Clone());
+  if (member_)
+    node->set_member(member_->Clone());
+  return node;
+}
+
 #define DECLARE_CHILD_AS_LIST_OR_FAIL()                     \
   const base::Value* child = value.FindKey(kJsonNodeChild); \
   if (!child || !child->is_list()) {                        \
@@ -553,6 +575,16 @@
   return dict;
 }
 
+std::unique_ptr<ParseNode> BinaryOpNode::CloneImpl() const {
+  auto node = std::make_unique<BinaryOpNode>();
+  node->set_op(op_);
+  if (left_)
+    node->set_left(left_->Clone());
+  if (right_)
+    node->set_right(right_->Clone());
+  return node;
+}
+
 // static
 std::unique_ptr<BinaryOpNode> BinaryOpNode::NewFromJSON(
     const base::Value& value) {
@@ -660,6 +692,18 @@
   return dict;
 }
 
+std::unique_ptr<ParseNode> BlockNode::CloneImpl() const {
+  auto node = std::make_unique<BlockNode>(result_mode_);
+  node->set_begin_token(begin_token_);
+  if (end_)
+    node->set_end(end_->Clone());
+  for (const auto& statement : statements_) {
+    if (statement)
+      node->append_statement(statement->Clone());
+  }
+  return node;
+}
+
 // static
 std::unique_ptr<BlockNode> BlockNode::NewFromJSON(const base::Value& value) {
   const std::string& result_mode = value.FindKey(kDumpResultMode)->GetString();
@@ -748,6 +792,18 @@
   return dict;
 }
 
+std::unique_ptr<ParseNode> ConditionNode::CloneImpl() const {
+  auto node = std::make_unique<ConditionNode>();
+  node->set_if_token(if_token_);
+  if (condition_)
+    node->set_condition(condition_->Clone());
+  if (if_true_)
+    node->set_if_true(if_true_->Clone());
+  if (if_false_)
+    node->set_if_false(if_false_->Clone());
+  return node;
+}
+
 // static
 std::unique_ptr<ConditionNode> ConditionNode::NewFromJSON(
     const base::Value& value) {
@@ -805,6 +861,16 @@
   return dict;
 }
 
+std::unique_ptr<ParseNode> FunctionCallNode::CloneImpl() const {
+  auto node = std::make_unique<FunctionCallNode>();
+  node->set_function(function_);
+  if (args_)
+    node->set_args(args_->Clone());
+  if (block_)
+    node->set_block(block_->Clone());
+  return node;
+}
+
 // static
 std::unique_ptr<FunctionCallNode> FunctionCallNode::NewFromJSON(
     const base::Value& value) {
@@ -881,6 +947,10 @@
   return CreateJSONNode(kDumpNodeName, value_.value(), GetRange());
 }
 
+std::unique_ptr<ParseNode> IdentifierNode::CloneImpl() const {
+  return std::make_unique<IdentifierNode>(value_);
+}
+
 // static
 std::unique_ptr<IdentifierNode> IdentifierNode::NewFromJSON(
     const base::Value& value) {
@@ -947,6 +1017,18 @@
   return dict;
 }
 
+std::unique_ptr<ParseNode> ListNode::CloneImpl() const {
+  auto node = std::make_unique<ListNode>();
+  node->set_begin_token(begin_token_);
+  if (end_)
+    node->set_end(end_->Clone());
+  for (const auto& item : contents_) {
+    if (item)
+      node->append_item(item->Clone());
+  }
+  return node;
+}
+
 // static
 std::unique_ptr<ListNode> ListNode::NewFromJSON(const base::Value& value) {
   auto ret = std::make_unique<ListNode>();
@@ -1212,6 +1294,16 @@
   return CreateJSONNode(kDumpNodeName, value_.value(), GetRange());
 }
 
+std::unique_ptr<ParseNode> LiteralNode::CloneImpl() const {
+  auto node = std::make_unique<LiteralNode>(value_);
+  if (!shortened_value_.empty()) {
+    node->shortened_value_ = shortened_value_;
+    node->value_ =
+        Token(value_.location(), value_.type(), node->shortened_value_);
+  }
+  return node;
+}
+
 // static
 std::unique_ptr<LiteralNode> LiteralNode::NewFromJSON(
     const base::Value& value) {
@@ -1302,6 +1394,14 @@
   return dict;
 }
 
+std::unique_ptr<ParseNode> UnaryOpNode::CloneImpl() const {
+  auto node = std::make_unique<UnaryOpNode>();
+  node->set_op(op_);
+  if (operand_)
+    node->set_operand(operand_->Clone());
+  return node;
+}
+
 // static
 std::unique_ptr<UnaryOpNode> UnaryOpNode::NewFromJSON(
     const base::Value& value) {
@@ -1341,6 +1441,12 @@
   return CreateJSONNode(kDumpNodeName, comment_.value(), GetRange());
 }
 
+std::unique_ptr<ParseNode> BlockCommentNode::CloneImpl() const {
+  auto node = std::make_unique<BlockCommentNode>();
+  node->set_comment(comment_);
+  return node;
+}
+
 // static
 std::unique_ptr<BlockCommentNode> BlockCommentNode::NewFromJSON(
     const base::Value& value) {
@@ -1378,6 +1484,10 @@
   return CreateJSONNode(kDumpNodeName, value_.value(), GetRange());
 }
 
+std::unique_ptr<ParseNode> EndNode::CloneImpl() const {
+  return std::make_unique<EndNode>(value_);
+}
+
 // static
 std::unique_ptr<EndNode> EndNode::NewFromJSON(const base::Value& value) {
   auto ret = std::make_unique<EndNode>(TokenFromValue(value));
diff --git a/src/gn/parse_tree.h b/src/gn/parse_tree.h
index 47a7b43..4d23ebb 100644
--- a/src/gn/parse_tree.h
+++ b/src/gn/parse_tree.h
@@ -44,6 +44,8 @@
   Comments();
   virtual ~Comments();
 
+  std::unique_ptr<Comments> Clone() const;
+
   const std::vector<Token>& before() const { return before_; }
   void append_before(Token c) { before_.push_back(c); }
   void clear_before() { before_.clear(); }
@@ -59,6 +61,9 @@
   void append_after(Token c) { after_.push_back(c); }
 
  private:
+  Comments(const Comments&) = default;
+  Comments& operator=(const Comments&) = delete;
+
   // Whole line comments before the expression.
   std::vector<Token> before_;
 
@@ -68,9 +73,6 @@
   // For top-level expressions only, after_ lists whole-line comments
   // following the expression.
   std::vector<Token> after_;
-
-  Comments(const Comments&) = delete;
-  Comments& operator=(const Comments&) = delete;
 };
 
 // ParseNode -------------------------------------------------------------------
@@ -133,12 +135,16 @@
   // exporting the tree as a JSON or formatted text with indents.
   virtual base::Value GetJSONNode() const = 0;
 
+  // Clones the node. Useful for AST manipulation.
+  std::unique_ptr<ParseNode> Clone() const;
+
   const Comments* comments() const { return comments_.get(); }
   Comments* comments_mutable();
 
   static std::unique_ptr<ParseNode> BuildFromJSON(const base::Value& value);
 
  protected:
+  virtual std::unique_ptr<ParseNode> CloneImpl() const = 0;
   // Helper functions for GetJSONNode. Creates and fills a Value object with
   // given type (and value).
   base::Value CreateJSONNode(const char* type, LocationRange location) const;
@@ -224,6 +230,9 @@
 
   static constexpr const char* kDumpNodeName = "ACCESSOR";
 
+ protected:
+  std::unique_ptr<ParseNode> CloneImpl() const override;
+
  private:
   Value ExecuteSubscriptAccess(Scope* scope, Err* err) const;
   Value ExecuteArrayAccess(Scope* scope,
@@ -285,6 +294,9 @@
 
   static constexpr const char* kDumpNodeName = "BINARY";
 
+ protected:
+  std::unique_ptr<ParseNode> CloneImpl() const override;
+
  private:
   std::unique_ptr<ParseNode> left_;
   Token op_;
@@ -319,6 +331,10 @@
       const std::string& msg,
       const std::string& help = std::string()) const override;
   base::Value GetJSONNode() const override;
+  std::unique_ptr<BlockNode> Clone() const {
+    return std::unique_ptr<BlockNode>(
+        static_cast<BlockNode*>(ParseNode::Clone().release()));
+  }
   static std::unique_ptr<BlockNode> NewFromJSON(const base::Value& value);
 
   void set_begin_token(const Token& t) { begin_token_ = t; }
@@ -337,6 +353,9 @@
 
   static constexpr const char* kDumpNodeName = "BLOCK";
 
+ protected:
+  std::unique_ptr<ParseNode> CloneImpl() const override;
+
  private:
   static constexpr const char* kDumpResultMode = "result_mode";
   static constexpr const char* kDumpResultModeReturnsScope = "returns_scope";
@@ -392,6 +411,9 @@
 
   static constexpr const char* kDumpNodeName = "CONDITION";
 
+ protected:
+  std::unique_ptr<ParseNode> CloneImpl() const override;
+
  private:
   // Token corresponding to the "if" string.
   Token if_token_;
@@ -436,6 +458,9 @@
 
   static constexpr const char* kDumpNodeName = "FUNCTION";
 
+ protected:
+  std::unique_ptr<ParseNode> CloneImpl() const override;
+
  private:
   Token function_;
   std::unique_ptr<ListNode> args_;
@@ -460,6 +485,10 @@
       const std::string& msg,
       const std::string& help = std::string()) const override;
   base::Value GetJSONNode() const override;
+  std::unique_ptr<IdentifierNode> Clone() const {
+    return std::unique_ptr<IdentifierNode>(
+        static_cast<IdentifierNode*>(ParseNode::Clone().release()));
+  }
   static std::unique_ptr<IdentifierNode> NewFromJSON(const base::Value& value);
 
   const Token& value() const { return value_; }
@@ -469,6 +498,9 @@
 
   static constexpr const char* kDumpNodeName = "IDENTIFIER";
 
+ protected:
+  std::unique_ptr<ParseNode> CloneImpl() const override;
+
  private:
   Token value_;
 
@@ -490,6 +522,10 @@
       const std::string& msg,
       const std::string& help = std::string()) const override;
   base::Value GetJSONNode() const override;
+  std::unique_ptr<ListNode> Clone() const {
+    return std::unique_ptr<ListNode>(
+        static_cast<ListNode*>(ParseNode::Clone().release()));
+  }
   static std::unique_ptr<ListNode> NewFromJSON(const base::Value& value);
 
   void set_begin_token(const Token& t) { begin_token_ = t; }
@@ -520,6 +556,9 @@
 
   static constexpr const char* kDumpNodeName = "LIST";
 
+ protected:
+  std::unique_ptr<ParseNode> CloneImpl() const override;
+
  private:
   template <typename Comparator>
   void SortList(Comparator comparator);
@@ -560,6 +599,9 @@
 
   static constexpr const char* kDumpNodeName = "LITERAL";
 
+ protected:
+  std::unique_ptr<ParseNode> CloneImpl() const override;
+
  private:
   Token value_;
   std::string shortened_value_;
@@ -594,6 +636,9 @@
 
   static constexpr const char* kDumpNodeName = "UNARY";
 
+ protected:
+  std::unique_ptr<ParseNode> CloneImpl() const override;
+
  private:
   Token op_;
   std::unique_ptr<ParseNode> operand_;
@@ -629,6 +674,9 @@
 
   static constexpr const char* kDumpNodeName = "BLOCK_COMMENT";
 
+ protected:
+  std::unique_ptr<ParseNode> CloneImpl() const override;
+
  private:
   Token comment_;
 
@@ -654,6 +702,10 @@
       const std::string& msg,
       const std::string& help = std::string()) const override;
   base::Value GetJSONNode() const override;
+  std::unique_ptr<EndNode> Clone() const {
+    return std::unique_ptr<EndNode>(
+        static_cast<EndNode*>(ParseNode::Clone().release()));
+  }
   static std::unique_ptr<EndNode> NewFromJSON(const base::Value& value);
 
   const Token& value() const { return value_; }
@@ -661,6 +713,9 @@
 
   static constexpr const char* kDumpNodeName = "END";
 
+ protected:
+  std::unique_ptr<ParseNode> CloneImpl() const override;
+
  private:
   Token value_;
 
diff --git a/src/gn/parse_tree_unittest.cc b/src/gn/parse_tree_unittest.cc
index e5f41ff..7621bc5 100644
--- a/src/gn/parse_tree_unittest.cc
+++ b/src/gn/parse_tree_unittest.cc
@@ -366,3 +366,30 @@
     EXPECT_TRUE(err.has_error());
   }
 }
+
+TEST(ParseTree, Clone) {
+  TestParseInput input(
+      "# Top comment\n"
+      "a = [ \"foo\", \"bar\" ]\n"
+      "if (a != []) {\n"
+      "  b = a[0]\n"
+      "  c = !false\n"
+      "}\n"
+      "print(a)\n");
+  EXPECT_SUCCESS(input);
+
+  const ParseNode* original = input.parsed();
+  std::unique_ptr<ParseNode> cloned = original->Clone();
+  ASSERT_NE(nullptr, cloned);
+  EXPECT_EQ(original->GetJSONNode(), cloned->GetJSONNode());
+
+  TestWithScope setup_orig;
+  Err err_orig;
+  original->Execute(setup_orig.scope(), &err_orig);
+  EXPECT_SUCCESS(err_orig);
+
+  TestWithScope setup_clone;
+  Err err_clone;
+  cloned->Execute(setup_clone.scope(), &err_clone);
+  EXPECT_SUCCESS(err_clone);
+}