pw_compilation_testing: Add negative compilation tests

- Convert existing negative compilation tests to the new framework.
- Add a negative compilation test for pw_rpc service creation.

Change-Id: I204f4602dd8f49cb7c59fda04fbb1321828640ae
Reviewed-on: https://pigweed-review.googlesource.com/c/pigweed/pigweed/+/103364
Commit-Queue: Wyatt Hepler <hepler@google.com>
Reviewed-by: Keir Mierle <keir@google.com>
Pigweed-Auto-Submit: Wyatt Hepler <hepler@google.com>
diff --git a/pw_assert/BUILD.bazel b/pw_assert/BUILD.bazel
index 27ddadb..0ac7975 100644
--- a/pw_assert/BUILD.bazel
+++ b/pw_assert/BUILD.bazel
@@ -101,6 +101,7 @@
     deps = [
         ":facade",
         "//pw_assert",
+        "//pw_compilation_testing:negative_compilation_testing",
         "//pw_preprocessor",
         "//pw_span",
         "//pw_string",
diff --git a/pw_assert/BUILD.gn b/pw_assert/BUILD.gn
index 7aa80a1..f32e3c4 100644
--- a/pw_assert/BUILD.gn
+++ b/pw_assert/BUILD.gn
@@ -209,6 +209,7 @@
     dir_pw_status,
     dir_pw_string,
   ]
+  negative_compilation_tests = true
 
   # TODO(frolv): Fix this test on the QEMU target.
   enable_if = pw_build_EXECUTABLE_TARGET_TYPE != "lm3s6965evb_executable"
diff --git a/pw_assert/CMakeLists.txt b/pw_assert/CMakeLists.txt
index c04545f..610885a 100644
--- a/pw_assert/CMakeLists.txt
+++ b/pw_assert/CMakeLists.txt
@@ -122,6 +122,7 @@
     pw_assert_test/fake_backend.h
   DEPS
     pw_assert
+    pw_compilation_testing._pigweed_only_negative_compilation
     pw_status
     pw_string
   GROUPS
diff --git a/pw_assert/assert_facade_test.cc b/pw_assert/assert_facade_test.cc
index fb42c57..0c6c473 100644
--- a/pw_assert/assert_facade_test.cc
+++ b/pw_assert/assert_facade_test.cc
@@ -26,6 +26,7 @@
 // clang-format on
 
 #include "gtest/gtest.h"
+#include "pw_compilation_testing/negative_compilation.h"
 #include "pw_status/status.h"
 
 namespace {
@@ -282,6 +283,14 @@
   PW_CHECK_NOTNULL(function);
 }
 
+[[maybe_unused]] void CompareIntWithString() {
+#if PW_NC_TEST(CompareIntWithString)
+  PW_NC_EXPECT("cannot initialize|invalid conversion");
+
+  PW_CHECK_INT_EQ(123l, "This check message is accidentally compared to 123!");
+#endif  // PW_NC_TEST
+}
+
 // Note: Due to platform inconsistencies, the below test for the NOTNULL
 // message doesn't work. Some platforms print NULL formatted as %p as "(nil)",
 // others "0x0". Leaving this here for reference.
diff --git a/pw_compilation_testing/docs.rst b/pw_compilation_testing/docs.rst
index 5018bbb..00ce0c1 100644
--- a/pw_compilation_testing/docs.rst
+++ b/pw_compilation_testing/docs.rst
@@ -14,7 +14,9 @@
 - For a ``constexpr`` function, testing that a ``PW_ASSERT`` is triggered as
   expected.
 
-Negative compilation tests are only supported in GN currently.
+Negative compilation tests are only supported in GN currently. Negative
+compilation tests are not currently supported in GN on Windows due to
+`b/241565082 <bugs.pigweed.dev/241565082>`_.
 
 .. warning::
 
