pw_status: Optionally add [[nodiscard]] to status

- Add a config option to pw_status that enables [[nodiscard]] to
  pw::Status and pw::StatusWithSize. Default to off.
- Add [[nodiscard]] to pw::Result.
- Add Abseil-style IgnoreError() functions to Status, StatusWithSize,
  and Result.

Change-Id: Iaa11ce5edc963e8bdd795e743545bafa9e42bf62
Reviewed-on: https://pigweed-review.googlesource.com/c/pigweed/pigweed/+/46603
Reviewed-by: Ewout van Bekkum <ewout@google.com>
Commit-Queue: Wyatt Hepler <hepler@google.com>
diff --git a/pw_protobuf/encoder_fuzzer.cc b/pw_protobuf/encoder_fuzzer.cc
index f22eaed..8eb7ed6 100644
--- a/pw_protobuf/encoder_fuzzer.cc
+++ b/pw_protobuf/encoder_fuzzer.cc
@@ -152,7 +152,7 @@
     switch (provider.ConsumeEnum<FieldType>()) {
       case kEncodeAndClear:
         // Special "field". Encode all the fields so far and reset the encoder.
-        encoder.Encode();
+        encoder.Encode().IgnoreError();
         encoder.Clear();
         break;
       case kUint32:
@@ -276,7 +276,7 @@
     }
   }
   // Ensure we call `Encode` at least once.
-  encoder.Encode();
+  encoder.Encode().IgnoreError();
 
   // Don't forget to unpoison for the next iteration!
   ASAN_UNPOISON_MEMORY_REGION(poisoned, poisoned_length);
diff --git a/pw_result/public/pw_result/result.h b/pw_result/public/pw_result/result.h
index 0fcffd1..642b667 100644
--- a/pw_result/public/pw_result/result.h
+++ b/pw_result/public/pw_result/result.h
@@ -29,7 +29,7 @@
 // TODO(pwbug/363): Refactor pw::Result to properly support non-default move
 // and/or copy assignment operators and/or constructors.
 template <typename T>
