[tint][fuzzers] Port tint_ast_clone_fuzzer to tint_wgsl_fuzzer

Change-Id: Id32540a27513f252a8e75f4fb8b2a7b369d6c8ef
Reviewed-on: https://dawn-review.googlesource.com/c/dawn/+/154543
Reviewed-by: James Price <jrprice@google.com>
Commit-Queue: James Price <jrprice@google.com>
Kokoro: Kokoro <noreply+kokoro@google.com>
Auto-Submit: Ben Clayton <bclayton@google.com>
diff --git a/src/tint/cmd/fuzz/wgsl/BUILD.cmake b/src/tint/cmd/fuzz/wgsl/BUILD.cmake
index 9ba6530..022ae21 100644
--- a/src/tint/cmd/fuzz/wgsl/BUILD.cmake
+++ b/src/tint/cmd/fuzz/wgsl/BUILD.cmake
@@ -51,6 +51,7 @@
   tint_lang_wgsl
   tint_lang_wgsl_ast
   tint_lang_wgsl_program
+  tint_lang_wgsl_program_fuzz
   tint_lang_wgsl_sem
   tint_lang_wgsl_fuzz
   tint_utils_cli
diff --git a/src/tint/cmd/fuzz/wgsl/BUILD.gn b/src/tint/cmd/fuzz/wgsl/BUILD.gn
index 5c4ccc6..15e4a15 100644
--- a/src/tint/cmd/fuzz/wgsl/BUILD.gn
+++ b/src/tint/cmd/fuzz/wgsl/BUILD.gn
@@ -86,6 +86,7 @@
       "${tint_src_dir}/lang/wgsl:fuzz",
       "${tint_src_dir}/lang/wgsl/ast",
       "${tint_src_dir}/lang/wgsl/program",
+      "${tint_src_dir}/lang/wgsl/program:fuzz",
       "${tint_src_dir}/lang/wgsl/sem",
       "${tint_src_dir}/utils/cli",
       "${tint_src_dir}/utils/containers",