diff --git a/pw_containers/BUILD.bazel b/pw_containers/BUILD.bazel
index b285b7b..bd6c77a 100644
--- a/pw_containers/BUILD.bazel
+++ b/pw_containers/BUILD.bazel
@@ -51,7 +51,10 @@
         "public/pw_containers/intrusive_list.h",
     ],
     includes = ["public"],
-    deps = ["//pw_assert"],
+    deps = [
+        "//pw_assert",
+        "//pw_compilation_testing:negative_compilation_testing",
+    ],
 )
 
 pw_cc_library(
diff --git a/pw_containers/BUILD.gn b/pw_containers/BUILD.gn
index 75fa867..8cf96b5 100644
--- a/pw_containers/BUILD.gn
+++ b/pw_containers/BUILD.gn
@@ -138,6 +138,7 @@
     ":intrusive_list",
     "$dir_pw_preprocessor",
   ]
+  negative_compilation_tests = true
 }
 
 pw_doc_group("docs") {
diff --git a/pw_containers/CMakeLists.txt b/pw_containers/CMakeLists.txt
index 67f3121..4efda1a 100644
--- a/pw_containers/CMakeLists.txt
+++ b/pw_containers/CMakeLists.txt
@@ -162,6 +162,7 @@
   SOURCES
     intrusive_list_test.cc
   DEPS
+    pw_compilation_testing._pigweed_only_negative_compilation
     pw_containers.intrusive_list
     pw_preprocessor
   GROUPS
diff --git a/pw_containers/intrusive_list_test.cc b/pw_containers/intrusive_list_test.cc
index 7e39cf4..80e6d53 100644
--- a/pw_containers/intrusive_list_test.cc
+++ b/pw_containers/intrusive_list_test.cc
@@ -19,6 +19,7 @@
 #include <cstdint>
 
 #include "gtest/gtest.h"
+#include "pw_compilation_testing/negative_compilation.h"
 #include "pw_preprocessor/util.h"
 
 namespace pw {
@@ -414,7 +415,8 @@
   EXPECT_EQ(list.end(), list.cend());
 }
 
-#if defined(PW_COMPILE_FAIL_TEST_incompatible_iterator_types)
+#if PW_NC_TEST(IncompatibleIteratorTypes)
+PW_NC_EXPECT("comparison (of|between) distinct pointer types");
 
 struct OtherItem : public IntrusiveList<OtherItem>::Item {};
 
@@ -424,11 +426,11 @@
   static_cast<void>(list.end() == list2.end());
 }
 
-#endif
+#endif  // PW_NC_TEST
 
-// TODO(b/234882063): These tests should fail to compile, enable when no-compile
-// tests are set up in Pigweed.
-#if defined(PW_COMPILE_FAIL_TEST_cannot_modify_through_const_iterator)
+#if PW_NC_TEST(CannotModifyThroughConstIterator)
+PW_NC_EXPECT("function is not marked const|discards qualifiers");
+
 TEST(IntrusiveList, ConstIteratorModify) {
   TestItem item1(1);
   TestItem item2(99);
@@ -445,7 +447,7 @@
     it++;
   }
 }
-#endif  // Compile failure test
+#endif  // PW_NC_TEST
 
 // TODO(b/235289499): These tests should trigger a CHECK failure. This requires
 // using a testing version of pw_assert.
@@ -675,14 +677,17 @@
 
   EXPECT_EQ(1u, derived_from_compatible_item_type.size());
 
-// TODO(b/234882063): Make these proper automated compilation failure tests.
-#if defined(PW_COMPILE_FAIL_TEST_cannot_add_base_class_to_derived_class_list)
+#if PW_NC_TEST(CannotAddBaseClassToDerivedClassList)
+  PW_NC_EXPECT_CLANG("cannot bind to a value of unrelated type");
+  PW_NC_EXPECT_GCC("cannot convert");
+
   TestItem item2;
   derived_from_compatible_item_type.push_front(item2);
 #endif
 }
 