-class Result {
+class [[nodiscard]] Result {
  public:
   constexpr Result(T&& value) : value_(std::move(value)), status_(OkStatus()) {}
   constexpr Result(const T& value) : value_(value), status_(OkStatus()) {}
@@ -51,8 +51,8 @@
   constexpr Result(Result&&) = default;
   constexpr Result& operator=(Result&&) = default;
 
-  constexpr Status status() const { return status_; }
-  constexpr bool ok() const { return status_.ok(); }
+  [[nodiscard]] constexpr Status status() const { return status_; }
+  [[nodiscard]] constexpr bool ok() const { return status_.ok(); }
 
   constexpr T& value() & {
     PW_ASSERT(status_.ok());
@@ -89,6 +89,11 @@
     return std::forward<U>(default_value);
   }
 
+  // Ignores any errors. This method does nothing except potentially suppress
+  // complaints from any tools that are checking that errors are not dropped on
+  // the floor.
+  constexpr void IgnoreError() const {}
+
  private:
   struct Dummy {};
 
diff --git a/pw_status/BUILD b/pw_status/BUILD
index c4089d2..170626f 100644
--- a/pw_status/BUILD
+++ b/pw_status/BUILD
@@ -24,7 +24,10 @@
 
 pw_cc_library(
     name = "pw_status",
-    srcs = ["status.cc"],
+    srcs = [
+        "public/pw_status/internal/config.h",
+        "status.cc",
+    ],
     hdrs = [
         "public/pw_status/status.h",
         "public/pw_status/status_with_size.h",
diff --git a/pw_status/BUILD.gn b/pw_status/BUILD.gn
index c1d7086..0b51fd0 100644
--- a/pw_status/BUILD.gn
+++ b/pw_status/BUILD.gn
@@ -14,22 +14,45 @@
 
 import("//build_overrides/pigweed.gni")
 
+import("$dir_pw_build/module_config.gni")
 import("$dir_pw_build/target_types.gni")
 import("$dir_pw_docgen/docs.gni")
 import("$dir_pw_unit_test/test.gni")
 
-config("default_config") {
+declare_args() {
+  # The build target that overrides the default configuration options for this
+  # module. This should point to a source set that provides defines through a
+  # public config (which may -include a file or add defines directly).
+  pw_status_CONFIG = pw_build_DEFAULT_MODULE_CONFIG
+}
+
+config("public_include_path") {
   include_dirs = [ "public" ]
+  visibility = [ ":*" ]
 }
 
 pw_source_set("pw_status") {
-  public_configs = [ ":default_config" ]
+  public_configs = [ ":public_include_path" ]
+  public_deps = [ pw_status_CONFIG ]
   public = [
     "public/pw_status/status.h",
     "public/pw_status/status_with_size.h",
     "public/pw_status/try.h",
   ]
-  sources = [ "status.cc" ]
+  sources = [
+    "public/pw_status/internal/config.h",
+    "status.cc",
+  ]
+}
+
+config("check_if_used_config") {
+  defines = [ "PW_STATUS_CHECK_IF_USED=1" ]
+  visibility = [ ":*" ]
+}
+
+# Use this for pw_status_CONFIG to require pw::Status objects to be used.
+pw_source_set("check_if_used") {
+  public_configs = [ ":check_if_used_config" ]
 }
 
 pw_test_group("tests") {
diff --git a/pw_status/docs.rst b/pw_status/docs.rst
index c2595c5..a6434bf 100644
--- a/pw_status/docs.rst
+++ b/pw_status/docs.rst
@@ -220,6 +220,17 @@
     }
     return overall_status;
 
+Unused result warnings
+----------------------
+If the ``PW_STATUS_CHECK_IF_USED`` option is enabled, ``pw::Status`` objects
+returned from function calls must be used or it is a compilation error. To
+silence these warnings call ``IgnoreError()`` on the returned status object.
+
+``PW_STATUS_CHECK_IF_USED`` defaults to off. Pigweed and projects that use it
+will be updated to compile with this option enabled. After all projects have
+migrated, unused result warnings will be enabled unconditionally. See
+`pwbug/387 <https://bugs.chromium.org/p/pigweed/issues/detail?id=387>`_.
+
 C compatibility
 ---------------
 ``pw_status`` provides the C-compatible ``pw_Status`` enum for the status codes.
diff --git a/pw_status/public/pw_status/internal/config.h b/pw_status/public/pw_status/internal/config.h
new file mode 100644
index 0000000..62db941
--- /dev/null
+++ b/pw_status/public/pw_status/internal/config.h
@@ -0,0 +1,28 @@
+// 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.
+#pragma once
+
+// Controls whether to check if pw::Status values are used. Ununsed Status
+// values cause compilation warnings / errors. Calling the nop IgnoreError()
+// function silences these warnings.
+#ifndef PW_STATUS_CFG_CHECK_IF_USED
+#define PW_STATUS_CFG_CHECK_IF_USED 0
+#endif  // PW_STATUS_CFG_CHECK_IF_USED
+
+// Set internal macro that optionally adds the [[nodiscard]] attribute.
+#if PW_STATUS_CFG_CHECK_IF_USED
+#define _PW_STATUS_NO_DISCARD [[nodiscard]]
+#else
+#define _PW_STATUS_NO_DISCARD
+#endif  // PW_STATUS_CFG_CHECK_IF_USED
diff --git a/pw_status/public/pw_status/status.h b/pw_status/public/pw_status/status.h
index eb02440..8ed5330 100644
--- a/pw_status/public/pw_status/status.h
+++ b/pw_status/public/pw_status/status.h
@@ -13,6 +13,8 @@
 // the License.
 #pragma once
 
+#include "pw_status/internal/config.h"
+
 #ifdef __cplusplus
 extern "C" {
 #endif  // __cplusplus
@@ -185,7 +187,7 @@
 // The Status class is a thin, zero-cost abstraction around the pw_Status enum.
 // It initializes to OkStatus() by default and adds ok() and str() methods.
 // Implicit conversions are permitted between pw_Status and pw::Status.
-class Status {
+class _PW_STATUS_NO_DISCARD Status {
  public:
   using Code = pw_Status;
 
@@ -312,6 +314,11 @@
     }
   }
 
+  // Ignores any errors. This method does nothing except potentially suppress
+  // complaints from any tools that are checking that errors are not dropped on
+  // the floor.
+  constexpr void IgnoreError() const {}
+
   // Returns a null-terminated string representation of the Status.
   [[nodiscard]] const char* str() const { return pw_StatusString(code_); }
 
diff --git a/pw_status/public/pw_status/status_with_size.h b/pw_status/public/pw_status/status_with_size.h
index 48b1496..9dfb952 100644
--- a/pw_status/public/pw_status/status_with_size.h
+++ b/pw_status/public/pw_status/status_with_size.h
@@ -45,7 +45,7 @@
 //      At least for ARMv7-M, the returned struct is created on the stack, which
 //      increases code size.
 //
-class StatusWithSize {
+class _PW_STATUS_NO_DISCARD StatusWithSize {
  public:
   static constexpr StatusWithSize Cancelled(size_t size = 0) {
     return StatusWithSize(Status::Cancelled(), size);
@@ -114,15 +114,22 @@
   constexpr StatusWithSize& operator=(const StatusWithSize&) = default;
 
   // Returns the size. The size is always present, even if status() is an error.
-  constexpr size_t size() const { return size_ & kSizeMask; }
+  [[nodiscard]] constexpr size_t size() const { return size_ & kSizeMask; }
 
   // The maximum valid value for size.
-  static constexpr size_t max_size() { return kSizeMask; }
+  [[nodiscard]] static constexpr size_t max_size() { return kSizeMask; }
 
   // True if status() == OkStatus().
-  constexpr bool ok() const { return (size_ & kStatusMask) == 0u; }
+  [[nodiscard]] constexpr bool ok() const {
+    return (size_ & kStatusMask) == 0u;
+  }
 
-  constexpr Status status() const {
+  // Ignores any errors. This method does nothing except potentially suppress
+  // complaints from any tools that are checking that errors are not dropped on
+  // the floor.
+  constexpr void IgnoreError() const {}
+
+  [[nodiscard]] constexpr Status status() const {
     return static_cast<Status::Code>((size_ & kStatusMask) >> kStatusShift);
   }