// bsla_nodiscard.h                                                   -*-C++-*-
#ifndef INCLUDED_BSLA_NODISCARD
#define INCLUDED_BSLA_NODISCARD

#include <bsls_ident.h>
BSLS_IDENT("$Id: $")

//@PURPOSE: Provide a macro for warning about ignored function results.
//
//@MACROS:
//  BSLA_NODISCARD: warn if annotated function result is not used
//  BSLA_NODISCARD_IS_ACTIVE: 1 if 'BSLA_NODISCARD' is active and 0 otherwise
//  BSLA_NODISCARD_CPP17: insert C++17 '[[nodiscard]]' if available
//  BSLA_NODISCARD_CPP17_IS_ACTIVE: 1 if 'BSLA_NODISCARD_CPP17' is active, or 0
//
//@SEE_ALSO: bsla_annotations
//
//@DESCRIPTION: This component provides two preprocessor macros that annotate
// entities that should not be ignored.  Widely supported is annotating a
// function such that a compiler warning will be generated if the return value
// of the function is ignored, which is what 'BSLA_NODISCARD' does.  C++17 and
// later compilers also support annotating types, 'BSLA_NODISCARD_CPP17' is
// defined when such annotation is supported.
//
///Macro Reference
///---------------
//: 'BSLA_NODISCARD'
//:     This annotation causes a warning to be emitted if the caller of a
//:     so-annotated function does not use its return value.  This is useful
//:     for functions where not checking the result is either a security
//:     problem or always a bug, such as with the 'realloc' function.
//:     The annotation cannot be used portably on constructors if C++03 support
//:     is required because the GNU compiler reuses their C (language)
//:     implementation for compatibility and that does not understand that
//:     constructors.  Attempt to apply 'BSLA_NODISCARD' to a constructor will
//:     result in a warning from gcc:
//:     warning: 'warn_unused_result' attribute ignored [-Wattributes]
//:          4 |     BSLA_NODISCARD Type() {}
//:            |                         ^
//
//: 'BSLA_NODISCARD_IS_ACTIVE'
//:     The macro 'BSLA_NODISCARD_IS_ACTIVE' is defined to 0 if
//:     'BSLA_NODISCARD' expands to nothing and 1 otherwise.
//:
//: 'BSLA_NODISCARD_CPP17'
//:     This annotation can be used on both types and functions.  Due to
//:     differences in compiler parser implementations this macro must be
//:     placed *after* the 'class' (or 'struct') keyword and before the name of
//:     the type; otherwise it might not compile.
//:
//:     GNU gcc-9 does not fully implement this C++17 standard attribute as it
//:     is ignored on constructors and warns:
//:     warning: 'nodiscard' attribute applied to 'Type::Type()' with void
//:                                                  return type [-Wattributes]
//:     4 | BSLA_NODISCARD Type() {}
//:                      | ^ ~~~
//:     There is no known use case for marking individual constructors
//:     '[[ nodiscard ]]' instead of the whole type, so it is easy to avoid
//:     portability issues by marking the type itself nodiscard.
//:
//:     Marking a type with 'BSLA_NODISCARD_CPP17' in effect makes any function
//:     (including constructors) that return such a type by value or create an
//:     object of that type (in case of constructors) behave as if they were
//:     all (individually) marked by 'BSLA_NODISCARD_CPP17'.  This ability is
//:     very useful for guards or any other RAII types where using the object
//:     as part of a discarded-value expression has completely different
//:     behavior than using a (named) variable of it.
//
//: 'BSLA_NODISCARD_CPP17_IS_ACTIVE'
//:     The macro 'BSLA_NODISCARD_CPP17_IS_ACTIVE' is defined to 0 if
//:     'BSLA_NODISCARD_CPP17' expands to nothing and 1 otherwise.
//
///Usage Examples
///--------------
// This section illustrates intended use of this component.
//
///Example 1: Square Root Function
///- - - - - - - - - - - - - - - -
// First, we define a function, 'newtonsSqrt', which uses Newton's method for
// calculating a square root.  Since the function has no side effects, it
// doesn't make sense to call it and ignore its result, so we annotate it with
// 'BSLA_NODISCARD':
//..
//  BSLA_NODISCARD
//  double newtonsSqrt(double x);
//  double newtonsSqrt(double x)
//      // Take the square root of the specified 'x'.  The behavior is
//      // undefined unless 'x' is positive.
//  {
//      BSLS_ASSERT(x > 0);
//
//      double guess = 1.0;
//      for (int ii = 0; ii < 100; ++ii) {
//          guess = (guess + x / guess) / 2;
//      }
//
//      return guess;
//  }
//..
// Then, in 'main', we call it normally a few times and observe that it works
// with no compiler warnings generated:
//..
//  printf("Square root of 9.0 = %g\n",           newtonsSqrt(9.0));
//  printf("Square root of 0.01 = %g\n",          newtonsSqrt(0.01));
//  printf("Square root of 0.917 * 0.917 = %g\n", newtonsSqrt(0.917 * 0.917));
//..
// Next, we call it and do nothing with the result, which will generate a
// warning:
//..
//  newtonsSqrt(36.0);
//..
// Now, we call it and explicitly void the result, which, with gcc, still won't
// suppress the "unused result" warning:
//..
//  (void) newtonsSqrt(25.0);
//..
// Finally, we observe the compiler warnings from the last 2 calls:
//..
//  .../bsla_nodiscard.t.cpp:289:22: warning: ignoring return value of 'double
//  newtonsSqrt(double)', declared with attribute warn_unused_result
//  [-Wunused-result]
//       newtonsSqrt(36.0);
//                        ^
//  .../bsla_nodiscard.t.cpp:294:29: warning: ignoring return value of 'double
//  newtonsSqrt(double)', declared with attribute warn_unused_result
//  [-Wunused-result]
//       (void) newtonsSqrt(25.0);
//                               ^
//..
//
///Example 2: No-discard Type
/// - - - - - - - - - - - - -
// Suppose we create a guard type that is capable of closing delimiters that we
// have opened while operating on an output stream.  The example uses C I/O and
// string literals for brevity.
//
// First, we define a guard type 'DelimiterGuard0':
//..
//  class DelimGuard0 {
//    private:
//      // DATA
//      const char *d_closingText_p;  // Held, not owned
//
//    public:
//      // CREATORS
//      explicit DelimGuard0(const char *closingText);
//          // Create a delimiter guard that upon its destruction prints the
//          // specified 'closingText' to 'stdout'.  The behavior is undefined
//          // unless 'closingText' outlives the created object.
//
//      ~DelimGuard0();
//          // Print the closing text to the output file, then destroy this
//          // object.
//  };
//..
// Then we can write code that uses the guard properly:
//..
//  void guard0ProperUse()
//  {
//      printf("\n```ruby\n");  DelimGuard0 closeCode("```\n");
//
//      // Suppose long and complicated code with early returns writing some
//      // source code between the delimiters.  Instead we write something
//      // trivial for brevity:
//      printf("puts 'Hello World'\n");
//  }
//..
// Next, we demonstrate that the guard works as intended:
//..
//  guard0ProperUse();  // prints: [\n]```ruby[\n]puts 'Hello World'[\n]```[\n]
//..
// Then, we write code missing the variable name for the guard.  By not giving
// a variable name we turn what should be an automatic (local) variable
// definition into a so-called expression statement: '<expression>;'.
// Expression statements execute an expression for its side effects, then
// destroy all temporaries created in the expression "at the semicolon".  All
// the 'printf' function calls below are expression statements, they just don't
// have any temporaries to destroy.
//..
//  void discardedGuard0()
//  {
//      printf("(");  DelimGuard0(")\n");
//      printf("in-parens");
//  }
//..
// Next, we demonstrate the bug cause by the guard variable name missing:
//..
//  discardedGuard0();  // prints: ()[\n]in-parens
//..
// Then, we add the no-discard annotation to our guard type directly:
//..
//  class BSLA_NODISCARD_CPP17 DelimGuardCpp17 {
//..
// The rest is omitted for brevity.
//
// Next, we can write the buggy code again using the annotated type:
//..
//  void discardedGuardCpp17()
//  {
//      printf("[");  DelimGuardCpp17("]");
//      printf("in-brackets");
//  }
//..
// Finally, we can demonstrate using a C++17 compiler that we get a warning for
// the buggy code:
//..
// .../bsla_nodiscard.t.cpp:LLL:CC: warning: ignoring temporary created by a
//             constructor declared with 'nodiscard' attribute [-Wunused-value]
//     printf("[");  DelimGuardCpp17("]");
//                   ^~~~~~~~~~~~~~~~~~~~
//
// ...\bsla_nodiscard.t.cpp(227,36): warning C4834: discarding return value of
//                                          function with 'nodiscard' attribute
//..