-#if defined(PW_COMPILE_FAIL_TEST_incompatibile_item_type)
+#if PW_NC_TEST(IncompatibileItemType)
+PW_NC_EXPECT("IntrusiveList items must be derived from IntrusiveList<T>::Item");
 
 struct Foo {};
 
@@ -690,13 +695,14 @@
 
 [[maybe_unused]] IntrusiveList<BadItem> derived_from_incompatible_item_type;
 
-#elif defined(PW_COMPILE_FAIL_TEST_does_not_inherit_from_item)
+#elif PW_NC_TEST(DoesNotInheritFromItem)
+PW_NC_EXPECT("IntrusiveList items must be derived from IntrusiveList<T>::Item");
 
 struct NotAnItem {};
 
 [[maybe_unused]] IntrusiveList<NotAnItem> list;
 
-#endif
+#endif  // PW_NC_TEST
 
 }  // namespace
 }  // namespace pw
diff --git a/pw_function/BUILD.gn b/pw_function/BUILD.gn
index 17befd1..2302fa2 100644
--- a/pw_function/BUILD.gn
+++ b/pw_function/BUILD.gn
@@ -68,6 +68,7 @@
     dir_pw_polyfill,
   ]
   sources = [ "function_test.cc" ]
+  negative_compilation_tests = true
 }
 
 pw_size_report("function_size") {
diff --git a/pw_function/CMakeLists.txt b/pw_function/CMakeLists.txt
index 0dc579e..c0b891b 100644
--- a/pw_function/CMakeLists.txt
+++ b/pw_function/CMakeLists.txt
@@ -44,6 +44,7 @@
   SOURCES
     function_test.cc
   DEPS
+    pw_compilation_testing._pigweed_only_negative_compilation
     pw_function
     pw_polyfill
   GROUPS
diff --git a/pw_function/function_test.cc b/pw_function/function_test.cc
index c6fd122..1c1036d 100644
--- a/pw_function/function_test.cc
+++ b/pw_function/function_test.cc
@@ -15,31 +15,35 @@
 #include "pw_function/function.h"
 
 #include "gtest/gtest.h"
+#include "pw_compilation_testing/negative_compilation.h"
 #include "pw_polyfill/language_feature_macros.h"
 
 namespace pw {
 namespace {
 
-// TODO(b/234882063): Convert this to a compilation failure test.
-#if defined(PW_COMPILE_FAIL_TEST_CannotInstantiateWithNonFunction)
+#if PW_NC_TEST(CannotInstantiateWithNonFunction)
+PW_NC_EXPECT("pw::Function may only be instantiated for a function type");
 
 [[maybe_unused]] Function<int> function_pointer;
 
-#elif defined(PW_COMPILE_FAIL_TEST_CannotInstantiateWithFunctionPointer1)
+#elif PW_NC_TEST(CannotInstantiateWithFunctionPointer1)
+PW_NC_EXPECT("pw::Function may only be instantiated for a function type");
 
 [[maybe_unused]] Function<void (*)()> function_pointer;
 
-#elif defined(PW_COMPILE_FAIL_TEST_CannotInstantiateWithFunctionPointer2)
+#elif PW_NC_TEST(CannotInstantiateWithFunctionPointer2)
+PW_NC_EXPECT("pw::Function may only be instantiated for a function type");
 
 [[maybe_unused]] void SomeFunction(int);
 
 [[maybe_unused]] Function<decltype(&SomeFunction)> function_pointer;
 
-#elif defined(PW_COMPILE_FAIL_TEST_CannotInstantiateWithFunctionReference)
+#elif PW_NC_TEST(CannotInstantiateWithFunctionReference)
+PW_NC_EXPECT("pw::Function may only be instantiated for a function type");
 
 [[maybe_unused]] Function<void (&)()> function_pointer;
 
-#endif  // compile fail tests
+#endif  // PW_NC_TEST
 
 // Ensure that Function can be constant initialized.
 [[maybe_unused]] PW_CONSTINIT Function<void()> can_be_constant_initialized;
diff --git a/pw_function/public/pw_function/function.h b/pw_function/public/pw_function/function.h
index c1f7957..c0bc2c5 100644
--- a/pw_function/public/pw_function/function.h
+++ b/pw_function/public/pw_function/function.h
@@ -46,7 +46,7 @@
 template <typename Callable>
 class Function {
   static_assert(std::is_function_v<Callable>,
-                "pw::Function may only be instantianted for a function type, "
+                "pw::Function may only be instantiated for a function type, "
                 "such as pw::Function<void(int)>.");
 };
 
diff --git a/pw_rpc/BUILD.gn b/pw_rpc/BUILD.gn
index 69bd5d2..8850b77 100644
--- a/pw_rpc/BUILD.gn
+++ b/pw_rpc/BUILD.gn
@@ -19,6 +19,7 @@
 import("$dir_pw_build/python_action.gni")
 import("$dir_pw_build/target_types.gni")
 import("$dir_pw_chrono/backend.gni")
+import("$dir_pw_compilation_testing/negative_compilation_test.gni")
 import("$dir_pw_docgen/docs.gni")
 import("$dir_pw_protobuf_compiler/proto.gni")
 import("$dir_pw_sync/backend.gni")
diff --git a/pw_rpc/raw/BUILD.bazel b/pw_rpc/raw/BUILD.bazel
index 17f560f..ac9c10e 100644
--- a/pw_rpc/raw/BUILD.bazel
+++ b/pw_rpc/raw/BUILD.bazel
@@ -179,6 +179,14 @@
     ],
 )
 
+# Negative compilation testing is not supported by Bazel. Build this as a
+# regular unit for now test.
+pw_cc_test(
+    name = "service_nc_test",
+    srcs = ["service_nc_test.cc"],
+    deps = ["//pw_rpc:pw_rpc_test_cc.raw_rpc"],
+)
+
 pw_cc_test(
     name = "stub_generation_test",
     srcs = ["stub_generation_test.cc"],
diff --git a/pw_rpc/raw/BUILD.gn b/pw_rpc/raw/BUILD.gn
index 33bbe14..48b35bf 100644
--- a/pw_rpc/raw/BUILD.gn
+++ b/pw_rpc/raw/BUILD.gn
@@ -15,6 +15,7 @@
 import("//build_overrides/pigweed.gni")
 
 import("$dir_pw_build/target_types.gni")
+import("$dir_pw_compilation_testing/negative_compilation_test.gni")
 import("$dir_pw_docgen/docs.gni")
 import("$dir_pw_unit_test/test.gni")
 
@@ -119,6 +120,7 @@
 pw_test("method_test") {
   deps = [
     ":server_api",
+    ":service_nc_test",  # Pull in the service NC test through this test
     "$dir_pw_containers",
     "..:test_protos.pwpb",
     "..:test_protos.raw_rpc",
@@ -160,3 +162,8 @@
   deps = [ "..:test_protos.raw_rpc" ]
   sources = [ "stub_generation_test.cc" ]
 }
+
+pw_cc_negative_compilation_test("service_nc_test") {
+  sources = [ "service_nc_test.cc" ]
+  deps = [ "..:test_protos.raw_rpc" ]
+}
diff --git a/pw_rpc/raw/CMakeLists.txt b/pw_rpc/raw/CMakeLists.txt
index 8a9c7ad..00dcec2 100644
--- a/pw_rpc/raw/CMakeLists.txt
+++ b/pw_rpc/raw/CMakeLists.txt
@@ -21,6 +21,7 @@
     pw_rpc.common
     pw_rpc.server
   TEST_DEPS
+    pw_compilation_testing._pigweed_only_negative_compilation
     pw_rpc.test_protos.pwpb
     pw_rpc.test_protos.raw_rpc
     pw_rpc.test_utils
diff --git a/pw_rpc/raw/service_nc_test.cc b/pw_rpc/raw/service_nc_test.cc
new file mode 100644
index 0000000..695f807
--- /dev/null
+++ b/pw_rpc/raw/service_nc_test.cc
@@ -0,0 +1,38 @@
+// Copyright 2021 The Pigweed Authors
+//
+// Licensed under the Apache License, Version 2.0 (the "License"); you may not
+// use this file except in compliance with the License. You may obtain a copy of
+// the License at
+//
+//     https://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+// License for the specific language governing permissions and limitations under
+// the License.
+
+#include "pw_compilation_testing/negative_compilation.h"
+#include "pw_rpc_test_protos/test.raw_rpc.pb.h"
+
+namespace pw::rpc {
+namespace test {
+
+#if PW_NC_TEST(NoMethods)
+PW_NC_EXPECT("TestUnaryRpc");
+
+class TestService final
+    : public pw_rpc::raw::TestService::Service<TestService> {
+ public:
+};
+
+#else
+
+class TestService {};
+
+#endif  // PW_NC_TEST
+
+TestService test_service;
+
+}  // namespace test
+}  // namespace pw::rpc
diff --git a/pw_unit_test/static_library_support_test.cc b/pw_unit_test/static_library_support_test.cc
index cf13407..166e77c 100644
--- a/pw_unit_test/static_library_support_test.cc
+++ b/pw_unit_test/static_library_support_test.cc
@@ -41,15 +41,17 @@
   }
 } check_that_tests_ran;
 
-// TODO(b/234882063): Convert this to a compilation failure test.
-#if defined(PW_COMPILE_FAIL_TEST_FailsToLinkInvalidTestSuite)
+// Test that linking fails if these macros refer to tests that do not exist.
+// These cannot be a negative compilation tests because they compile
+// successfully, but fail to link.
+#if 0
 
 PW_UNIT_TEST_LINK_FILE_CONTAINING_TEST(NotARealSuite, NotARealTest);
 
-#elif defined(PW_COMPILE_FAIL_TEST_FailsToLinkInvalidTestName)
+#elif 0
 
 PW_UNIT_TEST_LINK_FILE_CONTAINING_TEST(StaticLibraryArchivedTest, NotARealTest);
 
-#endif  // compile fail tests
+#endif  // negative linking tests
 
 }  // namespace pw::unit_test