diff --git a/src/tint/fuzzers/BUILD.gn b/src/tint/fuzzers/BUILD.gn
index 7f5b0cd..e13d89c 100644
--- a/src/tint/fuzzers/BUILD.gn
+++ b/src/tint/fuzzers/BUILD.gn
@@ -134,20 +134,6 @@
   }
 
   if (tint_build_wgsl_reader && tint_build_wgsl_writer) {
-    fuzzer_test("tint_ast_clone_fuzzer") {
-      sources = [ "tint_ast_clone_fuzzer.cc" ]
-      deps = [ ":tint_fuzzer_common_with_init_src" ]
-      deps += [
-        "${tint_src_dir}/lang/wgsl/reader",
-        "${tint_src_dir}/lang/wgsl/reader/parser",
-      ]
-
-      dict = "dictionary.txt"
-      libfuzzer_options = tint_fuzzer_common_libfuzzer_options
-      seed_corpus = fuzzer_corpus_wgsl_dir
-      seed_corpus_deps = [ ":tint_generate_wgsl_corpus" ]
-    }
-
     if (build_with_chromium) {
       fuzzer_test("tint_ast_wgsl_writer_fuzzer") {
         sources = [ "tint_ast_fuzzer/tint_ast_wgsl_writer_fuzzer.cc" ]
@@ -344,7 +330,6 @@
 
     if (tint_build_wgsl_reader && tint_build_wgsl_writer) {
       deps += [
-        ":tint_ast_clone_fuzzer",
         ":tint_regex_wgsl_writer_fuzzer",
         ":tint_wgsl_reader_wgsl_writer_fuzzer",
       ]
diff --git a/src/tint/fuzzers/CMakeLists.txt b/src/tint/fuzzers/CMakeLists.txt
index cd5a057..d76b452 100644
--- a/src/tint/fuzzers/CMakeLists.txt
+++ b/src/tint/fuzzers/CMakeLists.txt
@@ -95,10 +95,6 @@
   add_tint_fuzzer(tint_spv_reader_msl_writer_fuzzer)
 endif()
 
-if (${TINT_BUILD_WGSL_READER} AND ${TINT_BUILD_WGSL_WRITER})
-  add_tint_fuzzer(tint_ast_clone_fuzzer)
-endif()
-
 if (${TINT_BUILD_AST_FUZZER})
   add_subdirectory(tint_ast_fuzzer)
 endif()
diff --git a/src/tint/fuzzers/tint_ast_clone_fuzzer.cc b/src/tint/fuzzers/tint_ast_clone_fuzzer.cc
deleted file mode 100644
index e33d3f9..0000000
--- a/src/tint/fuzzers/tint_ast_clone_fuzzer.cc
+++ /dev/null
@@ -1,126 +0,0 @@
-// Copyright 2020 The Dawn & Tint Authors
-//
-// Redistribution and use in source and binary forms, with or without
-// modification, are permitted provided that the following conditions are met:
-//
-// 1. Redistributions of source code must retain the above copyright notice, this
-//    list of conditions and the following disclaimer.
-//
-// 2. Redistributions in binary form must reproduce the above copyright notice,
-//    this list of conditions and the following disclaimer in the documentation
-//    and/or other materials provided with the distribution.
-//
-// 3. Neither the name of the copyright holder nor the names of its
-//    contributors may be used to endorse or promote products derived from
-//    this software without specific prior written permission.
-//
-// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
-// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
-// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
-// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
-// FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
-// DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
-// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
-// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
-// OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
-// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-
-#include <iostream>
-#include <string>
-#include <unordered_set>
-
-#include "src/tint/lang/wgsl/reader/parser/parser.h"
-#include "src/tint/lang/wgsl/writer/writer.h"
-
-#define ASSERT_EQ(A, B)                                        \
-    do {                                                       \
-        decltype(A) assert_a = (A);                            \
-        decltype(B) assert_b = (B);                            \
-        if (assert_a != assert_b) {                            \
-            std::cerr << "ASSERT_EQ(" #A ", " #B ") failed:\n" \
-                      << #A << " was: " << assert_a << "\n"    \
-                      << #B << " was: " << assert_b << "\n";   \
-            __builtin_trap();                                  \
-        }                                                      \
-    } while (false)
-
-#define ASSERT_TRUE(A)                                                                          \
-    do {                                                                                        \
-        decltype(A) assert_a = (A);                                                             \
-        if (!assert_a) {                                                                        \
-            std::cerr << "ASSERT_TRUE(" #A ") failed:\n" << #A << " was: " << assert_a << "\n"; \
-            __builtin_trap();                                                                   \
-        }                                                                                       \
-    } while (false)
-
-[[noreturn]] void TintInternalCompilerErrorReporter(const tint::InternalCompilerError& err) {
-    std::cerr << err.Error() << std::endl;
-    __builtin_trap();
-}
-
-extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) {
-    std::string str(reinterpret_cast<const char*>(data), size);
-
-    tint::SetInternalCompilerErrorReporter(&TintInternalCompilerErrorReporter);
-
-    tint::Source::File file("test.wgsl", str);
-
-    // Parse the wgsl, create the src program
-    tint::wgsl::reader::Parser parser(&file);
-    parser.set_max_errors(1);
-    if (!parser.Parse()) {
-        return 0;
-    }
-    auto src = parser.program();
-    if (!src.IsValid()) {
-        return 0;
-    }
-
-    // Clone the src program to dst
-    tint::Program dst(src.Clone());
-
-    // Expect the printed strings to match
-    ASSERT_EQ(tint::Program::printer(src), tint::Program::printer(dst));
-
-    // Check that none of the AST nodes or type pointers in dst are found in src
-    std::unordered_set<const tint::ast::Node*> src_nodes;
-    for (auto* src_node : src.ASTNodes().Objects()) {
-        src_nodes.emplace(src_node);
-    }
-    std::unordered_set<const tint::core::type::Type*> src_types;
-    for (auto* src_type : src.Types()) {
-        src_types.emplace(src_type);
-    }
-    for (auto* dst_node : dst.ASTNodes().Objects()) {
-        ASSERT_EQ(src_nodes.count(dst_node), 0u);
-    }
-    for (auto* dst_type : dst.Types()) {
-        ASSERT_EQ(src_types.count(dst_type), 0u);
-    }
-
-    // Regenerate the wgsl for the src program. We use this instead of the
-    // original source so that reformatting doesn't impact the final wgsl
-    // comparison.
-    std::string src_wgsl;
-    tint::wgsl::writer::Options wgsl_options;
-    {
-        auto result = tint::wgsl::writer::Generate(src, wgsl_options);
-        ASSERT_TRUE(result == true);
-        src_wgsl = result->wgsl;
-
-        // Move the src program to a temporary that'll be dropped, so that the src
-        // program is released before we attempt to print the dst program. This
-        // guarantee that all the source program nodes and types are destructed and
-        // freed. ASAN should error if there's any remaining references in dst when
-        // we try to reconstruct the WGSL.
-        auto tmp = std::move(src);
-    }
-
-    // Print the dst program, check it matches the original source
-    auto result = tint::wgsl::writer::Generate(dst, wgsl_options);
-    ASSERT_TRUE(result == true);
-    auto dst_wgsl = result->wgsl;
-    ASSERT_EQ(src_wgsl, dst_wgsl);
-
-    return 0;
-}
diff --git a/src/tint/lang/wgsl/ast/module_clone_test.cc b/src/tint/lang/wgsl/ast/module_clone_test.cc
index 6e64f65..3444805 100644
--- a/src/tint/lang/wgsl/ast/module_clone_test.cc
+++ b/src/tint/lang/wgsl/ast/module_clone_test.cc
@@ -38,7 +38,6 @@
 
 TEST(ModuleCloneTest, Clone) {
     // Shader that exercises the bulk of the AST nodes and types.
-    // See also fuzzers/tint_ast_clone_fuzzer.cc for further coverage of cloning.
     Source::File file("test.wgsl", R"(enable f16;
 diagnostic(off, chromium.unreachable_code);
 
diff --git a/src/tint/lang/wgsl/program/BUILD.bazel b/src/tint/lang/wgsl/program/BUILD.bazel
index 0cc90fa..1906aad 100644
--- a/src/tint/lang/wgsl/program/BUILD.bazel
+++ b/src/tint/lang/wgsl/program/BUILD.bazel
@@ -111,3 +111,21 @@
   visibility = ["//visibility:public"],
 )
 
+alias(
+  name = "tint_build_wgsl_reader",
+  actual = "//src/tint:tint_build_wgsl_reader_true",
+)
+
+alias(
+  name = "tint_build_wgsl_writer",
+  actual = "//src/tint:tint_build_wgsl_writer_true",
+)
+
+selects.config_setting_group(
+    name = "tint_build_wgsl_reader_and_tint_build_wgsl_writer",
+    match_all = [
+        ":tint_build_wgsl_reader",
+        ":tint_build_wgsl_writer",
+    ],
+)
+
diff --git a/src/tint/lang/wgsl/program/BUILD.cmake b/src/tint/lang/wgsl/program/BUILD.cmake
index 07c09e0..eea928d 100644
--- a/src/tint/lang/wgsl/program/BUILD.cmake
+++ b/src/tint/lang/wgsl/program/BUILD.cmake
@@ -109,3 +109,54 @@
 tint_target_add_external_dependencies(tint_lang_wgsl_program_test test
   "gtest"
 )
+
+################################################################################
+# Target:    tint_lang_wgsl_program_fuzz
+# Kind:      fuzz
+################################################################################
+tint_add_target(tint_lang_wgsl_program_fuzz fuzz
+)
+
+tint_target_add_dependencies(tint_lang_wgsl_program_fuzz fuzz
+  tint_api_common
+  tint_lang_core
+  tint_lang_core_constant
+  tint_lang_core_type
+  tint_lang_wgsl
+  tint_lang_wgsl_ast
+  tint_lang_wgsl_program
+  tint_lang_wgsl_resolver
+  tint_lang_wgsl_sem
+  tint_utils_containers
+  tint_utils_diagnostic
+  tint_utils_ice
+  tint_utils_id
+  tint_utils_macros
+  tint_utils_math
+  tint_utils_memory
+  tint_utils_reflection
+  tint_utils_result
+  tint_utils_rtti
+  tint_utils_symbol
+  tint_utils_text
+  tint_utils_traits
+)
+
+if(TINT_BUILD_WGSL_READER)
+  tint_target_add_dependencies(tint_lang_wgsl_program_fuzz fuzz
+    tint_cmd_fuzz_wgsl_fuzz
+    tint_lang_wgsl_reader_parser
+  )
+endif(TINT_BUILD_WGSL_READER)
+
+if(TINT_BUILD_WGSL_READER AND TINT_BUILD_WGSL_WRITER)
+  tint_target_add_sources(tint_lang_wgsl_program_fuzz fuzz
+    "lang/wgsl/program/clone_context_fuzz.cc"
+  )
+endif(TINT_BUILD_WGSL_READER AND TINT_BUILD_WGSL_WRITER)
+
+if(TINT_BUILD_WGSL_WRITER)
+  tint_target_add_dependencies(tint_lang_wgsl_program_fuzz fuzz
+    tint_lang_wgsl_writer
+  )
+endif(TINT_BUILD_WGSL_WRITER)
diff --git a/src/tint/lang/wgsl/program/BUILD.gn b/src/tint/lang/wgsl/program/BUILD.gn
index 6cf7d8b..5641e61 100644
--- a/src/tint/lang/wgsl/program/BUILD.gn
+++ b/src/tint/lang/wgsl/program/BUILD.gn
@@ -109,3 +109,46 @@
     ]
   }
 }
+
+tint_fuzz_source_set("fuzz") {
+  sources = []
+  deps = [
+    "${tint_src_dir}/api/common",
+    "${tint_src_dir}/lang/core",
+    "${tint_src_dir}/lang/core/constant",
+    "${tint_src_dir}/lang/core/type",
+    "${tint_src_dir}/lang/wgsl",
+    "${tint_src_dir}/lang/wgsl/ast",
+    "${tint_src_dir}/lang/wgsl/program",
+    "${tint_src_dir}/lang/wgsl/resolver",
+    "${tint_src_dir}/lang/wgsl/sem",
+    "${tint_src_dir}/utils/containers",
+    "${tint_src_dir}/utils/diagnostic",
+    "${tint_src_dir}/utils/ice",
+    "${tint_src_dir}/utils/id",
+    "${tint_src_dir}/utils/macros",
+    "${tint_src_dir}/utils/math",
+    "${tint_src_dir}/utils/memory",
+    "${tint_src_dir}/utils/reflection",
+    "${tint_src_dir}/utils/result",
+    "${tint_src_dir}/utils/rtti",
+    "${tint_src_dir}/utils/symbol",
+    "${tint_src_dir}/utils/text",
+    "${tint_src_dir}/utils/traits",
+  ]
+
+  if (tint_build_wgsl_reader) {
+    deps += [
+      "${tint_src_dir}/cmd/fuzz/wgsl:fuzz",
+      "${tint_src_dir}/lang/wgsl/reader/parser",
+    ]
+  }
+
+  if (tint_build_wgsl_reader && tint_build_wgsl_writer) {
+    sources += [ "clone_context_fuzz.cc" ]
+  }
+
+  if (tint_build_wgsl_writer) {
+    deps += [ "${tint_src_dir}/lang/wgsl/writer" ]
+  }
+}
diff --git a/src/tint/lang/wgsl/program/clone_context_fuzz.cc b/src/tint/lang/wgsl/program/clone_context_fuzz.cc
new file mode 100644
index 0000000..e4cfc2f
--- /dev/null
+++ b/src/tint/lang/wgsl/program/clone_context_fuzz.cc
@@ -0,0 +1,94 @@
+// Copyright 2020 The Dawn & Tint Authors
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are met:
+//
+// 1. Redistributions of source code must retain the above copyright notice, this
+//    list of conditions and the following disclaimer.
+//
+// 2. Redistributions in binary form must reproduce the above copyright notice,
+//    this list of conditions and the following disclaimer in the documentation
+//    and/or other materials provided with the distribution.
+//
+// 3. Neither the name of the copyright holder nor the names of its
+//    contributors may be used to endorse or promote products derived from
+//    this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
+// FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+// DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+// OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+// GEN_BUILD:CONDITION(tint_build_wgsl_reader && tint_build_wgsl_writer)
+
+#include <string>
+#include <unordered_set>
+
+#include "src/tint/cmd/fuzz/wgsl/wgsl_fuzz.h"
+#include "src/tint/lang/wgsl/reader/parser/parser.h"
+#include "src/tint/lang/wgsl/writer/writer.h"
+
+namespace tint::program {
+
+#define ASSERT_EQ(A, B)                                         \
+    do {                                                        \
+        decltype(A) assert_a = (A);                             \
+        decltype(B) assert_b = (B);                             \
+        if (assert_a != assert_b) {                             \
+            TINT_ICE() << "ASSERT_EQ(" #A ", " #B ") failed:\n" \
+                       << #A << " was: " << assert_a << "\n"    \
+                       << #B << " was: " << assert_b << "\n";   \
+        }                                                       \
+    } while (false)
+
+#define ASSERT_TRUE(A)                                                                           \
+    do {                                                                                         \
+        decltype(A) assert_a = (A);                                                              \
+        if (!assert_a) {                                                                         \
+            TINT_ICE() << "ASSERT_TRUE(" #A ") failed:\n" << #A << " was: " << assert_a << "\n"; \
+        }                                                                                        \
+    } while (false)
+
+void CloneContextFuzzer(const tint::Program& src) {
+    // Clone the src program to dst
+    tint::Program dst(src.Clone());
+
+    // Expect the printed strings to match
+    ASSERT_EQ(tint::Program::printer(src), tint::Program::printer(dst));
+
+    // Check that none of the AST nodes or type pointers in dst are found in src
+    std::unordered_set<const tint::ast::Node*> src_nodes;
+    for (auto* src_node : src.ASTNodes().Objects()) {
+        src_nodes.emplace(src_node);
+    }
+    std::unordered_set<const tint::core::type::Type*> src_types;
+    for (auto* src_type : src.Types()) {
+        src_types.emplace(src_type);
+    }
+    for (auto* dst_node : dst.ASTNodes().Objects()) {
+        ASSERT_EQ(src_nodes.count(dst_node), 0u);
+    }
+    for (auto* dst_type : dst.Types()) {
+        ASSERT_EQ(src_types.count(dst_type), 0u);
+    }
+
+    tint::wgsl::writer::Options wgsl_options;
+
+    auto src_wgsl = tint::wgsl::writer::Generate(src, wgsl_options);
+    ASSERT_TRUE(src_wgsl);
+
+    auto dst_wgsl = tint::wgsl::writer::Generate(dst, wgsl_options);
+    ASSERT_TRUE(dst_wgsl);
+
+    ASSERT_EQ(src_wgsl->wgsl, dst_wgsl->wgsl);
+}
+
+}  // namespace tint::program
+
+TINT_WGSL_PROGRAM_FUZZER(tint::program::CloneContextFuzzer);