#include <bsls_compilerfeatures.h>
#include <bsls_platform.h>

#if defined(BSLS_COMPILERFEATURES_SUPPORT_ATTRIBUTE_NODISCARD)
    #define BSLA_NODISCARD           [[ nodiscard ]]
    #define BSLA_NODISCARD_IS_ACTIVE 1

    #define BSLA_NODISCARD_CPP17           BSLA_NODISCARD
    #define BSLA_NODISCARD_CPP17_IS_ACTIVE 1

#elif defined(BSLS_PLATFORM_CMP_GNU) // [[nodiscard]] not supported
    #if BSLS_COMPILERFEATURES_CPLUSPLUS >= 201103L
        #define BSLA_NODISCARD           __attribute__((warn_unused_result))
        #define BSLA_NODISCARD_IS_ACTIVE 1
    #else
        // gcc attribute cannot be disabled using (void) in C++03 mode, so it
        // would create too many false warnings.
        #define BSLA_NODISCARD
        #define BSLA_NODISCARD_IS_ACTIVE 0
    #endif

    #define BSLA_NODISCARD_CPP17
    #define BSLA_NODISCARD_CPP17_IS_ACTIVE 0

#elif defined(BSLS_PLATFORM_CMP_CLANG) // nodiscard not supported && not g++
    #define BSLA_NODISCARD           __attribute__((warn_unused_result))
    #define BSLA_NODISCARD_IS_ACTIVE 1

    #define BSLA_NODISCARD_CPP17           __attribute__((warn_unused_result))
    #define BSLA_NODISCARD_CPP17_IS_ACTIVE 1

#else   // not (gcc/g++ or clang) && [[nodiscard]] not supported
    #define BSLA_NODISCARD
    #define BSLA_NODISCARD_IS_ACTIVE 0

    #define BSLA_NODISCARD_CPP17
    #define BSLA_NODISCARD_CPP17_IS_ACTIVE 0
#endif  // not (gcc/g++ or clang) && [[nodiscard]] not supported

#endif

// ----------------------------------------------------------------------------
// Copyright 2019 Bloomberg Finance L.P.
//
// 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
//
//     http://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.
// ----------------------------- END-OF-FILE ----------------------------------