diff --git a/targets/host/target_toolchains.gni b/targets/host/target_toolchains.gni
index 8d5afea..e657c30 100644
--- a/targets/host/target_toolchains.gni
+++ b/targets/host/target_toolchains.gni
@@ -307,7 +307,8 @@
 _pigweed_internal = {
   pw_status_CONFIG = "$dir_pw_status:check_if_used"
 
-  pw_compilation_testing_NEGATIVE_COMPILATION_ENABLED = true
+  # TODO(b/241565082): Enable NC testing in GN Windows when it is fixed.
+  pw_compilation_testing_NEGATIVE_COMPILATION_ENABLED = host_os != "win"
 }
 
 # Host toolchains exclusively for upstream Pigweed use. To give upstream Pigweed
diff --git a/targets/stm32f429i_disc1/target_toolchains.gni b/targets/stm32f429i_disc1/target_toolchains.gni
index a6e2dc9..89af23c 100644
--- a/targets/stm32f429i_disc1/target_toolchains.gni
+++ b/targets/stm32f429i_disc1/target_toolchains.gni
@@ -25,7 +25,8 @@
 }
 
 _target_config = {
-  pw_compilation_testing_NEGATIVE_COMPILATION_ENABLED = true
+  # TODO(b/241565082): Enable NC testing in GN Windows when it is fixed.
+  pw_compilation_testing_NEGATIVE_COMPILATION_ENABLED = host_os != "win"
 
   # Use the logging main.
   pw_unit_test_MAIN = "$dir_pw_unit_test:logging_main"