[utils] Re-enable -Wunsafe-buffer-usage, add tests

Accidentally disabled when setting up -Weverything, because it triggered
on Wasm tests.

- Fix the Wasm tests.
  - Note the unsafe buffers plugin isn't enabled for Wasm builds, so the
    `-third_party/` suppression in unsafe_buffers_paths.txt doesn't
    apply, but we still get the warnings via -Weverything.
  - DAWN_UNSAFE_TODO is used instead of DAWN_UNSAFE_BUFFERS because we
    want to switch these to spanified APIs soon.
- Re-enable the warning.
- Add nocompile tests for basic builtin stuff, and for Dawn types that
  use DAWN_UNSAFE_BUFFER_USAGE.

Fixes: 523128530
Bug: 523358231
Change-Id: Ice88e4a9ed63e952656829f96e71138a386cce88
Reviewed-on: https://dawn-review.googlesource.com/c/dawn/+/321976
Reviewed-by: dan sinclair <dsinclair@chromium.org>
Auto-Submit: Kai Ninomiya <kainino@chromium.org>
Commit-Queue: Kai Ninomiya <kainino@chromium.org>
diff --git a/src/emdawnwebgpu/tests/SpotTests.cpp b/src/emdawnwebgpu/tests/SpotTests.cpp
index d54a42b..a40f8e0 100644
--- a/src/emdawnwebgpu/tests/SpotTests.cpp
+++ b/src/emdawnwebgpu/tests/SpotTests.cpp
@@ -126,8 +126,9 @@
         wgpu::SupportedFeatures deviceFeatures;
         device.GetFeatures(&deviceFeatures);
         mGotCompatibilityMode = true;
-        for (uint32_t i = 0; i < deviceFeatures.featureCount; ++i) {
-            if (deviceFeatures.features[i] == wgpu::FeatureName::CoreFeaturesAndLimits) {
+        for (auto feature :
+             DAWN_UNSAFE_TODO(std::span(deviceFeatures.features, deviceFeatures.featureCount))) {
+            if (feature == wgpu::FeatureName::CoreFeaturesAndLimits) {
                 mGotCompatibilityMode = false;
             }
         }
@@ -218,7 +219,7 @@
 TEST_P(SpotTests, GetWGSLLanguageFeatures) {
     wgpu::SupportedWGSLLanguageFeatures f;
     mInstance.GetWGSLLanguageFeatures(&f);
-    auto features = std::span(f.features, f.featureCount);
+    auto features = DAWN_UNSAFE_TODO(std::span(f.features, f.featureCount));
     for (auto feature : features) {
         // GetWGSLLanguageFeatures should filter out any unknown features.
         EXPECT_NE(feature, wgpu::WGSLLanguageFeatureName{0});
@@ -241,7 +242,7 @@
 void TestGetFeatures(AdapterOrDevice o, bool shouldHaveCompressedTexture) {
     wgpu::SupportedFeatures f;
     o.GetFeatures(&f);
-    auto features = std::span(f.features, f.featureCount);
+    auto features = DAWN_UNSAFE_TODO(std::span(f.features, f.featureCount));
     for (auto feature : features) {
         // GetFeatures should filter out any unknown features.
         EXPECT_NE(feature, wgpu::FeatureName{0});
diff --git a/src/utils/BUILD.gn b/src/utils/BUILD.gn
index 3ab151f..18319dc 100644
--- a/src/utils/BUILD.gn
+++ b/src/utils/BUILD.gn
@@ -56,6 +56,14 @@
   if (dawn_weverything) {
     assert(is_clang)
 
+    if (!is_wasm) {
+      # One of the warnings in -Weverything is Clang's -Wunsafe-buffer-usage,
+      # but we don't want to run that warning on Dawn without the corresponding
+      # Chromium plugin, which implements support for unsafe_buffers_paths.txt
+      # and probably other important things.
+      assert(clang_use_unsafe_buffers_plugin)
+    }
+
     cflags = [ "-Weverything" ]
 
     # Suppressions
@@ -118,7 +126,6 @@
       "-Wno-reserved-identifier",
       "-Wno-unused-member-function",
       "-Wno-tautological-type-limit-compare",
-      "-Wno-unsafe-buffer-usage",
       "-Wno-nonportable-system-include-path",
     ]
 
@@ -423,7 +430,7 @@
 }
 
 ###############################################################################
-# Additional clang plugin for raw_ptr and unsafe-buffers
+# Additional clang plugins.
 ###############################################################################
 
 # Helper template to define clang plugins.
diff --git a/src/utils/span_nocompile.nc b/src/utils/span_nocompile.nc
index 6a30c7e..c7e7842 100644
--- a/src/utils/span_nocompile.nc
+++ b/src/utils/span_nocompile.nc
@@ -25,7 +25,11 @@
 // 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.
 
-// Test the type checks of dawn::Span
+// Test the type checks of dawn::Span.
+//
+// Note: All unsafe buffer warning tests are in warning_nocompile.nc.
+// Warnings don't trigger in this file because it contains errors. Because of this,
+// DAWN_UNSAFE_BUFFERS() annotations also don't need be to used in this file - they're ignored.
 
 #include <array>
 
@@ -56,26 +60,16 @@
 };
 
 void TestConstPointerToNonConstSpan() {
-    DAWN_UNSAFE_BUFFERS(Span<const int>(kSpanData.data(), kSpanData.size())); // Control case.
-    DAWN_UNSAFE_BUFFERS(Span<int>(kSpanData.data(), kSpanData.size())); // expected-error {{no matching constructor for initialization}}
+    Span<const int>(kSpanData.data(), kSpanData.size()); // Control case.
+    Span<int>(kSpanData.data(), kSpanData.size()); // expected-error {{no matching constructor for initialization}}
 
-    DAWN_UNSAFE_BUFFERS(Span<const int>(kSpanData.begin(), kSpanData.end())); // Control case.
-    DAWN_UNSAFE_BUFFERS(Span<int>(kSpanData.begin(), kSpanData.end())); // expected-error {{no matching constructor for initialization}}
+    Span<const int>(kSpanData.begin(), kSpanData.end()); // Control case.
+    Span<int>(kSpanData.begin(), kSpanData.end()); // expected-error {{no matching constructor for initialization}}
 
     Span<const int>{FakeRange()}; // Control case.
     Span<int>{FakeRange()}; // expected-error {{no matching constructor for initialization}}
 }
 
-void TestConstructorsThatRequireDawnUnsafeBuffers() {
-    DAWN_UNSAFE_BUFFERS(Span<const int>(kSpanData.data(), kSpanData.size())); // Control case.
-    // TODO(https://crbug.com/523128530): DAWN_UNSAFE_BUFFERS doesn't seem required in no-compile tests for some reason. Fix that and add an expected error here.
-    Span<const int>(kSpanData.data(), kSpanData.size());
-
-    DAWN_UNSAFE_BUFFERS(Span<const int>(kSpanData.begin(), kSpanData.end())); // Control case.
-    // TODO(https://crbug.com/523128530): DAWN_UNSAFE_BUFFERS doesn't seem required in no-compile tests for some reason. Fix that and add an expected error here.
-    Span<const int>(kSpanData.begin(), kSpanData.end());
-}
-
 void TestConstructorWithRangeRequirements() {
     Span<const int>{FakeRange()}; // Control case.
 
@@ -107,7 +101,7 @@
 void TestTypedIntegerArguments() {
     ityp::span<Index, const int> sp{FakeTypedRange()};
 
-    DAWN_UNSAFE_BUFFERS(ityp::span<Index, const int>(kSpanData.data(), kSpanData.size())); // expected-error {{no matching constructor for initialization}}
+    ityp::span<Index, const int>(kSpanData.data(), kSpanData.size()); // expected-error {{no matching constructor for initialization}}
     (void) sp.at(2); // expected-error {{no viable conversion from}}
     (void) sp[2]; // expected-error {{no viable overloaded operator[]}}
     (void) sp.first(2); // expected-error {{no viable conversion from}}
diff --git a/src/utils/span_tests.cc b/src/utils/span_tests.cc
index 9225f53..054c7d3 100644
--- a/src/utils/span_tests.cc
+++ b/src/utils/span_tests.cc
@@ -652,7 +652,7 @@
         std::array<int, 3> ints{};
 
         // SAFETY: This is viewing ints, just with typed indices.
-        ityp::span<Index, int> sp{ints.data(), Index(3u)};
+        ityp::span<Index, int> DAWN_UNSAFE_BUFFERS(sp{ints.data(), Index(3u)});
         auto bsp = SpanAsBytes(sp);
         static_assert(std::is_same_v<Span<const std::byte>, decltype(bsp)>);
         EXPECT_EQ(bsp.size(), sp.size_bytes());
@@ -663,7 +663,7 @@
         EXPECT_EQ(wbsp.data(), reinterpret_cast<std::byte*>(sp.data()));
 
         // SAFETY: This is viewing ints, just with typed indices.
-        ityp::span<Index, volatile int> vsp{ints.data(), Index(3u)};
+        ityp::span<Index, volatile int> DAWN_UNSAFE_BUFFERS(vsp{ints.data(), Index(3u)});
         auto vbsp = SpanAsBytes(vsp);
         static_assert(std::is_same_v<Span<const volatile std::byte>, decltype(vbsp)>);
         EXPECT_EQ(vbsp.size(), vsp.size_bytes());
diff --git a/src/utils/warning_nocompile.nc b/src/utils/warning_nocompile.nc
index 229cb87..03a45ca 100644
--- a/src/utils/warning_nocompile.nc
+++ b/src/utils/warning_nocompile.nc
@@ -25,17 +25,66 @@
 // 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.
 
+// Tests for warnings. Note this code should ONLY trigger warnings (no other hard errors),
+// otherwise the compiler suppresses the warnings in favor of the errors.
+
 #include <stdint.h>
 
+#include <array>
+#include <span>
+
+#include "src/utils/span.h"
+
 namespace dawn {
 
-// This test verifies that warnings that are part of -Weverything but not
-// -Wall -Wextra are enabled when This test runs only when `dawn_weverything = true`.
-
-// -Wcast-align is one such warning.
-void TestCastAlign() {
+// Verify that warnings that are part of -Weverything but not -Wall -Wextra
+// are enabled. This test runs only when `dawn_weverything = true`.
+void TestWeverything() {
+    // -Wcast-align is one such warning.
     char* p = nullptr;
     [[maybe_unused]] int* q = (int*)p; // expected-error {{increases required alignment}}
 }
 
+// -Wunsafe-buffer-usage: operator[] on T*
+void TestUnsafeBuffersRawPointer() {
+    constexpr std::array<int, 4> arr{};
+
+    // Safe to get a pointer to the first element.
+    const int* arrPtr = arr.data();
+
+    // But unsafe to use it like an array.
+    { [[maybe_unused]] int x = arr[1]; }
+    { [[maybe_unused]] int x = std::span(arr)[1]; }
+    { [[maybe_unused]] int x = arrPtr[1]; }  // expected-error {{unsafe buffer access}}
+}
+
+// -Wunsafe-buffer-usage-in-libc-call: memcpy()
+void TestUnsafeBuffersMemcpy() {
+    int x = 1;
+    int y = 2;
+    memcpy(&y, &x, sizeof(int));  // expected-error {{function 'memcpy' is unsafe}}
+}
+
+// -Wunsafe-buffer-usage: std::span() constructors
+void TestUnsafeBuffersStdSpanConstructors() {
+    std::array<int, 4> arr{};
+    // std::span is NOT tagged as being unsafe when using the unsafe buffers plugin.
+    // (If we use -Wunsafe-buffer-usage without the plugin, it would be. This may be fixable.)
+    { [[maybe_unused]] auto s = std::span(arr.data(), arr.size()); }
+    { [[maybe_unused]] std::span<int> s(arr.data(), arr.size()); }
+}
+
+// -Wunsafe-buffer-usage: dawn::Span() constructors
+void TestUnsafeBuffersDawnSpanConstructors() {
+    {
+        constexpr std::array<int, 5> kArr{};
+
+        DAWN_UNSAFE_BUFFERS(Span<const int>(kArr.data(), kArr.size()));  // Control case.
+        Span<const int>(kArr.data(), kArr.size());  // expected-error {{introduces unsafe buffer manipulation}}
+
+        DAWN_UNSAFE_BUFFERS(Span<const int>(kArr.begin(), kArr.end()));  // Control case.
+        Span<const int>(kArr.begin(), kArr.end());  // expected-error {{introduces unsafe buffer manipulation}}
+    }
+}
+
 }
diff --git a/unsafe_buffers_paths.txt b/unsafe_buffers_paths.txt
index b8c7f20..dd1107b 100644
--- a/unsafe_buffers_paths.txt
+++ b/unsafe_buffers_paths.txt
@@ -25,9 +25,16 @@
 # 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.
 
-# See https://chromium.googlesource.com/chromium/src/+/main/docs/unsafe_buffers.md
+# Config used by Chromium's unsafe buffers plugin to control Clang's builtin
+# -Wunsafe-buffer-usage* warnings.
+# Docs: http://crsrc.org/v/docs/unsafe_buffers.md
+#
+# Note the plugin is not used in Wasm builds, so this config is ignored.
 
-# Checks to enforce
+# Checks to enforce.
+# Docs: http://crsrc.org/v/docs/unsafe_buffers.md#syntax-of-unsafe-buffer-paths-file
+#
+# NOTE: If adding a check here, add a test to warning_nocompile.nc too.
 .buffers,libc
 
 # These directories are excluded because they come from outside Dawn and Tint and