// bsls_review.h                                                      -*-C++-*-
#ifndef INCLUDED_BSLS_REVIEW
#define INCLUDED_BSLS_REVIEW

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

//@PURPOSE: Provide assertion macros to safely identify contract violations.
//
//@CLASSES:
//  bsls::Review: namespace for "review" management functions
//  bsls::ReviewFailureHandlerGuard: scoped guard for changing handlers safely
//  bsls::ReviewViolation: attributes describing a failed review check
//
//@MACROS:
//  BSLS_REVIEW: runtime check typically enabled in all non-opt build modes
//  BSLS_REVIEW_SAFE: runtime check typically only enabled in safe build modes
//  BSLS_REVIEW_OPT: runtime check typically enabled in all build modes
//  BSLS_REVIEW_INVOKE: immediately invoke the current review handler
//  BSLS_REVIEW_UNREACHABLE: mark unreachable code path, enabled like REVIEW
//  BSLS_REVIEW_SAFE_UNREACHABLE: unreachable code path, enabled like SAFE
//  BSLS_REVIEW_OPT_UNREACHABLE: unreachable code path, enabled like OPT
//
//@SEE_ALSO: bsls_assert
//
//@DESCRIPTION: This component provides three "assert-like" macros,
// `BSLS_REVIEW`, `BSLS_REVIEW_SAFE`, and `BSLS_REVIEW_OPT`, that can be used
// to enable optional *redundant* runtime checks in corresponding build modes
// that are designed to log their failures so production systems can be safely
// monitored for contract violations.
//
// This component is designed to allow apparently working production software,
// which nonetheless may harbor contract violations, to increase the number of
// precondition checks used to catch such bugs without negatively impacting the
// existing behavior of the software.  The assumption is that any contract
// violations uncovered through these checks in stable software is (at least
// for the moment) benign.  This component should *not* be used for assertions
// added to new code; new code should rely on `bsls_assert`, as any contract
// violations discovered in new code may not be benign and the resulting
// behavior may be much worse than the task terminating (as would happen using
// `bsls_assert`).
//
// If the argument of a review macro evaluates to 0, a runtime-configurable
// "handler" function is invoked with a `bsls::ReviewViolation`, a
// value-semantic class that encapsulates the current filename, line number,
// level of failed check, (0-valued expression) argument text, and a count of
// how many times that check has already failed.  The default handler logs that
// a failure has occurred and then allows processing to continue, thus not
// adversely impacting the running program.  The class `bsls::Review` provides
// functions for manipulating the globally configured "handler".  A scoped
// guard for setting and restoring the review handler is provided by
// `bsls::ReviewFailureHandlerGuard`.
//
// An additional macro, `BSLS_REVIEW_INVOKE`, is included for directly invoking
// the current review handler as if an assertion had failed on the current
// line with a string passed in.  This string will be used as if it was the
// string the other macros generate from the expression.   Though it can be
// dynamically generated, it is recommended to use a stable string to identify
// the character of the error and to use a logging component (such as
// `bsls_log`) to log any additional information relevant to the failure prior
// to invoking the handler.
//
///Detailed Behavior
///- - - - - - - - -
// If a review fires (i.e., due to a 0-valued expression argument in a review
// macro that is enabled), there is a violation of the contract that the review
// is checking.  For such a failing review, the program is in an undefined
// state but it is deemed inadvisable to immediately abort without further
// analysis.  It is the goal of the review to be sure to log that a contract
// was violated so the issue (in either the calling code or the contract) can
// be addressed.
//
// Reviews are enabled or disabled at compile time based on the review level,
// assertion level, and build mode flags that have been defined.  It is also
// possible that assert macros (see `bsls_assert`) can be configured in review
// mode, and so they may behave exactly as would review macros with appropriate
// build flags.
//
// When enabled, the review macros will all do essentially the same thing: Each
// macro tests the predicate expression `X`, and if `!(X)` is `true`, tracks a
// count of how many times this particular review has failed and invokes the
// currently installed review handler.  An instance of `bsls::ReviewViolation`
// will be created and populated with a textual rendering of the predicate
// (`#X`), the current `__FILE__`, the current `__LINE__`, a string
// representing which particular type of review or assertion has failed, and
// the current count of how many times this predicate has failed (used
// primarily to throttle repeated logging of violations from the same
// location).  This `violation` is then passed to the currently installed
// review failure handler, a function pointer with the type
// `bsls::Review::ViolationHandler` having the signature:
// ```
// void(const bsls::ReviewViolation&)
// ```
//
///Review Levels and Build Modes
///- - - - - - - - - - - - - - -
// There are a few macros available to control which of the review macros are
// actually enabled.  These macros are for the compilation and build
// environment to provide and are not themselves defined by BDE code -- e.g.,
// by supplying one or more of these macros with `-D` options on the compiler
// command line.  In general, these macros are used to determine a
// `REVIEW_LEVEL` that can be `NONE`, `REVIEW_OPT`, `REVIEW`, or `REVIEW_SAFE`.
// Depending on the review level, different review macros will be enabled.  For
// "safer" review configurations, more macros are enabled and all lower-level
// (and presumably lower cost) macros are kept enabled.  The `NONE` level is
// primarily provided for testing to ensure that the `REVIEW_OPT` level doesn't
// actually introduce any side-effects or incur measurable overhead when
// enabled, though in practice it should not be deployed.
// ```
//  ==================================================
//  "REVIEW" Macro Instantiation Based on Review Level
//  ==================================================
// BSLS_REVIEW_LEVEL  BSLS_REVIEW_OPT BSLS_REVIEW BSLS_REVIEW_SAFE
// -----------------  --------------- ----------- ----------------
// NONE
// REVIEW_OPT         ENABLED
// REVIEW             ENABLED         ENABLED
// REVIEW_SAFE        ENABLED         ENABLED     ENABLED
// ```
// The logic for the determination of the review level checks a few different
// macros.  The first check is for one of the 4 mutually exclusive
// `BSLS_REVIEW_LEVEL` macros that can explicitly set the review level:
// ```
// MACRO                         BSLS_REVIEW_LEVEL
// -----                         ----------------
// BSLS_REVIEW_LEVEL_NONE        NONE
// BSLS_REVIEW_LEVEL_REVIEW_OPT  REVIEW_OPT
// BSLS_REVIEW_LEVEL_REVIEW      REVIEW
// BSLS_REVIEW_LEVEL_REVIEW_SAFE REVIEW_SAFE
// ```
// If none of those macros are defined, the review level is implemented to be
// exactly the same as the assertion level (see `bsls_assert`).  This is so
// that any introduced review macro will still be enabled (and become an
// assertion) when it is textually replaced with a `BSLS_ASSERT`.  This means
// that first one of the 7 mutually exclusive `BSLS_ASSERT_LEVEL` macros are
// checked to determine the review level:
// ```
// MACRO                           BSLS_REVIEW_LEVEL
// -----                           ----------------
// BSLS_ASSERT_LEVEL_ASSUME_SAFE   NONE
// BSLS_ASSERT_LEVEL_ASSUME_ASSERT NONE
// BSLS_ASSERT_LEVEL_ASSUME_OPT    NONE
// BSLS_ASSERT_LEVEL_NONE          NONE
// BSLS_ASSERT_LEVEL_ASSERT_OPT    REVIEW_OPT
// BSLS_ASSERT_LEVEL_ASSERT        REVIEW
// BSLS_ASSERT_LEVEL_ASSERT_SAFE   REVIEW_SAFE
// ```
// Finally, the default review (and assert) level, if none of the overriding
// review or assert level macros above are defined, is determined by the build
// mode.  With "safer" build modes we incorporate higher-level defensive
// program checks.  A particular build mode is implied by the relevant (BDE)
// build targets that are defined at compilation (preprocessing) time.  The
// following table shows the three (BDE) build targets that can affect the
// assertion and review levels (note that `BDE_BUILD_TARGET_DBG` plays no
// role):
// ```
//       (BDE) Build Targets
//     -----------------------
// (A) BDE_BUILD_TARGET_SAFE_2
// (B) BDE_BUILD_TARGET_SAFE
// (C) BDE_BUILD_TARGET_OPT
// ```
// *Any* of the 8 possible combinations of the three build targets is valid;
// e.g., `BDE_BUILD_TARGET_OPT` and `BDE_BUILD_TARGET_SAFE_2` may both be
// defined.  The following table shows the review level that is set depending
// on which combination of build target macros have been set:
// ```
//  =========================================================
//  "REVIEW" Level Set With no Level-Overriding Flags defined
//  =========================================================
// --- BDE_BUILD_TARGET ----   BSLS_REVIEW_LEVEL
// _SAFE_2   _SAFE    _OPT
// -------  -------  -------   -----------------
//                             REVIEW
//                   DEFINED   REVIEW_OPT
//          DEFINED            REVIEW_SAFE
//          DEFINED  DEFINED   REVIEW_SAFE
// DEFINED                     REVIEW_SAFE
// DEFINED           DEFINED   REVIEW_SAFE
// DEFINED  DEFINED            REVIEW_SAFE
// DEFINED  DEFINED  DEFINED   REVIEW_SAFE
// ```
// As the table above illustrates, with no build target explicitly defined the
// review level defaults to `REVIEW`.  If only `BDE_BUILD_TARGET_OPT` is
// defined, the review level will be set to `REVIEW_OPT`.  If either
// `BDE_BUILD_TARGET_SAFE` or `BDE_BUILD_TARGET_SAFE_2` is defined then the
// review level is set to `REVIEW_SAFE` and ALL review macros will be enabled.
//
///Runtime-Configurable Review-Failure Behavior
///- - - - - - - - - - - - - - - - - - - - - -
// In addition to the three (BSLS) "REVIEW" macros, `BSLS_REVIEW`,
// `BSLS_REVIEW_SAFE`, and `BSLS_REVIEW_OPT`, and the immediate invocation
// macro `BSLS_REVIEW_INVOKE`, this component provides (1) an `invokeHandler`
// method used (primarily) to implement these "REVIEW" macros and enable their
// runtime configuration, (2) administrative methods to configure, at runtime,
// the behavior resulting from a review failure (i.e., by installing an
// appropriate review-failure handler function), and (3) a suite of standard
// ("off-the-shelf") review-failure handler functions, to be installed via the
// methods (if desired), and invoked by the `invokeHandler` method of a review
// failure.
//
// When an enabled review fails, the currently installed *failure* *handler*
// ("callback") function is invoked.  The default handler is the (`static`)
// `bsls::Review::failByLog` method, which will log the failure and the current
// callstack when invoked for the first time, and exponentially less frequently
// as additional failures of the same review site occur.  A user may replace
// this default handler by using the (`static`)
// `bsls::Review::setViolationHandler` administrative method and passing it
// (the address of) a function whose signature conforms to the
// `bsls::Review::ViolationHandler` `typedef`.  This handler may be one of the
// other handler methods provided in `bsls::Review`, or a "custom" function
// written by the user.
//
// One additional provided class, `bsls::ReviewFailureHandlerGuard`, can be
// used to override the review handler within a specific block of code.  Note
// that this is primarily intended for testing and should be instantiated at a
// non-granular level, as the setting and resetting of the handler done by this
// RAII class has no provisions in it to handle being used concurrently by
// multiple threads.
//
// The primary uses for setting the review handler to a non-default handler are
// to get all reviews to behave like some kind of an assertion, or to get
// reviews to throw exceptions to facilitate testing.  Both of these scenarios
// are primarily encountered in test drivers, and the general workflows for
// reviews and assertions depend on a policy where deployed production tasks
// use the default handlers.
//
///Conditional Compilation
///- - - - - - - - - - - -
// To recap, there are three (mutually compatible) general *build* *targets*:
// * `BDE_BUILD_TARGET_OPT`
// * `BDE_BUILD_TARGET_SAFE`
// * `BDE_BUILD_TARGET_SAFE_2`
// seven (mutually exclusive) component-specific *assertion* *levels*:
// * `BSLS_ASSERT_LEVEL_ASSERT_SAFE`
// * `BSLS_ASSERT_LEVEL_ASSERT`
// * `BSLS_ASSERT_LEVEL_ASSERT_OPT`
// * `BSLS_ASSERT_LEVEL_NONE`
// * `BSLS_ASSERT_LEVEL_ASSUME_OPT`
// * `BSLS_ASSERT_LEVEL_ASSUME_ASSERT`
// * `BSLS_ASSERT_LEVEL_ASSUME_SAFE`
// and four (mutually exclusive) component-specific *review* *levels*:
// * `BSLS_REVIEW_LEVEL_REVIEW_SAFE`
// * `BSLS_REVIEW_LEVEL_REVIEW`
// * `BSLS_REVIEW_LEVEL_REVIEW_OPT`
// * `BSLS_REVIEW_LEVEL_NONE`
// The above macros can be defined (externally) by the build environment to
// affect which of the three *review* *macros*:
// * `BSLS_REVIEW_SAFE(boolean-valued expression)`
// * `BSLS_REVIEW(boolean-valued expression)`
// * `BSLS_REVIEW_OPT(boolean-valued expression)`
// will be enabled (i.e., instantiated).
//
// The public interface of this component also provides some additional
// intermediate macros to identify how the various `BSLS_REVIEW` macros have
// been instantiated.  These exist for each level and have the following
// suffixes and meanings:
// * `IS_ACTIVE`: Defined if the corresponding level is enabled.
// * `IS_USED`: Defined if the expressions for the corresponding level need to
//   be valid (i.e., if they are"'ODR-used").
//
// Putting that together, these 3 macros are defined if the corresponding macro
// is enabled:
// * `BSLS_REVIEW_SAFE_IS_ACTIVE`
// * `BSLS_REVIEW_IS_ACTIVE`
// * `BSLS_REVIEW_OPT_IS_ACTIVE`
//
// Finally, three more macros with the `IS_USED` suffix are defined when the
// expression for the corresponding macro is going to be compiled.  This will
// be true if the macro is enabled or if `BSLS_REVIEW_VALIDATE_DISABLED_MACROS`
// has been defined.
// * `BSLS_REVIEW_SAFE_IS_USED`
// * `BSLS_REVIEW_IS_USED`
// * `BSLS_REVIEW_OPT_IS_USED`
//
// All of these additional "predicate" macros can be used directly by clients
// of this component to conditionally compile code other than just (BSLS)
// reviews, but that should be done with care to be sure code compiles and is
// compatible across all build modes.
//
///Validating Disabled Macro Expressions
///- - - - - - - - - - - - - - - - - - -
// An additional external macro, `BSLS_REVIEW_VALIDATE_DISABLED_MACROS`, can be
// defined to control the compile time behavior of `bsls_review`.  Enabling
// this macro configures all *disabled* review macros to still instantiate
// their predicates (in a non-evaluated context) to be sure that the predicate
// is still syntactically valid.  This can be used to ensure reviews that are
// rarely enabled have valid expressions.
//
///Uses for `bsls_review`
///- - - - - - - - - -
// `bsls_review` exists primarily as a step towards adding or modifying
// instances of `bsls_assert` macros in code that are already running in
// production.  New `bsls_assert` macros are risky to add, and enabled
// `bsls_assert` macros that were not previously enabled is equally risky.
//
// Given code that is already running and deployed, but not "breaking" in
// obvious ways, the review process provides a way to see if contracts are
// being met in those running processes without risking unexpected aborts
// happening in those systems.  The contracts that might be getting violated in
// already deployed applications need to be fixed and avoided, BUT there exists
// no evidence that these violations are currently crashing the system.
//
// In general, `bsls_review` can also be considered as a way to identify if
// deployed code is violating function contracts in some way.  Once calling
// applications and library code have been altered so all contracts are being
// met, uses of `bsls_review` macros are then changed into the corresponding
// `bsls_assert` macros, which enforce the contracts instead of simply
// monitoring them.  In some cases, the `bsls_review` macros can also be
// replaced by new behavior, e.g., widening narrow contracts without risking
// breaking the expectations of existing applications.
//
///Adding New `bsls_assert` Checks To New Code
///- - - - - - - - - - - - - - - - - - - - - -
// `bsls_review` is not appropriate for "new" code that has not yet been
// deployed to a production environment; use `bsls_assert` instead.
//
///Adding Assertions With `bsls_review`
///- - - - - - - - - - - - - - - - -
// The introduction of a new assertion using `bsls_review` should follow these
// steps:
// 1. Add the appropriate `bsls_review` macros to your code.
// 2. Commit these changes and make sure all processes using your code get
//    rebuilt and deployed to production.
// 3. Monitor relevant log files for "BSLS_REVIEW failure" messages citing the
//    location of these reviews.
// 4. If any failures occurred, fix the calling code, or widen the contract
//    being violated (which should be rare).
// 5. Wait for sufficient time to pass to be confident that the contract is not
//    being violated by the current version of the software in production.
// 6. Replace `BSLS_REVIEW` with `BSLS_ASSERT` and redeploy.
//
///Reducing The Assertion Level For Existing `bsls_assert` Macros
///- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
// In order to reduce the level of an existing assertion, such as changing a
// `BSLS_ASSERT_SAFE` into a `BSLS_ASSERT`, or a `BSLS_ASSERT` into a
// `BSLS_ASSERT_OPT`, you should follow a very similar process to the process
// for adding a new assertion of that level.  There are two possible approaches
// that largely depend on how widely deployed the code is that is already built
// with the old assertions enabled.
// 1. If the existing assertion is not enabled in deployed production code,
//    such as a `BSLS_ASSERT_SAFE` where client code rarely or never deploys
//    safe builds, then simply remove the old assertion and start the process
//    described above of adding in the higher level assertion as if it was a
//    newly added assertion.  Here changing a `BSLS_ASSERT_SAFE` to a
//    `BSLS_ASSERT` would then begin with changing the `BSLS_ASSERT_SAFE` into
//    a `BSLS_REVIEW` and continuing as above.
// 2. If the existing assertion is depended on in released production code then
//    the process involved needs to maintain the assertion at the existing
//    level while adding in the review at the higher level, eventually
//    including only the assert at the higher level when the process is
//    complete.
//
// Therefore, the "safest" way to increase an assertion level is to follow
// these steps:
// 1. Given an existing `BSLS_ASSERT` such as this:
//:..
//:  BSLS_ASSERT(some_test());
//:..
//: You can duplicate the test as a `BSLS_REVIEW_OPT`, if the test is very
//: negligible, like this:
//:..
//:  BSLS_ASSERT(some_test());
//:  BSLS_REVIEW_OPT(some_test());
//:..
//: If the duplicated check is unacceptably expensive (which should make you
//: question making this assertion a `BSLS_ASSERT_OPT` in the first place),
//: then you can opt for the more cumbersome:
//:..
//:  #ifdef BSLS_ASSERT_IS_ACTIVE
//:    BSLS_ASSERT(some_test());
//:  #else
//:    BSLS_REVIEW_OPT(some_test());
//:  #endif
//:..
// 2. Commit these changes and make sure all processes built at the higher
//    assertion level are rebuilt and deployed, thus beginning the review
//    process for this changed assertion.
// 3. Follow steps 3-5 of the {Adding Assertions With `bsls_review`} workflow.
// 4. When complete, replace all changes made in step 1 with:
//:..
//:  BSLS_ASSERT_OPT(some_test());
//:..
// 5. Revel in the more widespread use of your existing assertion.
//
///Increasing Deployed Assertion Levels With `BSLS_REVIEW_LEVEL_*`
///- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
// A common situation in deployed tasks is that they are built with only
// `BSLS_ASSERT_OPT` enabled.  To improve the robustness of these applications,
// there is then a desire to run them with `BSLS_ASSERT` enabled, or even
// `BSLS_ASSERT_SAFE`.  Enabling these assertions, however, brings in the same
// risks of hard failures for contract violations that adding new assertions
// does.  `BSLS_REVIEW` provides a process for making this change without
// risking aborting processes that were previously "running just fine".
//
// Any explicit setting of the `BSLS_REVIEW_LEVEL` ("review level") to a level
// higher than the `BSLS_ASSERT_LEVEL` ("assert level") will not only enable
// the `BSLS_REVIEW` macros at that level, but it will turn all `BSLS_ASSERT`
// macros at that level into reviews as well, and disable any assumption of
// `BSLS_ASSERT` assertions.  Given a task that is built with an assertion
// level of `OPT`, if you set the review level to `REVIEW` you will then get
// logs and notifications of any failed `BSLS_ASSERT` checks, but those failing
// checks will not immediately abort your application.
//
// So the process for deploying an application with a higher assertion level is
// to follow these steps:
// 1. Rebuild the task with the review level set to the desired assertion
//    level, using `BSLS_REVIEW_LEVEL_REVIEW_OPT`, `BSLS_REVIEW_LEVEL_REVIEW`,
//    or `BSLS_REVIEW_LEVEL_REVIEW_SAFE`.
// 2. Deploy the task.
// 3. Monitor relevant log files for "BSLS_REVIEW failure" messages citing the
//    location of these reviews.
// 4. Once all review failures have been addressed and no failures are logged
//    for a "sufficient" time, remove the explicitly set `BSLS_REVIEW_LEVEL`
//    from your build and change to set an explicit `BSLS_ASSERT_LEVEL` at your
//    new level.
// 5. Deploy your newly built application with increased enabled defensive
//    checks.
// 6. Revel in the comfort of taking advantage of the additional defensive
//    checks now enabled in your code.
//
///Checking Library Usage Before Changing it With `bsls_review`
///- - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
// Occasionally, given code that is in use in production, you may come across a
// case that makes no sense but that you want to reimplement with altered
// behavior.  The previous behavior might have been out of contract or just
// seemingly nonsensical, but you are not confident that no one is actually
// running code that executes that behavior and relies on it.
//
// `bsls_review` here provides a way to insert a review that checks if the code
// in question is ever being invoked.  To do that, follow these steps:
// 1. Depending on the structure of the code you want to monitor, add a
//    `BSLS_REVIEW_INVOKE` or a `BSLS_REVIEW_OPT` with a check that will fail
//    on the condition you want to monitor.  Use these macros as they will be
//    enabled in almost all build modes.
// 2. Commit your changes and get the applications using your code deployed.
// 3. If any code hits your review, assess it for why and fix the calling code
//    or re-assess the behavior you want to change.
// 4. Once "sufficient" time has passed with no review failures, remove the
//    `bsls_review` checks entirely.
// 5. Make changes to your code's behavior that impact only those states where
//    previously your review would have failed.  Check in and deploy those
//    changes.
// 6. Revel in safely having deployed a Liskov-substitutable version of your
//    library with exciting and new behavior.
//
///Concerns When Adding Reviews or Assertions
///- - - - - - - - - - - - - - - - - - - - -
// 1. Performance: In general, a new `bsls_review` check will perform exactly
//    the same as a `bsls_assert` with the same predicate.  One subtle
//    difference is that apparently working software can contain failing
//    `bsls_review` checks logging failures that may be ignored (whereas
//    `bsls_assert` failures are designed to be hard to ignore).  Handling
//    these failures requires maintaining the failure count using atomic
//    operations that may negatively impact performance if checks are failing
//    (and ignored) in performance-sensitive code.  Failing checks should
//    always be addressed as promptly as possible.
// 2. Downstream Linking: For a library developer, part of the review rollout
//    process will rely heavily on users of your library relinking multiple
//    times during the process of adding reviews.  One might assume that this
//    means the verification that a review is not failing can extend an
//    indefinite amount of time.  The better overall policy is to realize that,
//    just like library users can opt-in to rebuilding their tasks more often,
//    the stability benefits of being involved in the review process by
//    relinking and rolling out more often are opt-in as well.  For tasks that
//    are rebuilt very infrequently, they will simply have to accept that
//    library misuse on their end might result in crashes due to asserts that
//    were introduced between their own too-infrequent releases.
// 3. Unit Testing with Reviews: In general, unit test log files are rarely
//    monitored or read, so a failing `BSLS_REVIEW` is unlikely to be caught
//    during unit tests.  Test drivers should almost always set the review
//    handler to the abort handler so any failed reviews are caught immediately
//    as test failures.
// 4. Sufficient Time: All of the review-related workflows mention running
//    reviewed code for "sufficient" time to know the check is not failing.
//    "Sufficient" time will vary by application.
//
///Usage
///-----
//
///Example 1: Adding `BSLS_ASSERT` To An Existing Function
///- - - - - - - - - - - - - - - - - - - - - - - - - - - -
// Suppose you have an existing function, already deployed to production, that
// was not written with defensive programming in mind.  In order to increase
// robustness, you would like to add `BSLS_ASSERT` macros to this function that
// match the contract originally written when this function was initially
// released.
//
// For example, consider the function `myFunc` in the class `FunctionsV1` that
// was implemented like this:
// ```
// my_functions.h
// ...
//
//  class FunctionsV1 {
//      // ...
//    public:
//      // ...
//
//      static int myFunc(int x, int y);
//          // Do something with the specified positive integers 'x' and 'y'.
//  };
//
//  inline int FunctionsV1::myFunc(int x, int y)
//  {
//      int output = 0;
//      // ... do stuff with 'x' and 'y'.
//      return output;
//  }
// ```
// Notice that there are no checks on `x` and `y` within `myFunc` and no
// assertions to detect use of `myFunc` outside of its contract.  On the other
// hand, `myFunc` is part of legacy code that has been in use extensively for
// years or decades, so clearly this is not causing a problem (yet).
//
// Upon reviewing this class you realize that `myFunc` produces random results
// for values of `x` or `y` less than 0.  You, however, do not have enough
// information to conclude that no one is calling it with negative values and
// just using the bad results unknowingly.  There are a number of possibilities
// for how the result of this undefined behavior might be going unnoticed.
// * The invalid value might be discarded by a bounds check later in the
//   process.
// * The invalid value may only result in a small glitch the users have not
//   noticed or ignored.
// * The resulting value may actually be valid, but allowing negative input
//   for `x` and `y` may preclude potential future development in ways we do
//   not want to allow.
// All of these are bad, but adding in checks with `BSLS_ASSERT` that would
// replace these bad behaviors by process termination would turn silent errors
// into loud errors (potentially worse).  On the other hand, by not adding
// `BSLS_ASSERT` checks we permit future misuses of this function, which may
// not be innocuous, to potentially reach production systems.  `BSLS_REVIEW`
// here serves as a bridge, from the current state of `myFunc` (entirely
// unchecked) to the ideal state of `myFunc` (where misuse is caught loudly and
// immediately through `BSLS_ASSERT`), following a path that doesn't risk
// turning an un-noticed or irrelevant error into one that will significantly
// hinder ongoing business.
//
// The solution to this is to *initially* reimplement `myFunc` using
// `BSLS_REVIEW` like this:
// ```
// my_functions.h
// ...
//  #include <bsls_review.h>
// ...
//
//  class FunctionsV2 {
//      // ...
//    public:
//      // ...
//
//      static int myFunc(int x, int y);
//          // Do something with the specified 'x' and 'y'.  The behavior is
//          // undefined unless 'x > 0' and 'y > 0'.
//  };
//
//  inline int FunctionsV2::myFunc(int x, int y)
//  {
//      BSLS_REVIEW(x > 0);
//      BSLS_REVIEW(y > 0);
//      int output = 0;
//      // ... do stuff with 'x' and 'y'.
//      return output;
//  }
// ```
// Now you can deploy this code to production and then begin reviewing logs.
// The log messages you should look for are those produced by `bsls::Review`s
// default review failure handler and will be similar to:
// ```
// ERROR myfunction.h:17 BSLS_REVIEW failure (myfunction.h:17 level:R-DBG):
//                                'x > 0' Please run "/bb/bin/showfunc.tsk ...
// ```
// `showfunc.tsk` is a Bloomberg application that can be used (along with the
// task binary) to convert the reported stack addresses to a more traditional
// stack trace with a function call stack.
//
// It is important to note that `BSLS_REVIEW` is purely informative, and adding
// a review will not adversely affect behavior, and may in fact alert the
// library author to common client misconceptions about the intended behavior.
//
// For example, let's say actual usage makes it clear that users expect 0 to be
// valid values for the arguments to `myFunc`, and nothing in the
// implementation prevents us from accepting 0 as input and producing the
// answer clients expect.  Instead of changing all the clients, we may instead
// choose to change the function contract (and implemented checks):
// ```
// my_functions.h
// ...
//  #include <bsls_review.h>
// ...
//
//  class FunctionsV3 {
//      // ...
//    public:
//      // ...
//
//      static int myFunc(int x, int y);
//          // Do something with the specified 'x' and 'y'.  The behavior is
//          // undefined unless 'x >= 0' and 'y >= 0'.
//  };
//
//  inline int FunctionsV3::myFunc(int x, int y)
//  {
//      BSLS_REVIEW(x >= 0);
//      BSLS_REVIEW(y >= 0);
//      int output = 0;
//      // ... do stuff with 'x' and 'y'.
//      return output;
//  }
// ```
// Finally, at some point, the implementation of `myFunc` using `BSLS_REVIEW`
// has been running a suitable amount of time that you are comfortable
// transitioning the use of `bsls_review` to `bsls_assert`.  We now use our
// favorite text editor or script to replace "BSLS_REVIEW" with "BSLS_ASSERT":
// ```
// my_functions.h
// ...
//  #include <bsls_assert.h>
// ...
//
//  class FunctionsV4 {
//      // ...
//    public:
//      // ...
//
//      static int myFunc(int x, int y);
//          // Do something with the specified 'x' and 'y'.  The behavior is
//          // undefined unless 'x >= 0' and 'y >= 0'.
//  };
//
//  inline int FunctionsV4::myFunc(int x, int y)
//  {
//      BSLS_ASSERT(x >= 0);
//      BSLS_ASSERT(y >= 0);
//      int output = 0;
//      // ... do stuff with 'x' and 'y'.
//      return output;
//  }
// ```
// At this point, any contract violations in the use of `myFunc` in new code
// will be caught immediately (i.e., in appropriate build modes).

#include <bsls_annotation.h>
#include <bsls_assertimputil.h>
#include <bsls_atomicoperations.h>
#include <bsls_buildtarget.h>
#include <bsls_compilerfeatures.h>
#include <bsls_keyword.h>
#include <bsls_performancehint.h>
#include <bsls_platform.h>

#ifdef BSLS_ASSERT_USE_CONTRACTS
#include <contract>
#endif

                       // =============================
                       // Checks for Pre-Defined macros
                       // =============================

#if defined(BSLS_REVIEW)
#error BSLS_REVIEW is already defined!
#endif

#if defined(BSLS_REVIEW_REVIEW_IMP)
#error BSLS_REVIEW_REVIEW_IMP is already defined!
#endif

#if defined(BSLS_REVIEW_REVIEW_COUNT_IMP)
#error BSLS_REVIEW_REVIEW_COUNT_IMP is already defined!
#endif

#if defined(BSLS_REVIEW_DISABLED_IMP)
#error BSLS_REVIEW_DISABLED_IMP is already defined!
#endif

#if defined(BSLS_REVIEW_INVOKE)
#error BSLS_REVIEW_INVOKE is already defined!
#endif

#if defined(BSLS_REVIEW_UNREACHABLE_IMP)
#error BSLS_REVIEW_UNREACHABLE_IMP is already defined!
#endif

#if defined(BSLS_REVIEW_UNREACHABLE_DISABLED_IMP)
#error BSLS_REVIEW_UNREACHABLE_DISABLED_IMP is already defined!
#endif

#if defined(BSLS_REVIEW_IS_ACTIVE)
#error BSLS_REVIEW_IS_ACTIVE is already defined!
#endif

#if defined(BSLS_REVIEW_IS_USED)
#error BSLS_REVIEW_IS_USED is already defined!
#endif

#if defined(BSLS_REVIEW_OPT)
#error BSLS_REVIEW_OPT is already defined!
#endif

#if defined(BSLS_REVIEW_OPT_IS_ACTIVE)
#error BSLS_REVIEW_OPT_IS_ACTIVE is already defined!
#endif

#if defined(BSLS_REVIEW_OPT_IS_USED)
#error BSLS_REVIEW_OPT_IS_USED is already defined!
#endif

#if defined(BSLS_REVIEW_SAFE)
#error BSLS_REVIEW_SAFE is already defined!
#endif

#if defined(BSLS_REVIEW_SAFE_IS_ACTIVE)
#error BSLS_REVIEW_SAFE_IS_ACTIVE is already defined!
#endif

#if defined(BSLS_REVIEW_SAFE_IS_USED)
#error BSLS_REVIEW_SAFE_IS_USED is already defined!
#endif

#if defined(BSLS_REVIEW_SAFE_UNREACHABLE)
#error BSLS_REVIEW_SAFE_UNREACHABLE is already defined!
#endif

#if defined(BSLS_REVIEW_UNREACHABLE)
#error BSLS_REVIEW_UNREACHABLE is already defined!
#endif

#if defined(BSLS_REVIEW_OPT_UNREACHABLE)
#error BSLS_REVIEW_OPT_UNREACHABLE is already defined!
#endif

                     // =================================
                     // (BSLS) "REVIEW" Macro Definitions
                     // =================================

// Implementation Note: We wrap the 'if' statement below in a (seemingly
// redundant) do-while-false loop to require, syntactically, a trailing
// semicolon, and to ensure that the macro behaves properly in an if-then-else
// context -- even if one forgets to wrap, with curly braces, the body of an
// 'if' having just a single 'BSLS_REVIEW*' statement.

               // =============================================
               // Factored Implementation for Internal Use Only
               // =============================================

#if !(defined(BSLS_REVIEW_LEVEL_REVIEW_SAFE) ||                               \
      defined(BSLS_REVIEW_LEVEL_REVIEW) ||                                    \
      defined(BSLS_REVIEW_LEVEL_REVIEW_OPT) ||                                \
      defined(BSLS_REVIEW_LEVEL_NONE))
// In order to replicate the control logic of 'BSLS_ASSERT', if there has been
// no explicit review level set we check to see if there has been an explicit
// assert level set.  If so, we act as though the review level has been
// explicitly set to the same thing (instead of just basing the review level
// off of only the build mode).
    #if defined(BSLS_ASSERT_LEVEL_ASSERT_SAFE)
       #define BSLS_REVIEW_LEVEL_REVIEW_SAFE
       #define BSLS_REVIEW_NO_REVIEW_MACROS_DEFINED 0
    #elif defined(BSLS_ASSERT_LEVEL_ASSERT)
       #define BSLS_REVIEW_LEVEL_REVIEW
       #define BSLS_REVIEW_NO_REVIEW_MACROS_DEFINED 0
    #elif defined(BSLS_ASSERT_LEVEL_ASSERT_OPT)
       #define BSLS_REVIEW_LEVEL_REVIEW_OPT
       #define BSLS_REVIEW_NO_REVIEW_MACROS_DEFINED 0
    #elif defined(BSLS_ASSERT_LEVEL_NONE) ||                                  \
          defined(BSLS_ASSERT_LEVEL_ASSUME_SAFE) ||                           \
          defined(BSLS_ASSERT_LEVEL_ASSUME_ASSERT) ||                         \
          defined(BSLS_ASSERT_LEVEL_ASSUME_OPT)
       #define BSLS_REVIEW_LEVEL_NONE
       #define BSLS_REVIEW_NO_REVIEW_MACROS_DEFINED 0
    #else
       // Only here, with no explicit review level OR assertion level, does
       // this macro finally get set to true, which will trigger
       // buildtarget-based logic for macro configuration
       #define BSLS_REVIEW_NO_REVIEW_MACROS_DEFINED 1
    #endif
#else
    #define BSLS_REVIEW_NO_REVIEW_MACROS_DEFINED 0
#endif

                        // ============================
                        // BSLS_REVIEW_REVIEW_COUNT_IMP
                        // ============================

// This macro is defined in order to maintain a static 'count' where a
// 'BSLS_REVIEW' is used.  When possible, this is done inside a lamba (which
// will only be invoked when a violation happens) in order to facilitate use
// within 'constexpr' functions.

#ifdef BSLS_COMPILERFEATURES_SUPPORT_CONSTEXPR_CPP14
    #define BSLS_REVIEW_REVIEW_COUNT_IMP                                      \
        const int lastCount = BloombergLP::bsls::Review::updateCount(         \
            []{                                                               \
                static BloombergLP::bsls::Review::Count count = {0};          \
                return &count;                                                \
            }() );
#else
    #define BSLS_REVIEW_REVIEW_COUNT_IMP                                      \
        static BloombergLP::bsls::Review::Count count = {0};                  \
        const int lastCount = BloombergLP::bsls::Review::updateCount(&count);
#endif

                           // ======================
                           // BSLS_REVIEW_REVIEW_IMP
                           // ======================

#ifdef BSLS_ASSERT_USE_CONTRACTS
#define BSLS_REVIEW_REVIEW_IMP(X,LVL) [[ assert check_maybe_continue : X ]]

#ifdef BSLS_REVIEW_VALIDATE_DISABLED_MACROS
#define BSLS_REVIEW_DISABLED_IMP(X,LVL)   [[ assert ignore : X ]]
#else
#define BSLS_REVIEW_DISABLED_IMP(X,LVL)
#endif

#else // BSLS_ASSERT_USE_CONTRACTS

#define BSLS_REVIEW_REVIEW_IMP(X,LVL) do {                                    \
        if (BSLS_PERFORMANCEHINT_PREDICT_UNLIKELY(!(X))) {                    \
            BSLS_PERFORMANCEHINT_UNLIKELY_HINT;                               \
            BSLS_REVIEW_REVIEW_COUNT_IMP;                                     \
            BloombergLP::bsls::Review::invokeHandler(                         \
                BloombergLP::bsls::ReviewViolation(#X,                        \
                                                   BSLS_ASSERTIMPUTIL_FILE,   \
                                                   BSLS_ASSERTIMPUTIL_LINE,   \
                                                   LVL,                       \
                                                   lastCount));               \
        }                                                                     \
    } while (false)

#ifdef BSLS_REVIEW_VALIDATE_DISABLED_MACROS
#define BSLS_REVIEW_DISABLED_IMP(X,LVL) (void)sizeof((X)?true:false)
#else
#define BSLS_REVIEW_DISABLED_IMP(X,LVL)
#endif

#endif

                             // ==================
                             // BSLS_REVIEW_INVOKE
                             // ==================

// 'BSLS_REVIEW_INVOKE' is always active.
#define BSLS_REVIEW_INVOKE(X) do {                                            \
        BSLS_REVIEW_REVIEW_COUNT_IMP;                                         \
        BloombergLP::bsls::Review::invokeHandler(                             \
            BloombergLP::bsls::ReviewViolation(                               \
                                  X,                                          \
                                  BSLS_ASSERTIMPUTIL_FILE,                    \
                                  BSLS_ASSERTIMPUTIL_LINE,                    \
                                  BloombergLP::bsls::Review::k_LEVEL_INVOKE,  \
                                  lastCount));                                \
    } while (false)

                          // ========================
                          // BSLS_REVIEW_UNREACHABLE
                          // ========================

// 'BSLS_REVIEW_UNREACHABLE_IMP' invokes the review handler unconditionally
// with the specified message 'X'.  Unlike 'BSLS_REVIEW_REVIEW_IMP', this macro
// does not check a condition -- it always invokes the handler when reached.

#define BSLS_REVIEW_UNREACHABLE_IMP(X,LVL) do {                               \
        BSLS_REVIEW_REVIEW_COUNT_IMP;                                         \
        BloombergLP::bsls::Review::invokeHandler(                             \
            BloombergLP::bsls::ReviewViolation(                               \
                                  X,                                          \
                                  BSLS_ASSERTIMPUTIL_FILE,                    \
                                  BSLS_ASSERTIMPUTIL_LINE,                    \
                                  LVL,                                        \
                                  lastCount));                                \
    } while (false)

// 'BSLS_REVIEW_UNREACHABLE_DISABLED_IMP' does nothing except ensures that `X`
// is a valid expression convertable to 'const char *'.  It is used when the
// corresponding UNREACHABLE macro is disabled for the current build mode.

#define BSLS_REVIEW_UNREACHABLE_DISABLED_IMP(X,LVL) do {                      \
    (void)sizeof(static_cast<const char*>(X));                                \
} while (false)

                              // ================
                              // BSLS_REVIEW_SAFE
                              // ================

// Determine if 'BSLS_REVIEW_SAFE' should be active.

#if defined(BSLS_REVIEW_LEVEL_REVIEW_SAFE)                                    \
    || BSLS_REVIEW_NO_REVIEW_MACROS_DEFINED && (                              \
           defined(BDE_BUILD_TARGET_SAFE_2) ||                                \
           defined(BDE_BUILD_TARGET_SAFE)         )

    #define BSLS_REVIEW_SAFE_IS_ACTIVE  // also usable directly in client code
#endif

// Indicate when 'BSLS_REVIEW_SAFE' arguments will be ODR-used.
#if defined(BSLS_REVIEW_SAFE_IS_ACTIVE) ||                                    \
    defined(BSLS_REVIEW_VALIDATE_DISABLED_MACROS)
    #define BSLS_REVIEW_SAFE_IS_USED
#endif

// Define 'BSLS_REVIEW_SAFE' accordingly.

#if defined(BSLS_REVIEW_SAFE_IS_ACTIVE)
    #define BSLS_REVIEW_SAFE(X) BSLS_REVIEW_REVIEW_IMP(                       \
                                     X,                                       \
                                     BloombergLP::bsls::Review::k_LEVEL_SAFE)
#else
    #define BSLS_REVIEW_SAFE(X) BSLS_REVIEW_DISABLED_IMP(                     \
                                     X,                                       \
                                     BloombergLP::bsls::Review::k_LEVEL_SAFE)
#endif

// Define 'BSLS_REVIEW_SAFE_UNREACHABLE' accordingly.

#if defined(BSLS_REVIEW_SAFE_IS_ACTIVE)
    #define BSLS_REVIEW_SAFE_UNREACHABLE(X) BSLS_REVIEW_UNREACHABLE_IMP(      \
                                     X,                                       \
                                     BloombergLP::bsls::Review::k_LEVEL_SAFE)
#else
    #define BSLS_REVIEW_SAFE_UNREACHABLE(X)                                   \
                             BSLS_REVIEW_UNREACHABLE_DISABLED_IMP(            \
                                     X,                                       \
                                     BloombergLP::bsls::Review::k_LEVEL_SAFE)
#endif

                                // ===========
                                // BSLS_REVIEW
                                // ===========

// Determine if 'BSLS_REVIEW' should be active.

#if defined(BSLS_REVIEW_LEVEL_REVIEW_SAFE) ||                                 \
    defined(BSLS_REVIEW_LEVEL_REVIEW)                                         \
    || BSLS_REVIEW_NO_REVIEW_MACROS_DEFINED && (                              \
           defined(BDE_BUILD_TARGET_SAFE_2) ||                                \
           defined(BDE_BUILD_TARGET_SAFE)   ||                                \
           !defined(BDE_BUILD_TARGET_OPT)         )

    #define BSLS_REVIEW_IS_ACTIVE       // also usable directly in client code
#endif

// Indicate when 'BSLS_REVIEW' arguments will be ODR-used.
#if defined(BSLS_REVIEW_IS_ACTIVE) ||                                         \
    defined(BSLS_REVIEW_VALIDATE_DISABLED_MACROS)
    #define BSLS_REVIEW_IS_USED
#endif

// Define 'BSLS_REVIEW' accordingly.

#if defined(BSLS_REVIEW_IS_ACTIVE)
    #define BSLS_REVIEW(X) BSLS_REVIEW_REVIEW_IMP(                            \
                                   X,                                         \
                                   BloombergLP::bsls::Review::k_LEVEL_REVIEW)
#else
    #define BSLS_REVIEW(X) BSLS_REVIEW_DISABLED_IMP(                          \
                                   X,                                         \
                                   BloombergLP::bsls::Review::k_LEVEL_REVIEW)
#endif

// Define 'BSLS_REVIEW_UNREACHABLE' accordingly.

#if defined(BSLS_REVIEW_IS_ACTIVE)
    #define BSLS_REVIEW_UNREACHABLE(X) BSLS_REVIEW_UNREACHABLE_IMP(           \
                                   X,                                         \
                                   BloombergLP::bsls::Review::k_LEVEL_REVIEW)
#else
    #define BSLS_REVIEW_UNREACHABLE(X)                                        \
                             BSLS_REVIEW_UNREACHABLE_DISABLED_IMP(            \
                                   X,                                         \
                                   BloombergLP::bsls::Review::k_LEVEL_REVIEW)
#endif

                              // ===============
                              // BSLS_REVIEW_OPT
                              // ===============

// Determine if 'BSLS_REVIEW_OPT' should be active.

#if !defined(BSLS_REVIEW_LEVEL_NONE)
    #define BSLS_REVIEW_OPT_IS_ACTIVE   // also usable directly in client code
#endif

// Indicate when 'BSLS_REVIEW_OPT' arguments will be ODR-used.
#if defined(BSLS_REVIEW_OPT_IS_ACTIVE) ||                                     \
    defined(BSLS_REVIEW_VALIDATE_DISABLED_MACROS)
    #define BSLS_REVIEW_OPT_IS_USED
#endif

// Define 'BSLS_REVIEW_OPT' accordingly.

#if defined(BSLS_REVIEW_OPT_IS_ACTIVE)
    #define BSLS_REVIEW_OPT(X) BSLS_REVIEW_REVIEW_IMP(                        \
                                      X,                                      \
                                      BloombergLP::bsls::Review::k_LEVEL_OPT)
#else
    #define BSLS_REVIEW_OPT(X) BSLS_REVIEW_DISABLED_IMP(                      \
                                      X,                                      \
                                      BloombergLP::bsls::Review::k_LEVEL_OPT)
#endif

// Define 'BSLS_REVIEW_OPT_UNREACHABLE' accordingly.

#if defined(BSLS_REVIEW_OPT_IS_ACTIVE)
    #define BSLS_REVIEW_OPT_UNREACHABLE(X) BSLS_REVIEW_UNREACHABLE_IMP(       \
                                      X,                                      \
                                      BloombergLP::bsls::Review::k_LEVEL_OPT)
#else
    #define BSLS_REVIEW_OPT_UNREACHABLE(X)                                    \
                             BSLS_REVIEW_UNREACHABLE_DISABLED_IMP(            \
                                      X,                                      \
                                      BloombergLP::bsls::Review::k_LEVEL_OPT)
#endif

// A nested include guard is needed to support the test driver implementation.
#ifndef BSLS_REVIEW_RECURSIVELY_INCLUDED_TESTDRIVER_GUARD
#define BSLS_REVIEW_RECURSIVELY_INCLUDED_TESTDRIVER_GUARD

namespace BloombergLP {
namespace bsls {

                           // =====================
                           // class ReviewViolation
                           // =====================

/// This class is an unconstrained *in-core* value-semantic class that
/// characterizes the details of a review failure that has occurred.
class ReviewViolation {

    // DATA
    const char *d_comment_p;      // the comment associated with the violation,
                                  // generally representing the expression that
                                  // failed

    const char *d_fileName_p;     // the name of the file where the violation
                                  // occurred

    int         d_lineNumber;     // the line number where the violation
                                  // occurred

    const char *d_reviewLevel_p;  // the level and type of the violation that
                                  // occurred, generally one of the 'k_LEVEL'
                                  // constants defined in 'bsls::Review' or
                                  // 'bsls::Assert'

    int         d_count;          // the number of times that a particular
                                  // failure has happened in the running
                                  // process

  public:
    // CREATORS

    /// Create a `ReviewViolation` with the specified `comment`, `fileName`,
    /// `lineNumber`, `reviewLevel`, and `count`.  Note that the supplied
    /// `reviewLevel` will usually be one of the `k_LEVEL` constants defined
    /// in `bsls::Review` (or see `bsls::Assert` for the levels that will be
    /// passed for assertions that are being reviewed).
    BSLS_KEYWORD_CONSTEXPR
    ReviewViolation(const char *comment,
                    const char *fileName,
                    int         lineNumber,
                    const char *reviewLevel,
                    int         count);

    // ACCESSORS

    /// Return the `comment` attribute of this object.
    const char *comment() const;

    /// Return the `count` attribute of this object.
    int count() const;

    /// Return the `fileName` attribute of this object.
    const char *fileName() const;

    /// Return the `lineNumber` attribute of this object.
    int lineNumber() const;

    /// Return the `reviewLevel` attribute of this object.
    const char *reviewLevel() const;
};

                                // ============
                                // class Review
                                // ============

/// This "utility" class maintains a pointer containing the address of the
/// current review-failure handler function (of type
/// `Review::ViolationHandler`) and provides methods to administer this
/// function pointer.  The `invokeHandler` method calls the
/// currently-installed failure handler.  The default installed handler is
/// the `Review::failByLog` function.
///
/// This class also provides a suite of standard failure-handler functions
/// that are suitable to be installed as the current
/// `Review::ViolationHandler` function.  Note that clients are free to
/// install any of these ("off-the-shelf") handlers, or to provide their own
/// ("custom") review-failure handler function when using this facility.
/// Also note that review-failure handlers CAN return, unlike assertion
/// failure handlers, though not returning (thus escalating review behavior
/// implicitly to the level of asserts) is acceptable.
///
/// Finally, this class defines the constant strings that are used as the
/// `reviewLevel` in `ReviewViolation`s associated with failed `BSLS_REVIEW`
/// invocations.
class Review {

  public:
    // TYPES

    /// `Count` is an alias for an atomic integer.  All `bsls_review` macros
    /// declare a static local `Count` variable that is used to track how
    /// many times that review has failed.  This count gets updated through
    /// the `bsls::Review::updateCount` function.
    typedef bsls::AtomicOperations::AtomicTypes::Int Count;

    /// `ViolationHandler` is an alias for a pointer to a function returning
    /// `void`, and taking, as a parameter, a `const` reference to a
    /// `ReviewViolation` instance.  For example:
    /// ```
    /// void myHandler(const ReviewViolation& violation);
    /// ```
    typedef void (*ViolationHandler)(const ReviewViolation&);

  private:
    // FRIENDS
    friend class ReviewFailureHandlerGuard;

    // PRIVATE CLASS METHODS

    /// Make the specified handler `function` the current review-failure
    /// handler.  This method has effect regardless of whether the
    /// `lockReviewAdministration` method has been called.
    static void setViolationHandlerRaw(Review::ViolationHandler function);

  public:
    // PUBLIC CONSTANTS

                     // 'reviewLevel' Strings

    static const char k_LEVEL_SAFE[];
    static const char k_LEVEL_OPT[];
    static const char k_LEVEL_REVIEW[];
    static const char k_LEVEL_INVOKE[];

    // CLASS METHODS

                      // Administrative Methods

    /// Make the specified handler `function` the current review-failure
    /// handler.  This method has no effect if the
    /// `lockReviewAdministration` method has been called.
    static void setViolationHandler(Review::ViolationHandler function);

    /// Return the address of the currently installed review-failure handler
    /// function.  Note that the initial value of the review-failure handler
    /// is the `Review::failByLog` method.
    static Review::ViolationHandler violationHandler();

    /// Disable all subsequent calls to `setViolationHandler`.  Note that
    /// this method has no effect on the behavior of a
    /// `ReviewFailureHandlerGuard` object.
    static void lockReviewAdministration();

                      // Dispatcher Methods (called from within macros)

    /// Increment the specified `count` and return the new value.  Instead
    /// of overflowing, when the value is sufficiently large, decrement the
    /// value so that large values repeat periodically.
    static int updateCount(Count *count);

    /// Invoke the currently installed review-failure handler function with
    /// the specified `violation` as its argument.  Note that this function
    /// is intended for use by the (BSLS) "REVIEW" macros, but may also be
    /// called by clients directly as needed.
    static void invokeHandler(const ReviewViolation& violation);

#ifdef BSLS_ASSERT_USE_CONTRACTS
    static void invokeLanguageContractHandler(
                                     const std::contract_violation& violation);
        // Call 'invokeHandler' with a 'ReviewViolation' with properties from
        // the specified 'violation', tracking a 'count' of repeated violations
        // statically.
#endif

                      // Standard Review-Failure Handlers

    /// Log a message to `stdout` that an assertion has failed with
    /// information on the failure from the specified `violation`.  A
    /// suitably formatted "cheap stack" is included in the log message that
    /// identifies the call site where the failure occurred.
    static void failByLog(const ReviewViolation& violation);

    /// Emulate the invocation of the standard `assert` macro with a `false`
    /// argument, using the specified `violation` to generate an output
    /// message and then, after logging, unconditionally abort.
    BSLS_ANNOTATION_NORETURN
    static void failByAbort(const ReviewViolation& violation);

    /// Use the specified `violation` to generate an output message and
    /// then, after logging, spin in an infinite loop.  Note that this
    /// handler function is useful for hanging a process so that a debugger
    /// may be attached to it.
    BSLS_ANNOTATION_NORETURN
    static void failBySleep(const ReviewViolation& violation);

    /// Throw an `AssertTestException` (whose attributes are `comment`,
    /// `filename`, and `lineNumber` from the specified `violation`),
    /// provided that `BDE_BUILD_TARGET_EXC` is defined; otherwise, log an
    /// appropriate message and abort the program (similar to
    /// `failByAbort`).
    BSLS_ANNOTATION_NORETURN
    static void failByThrow(const ReviewViolation& violation);
};

                      // ===============================
                      // class ReviewFailureHandlerGuard
                      // ===============================

/// An object of this class saves the current review handler and installs
/// the one specified on construction.  On destruction, the original review
/// handler is restored.  Note that two objects of this class cannot be
/// safely used concurrently from two separate threads (but may of course
/// appear sequentially, including in nested blocks and function invocations
/// within a single thread).  Note that the behavior of objects of this
/// class is unaffected by the (`static`) `Review::lockReviewAdministration`
/// method (i.e., the temporary replacement will occur, regardless of
/// whether that method has been invoked).
class ReviewFailureHandlerGuard {

    // DATA
    Review::ViolationHandler d_original;  // original (restored at destruction)

  private:
    // NOT IMPLEMENTED
    ReviewFailureHandlerGuard(const ReviewFailureHandlerGuard&);
    ReviewFailureHandlerGuard& operator=(const ReviewFailureHandlerGuard&);

  public:
    // CREATORS

    /// Create a guard object that installs the specified `temporary` review
    /// failure handler and automatically restores the original handler on
    /// destruction.
    explicit ReviewFailureHandlerGuard(Review::ViolationHandler temporary);

    /// Restore the failure handler that was in place when this object was
    /// created and destroy this guard.
    ~ReviewFailureHandlerGuard();
};

// ============================================================================
//                      INLINE FUNCTION DEFINITIONS
// ============================================================================

                           // ---------------------
                           // class ReviewViolation
                           // ---------------------

// CREATORS
BSLS_KEYWORD_CONSTEXPR
inline
ReviewViolation::ReviewViolation(const char *comment,
                                 const char *fileName,
                                 int         lineNumber,
                                 const char *reviewLevel,
                                 int         count)
: d_comment_p((comment == 0) ? "" : comment)
, d_fileName_p((fileName == 0) ? "" : fileName)
, d_lineNumber(lineNumber)
, d_reviewLevel_p((reviewLevel == 0) ? "" : reviewLevel)
, d_count(count)
{
}

// ACCESSORS
inline
const char *ReviewViolation::comment() const
{
    return d_comment_p;
}

inline
int ReviewViolation::count() const
{
    return d_count;
}

inline
const char *ReviewViolation::fileName() const
{
    return d_fileName_p;
}

inline
int ReviewViolation::lineNumber() const
{
    return d_lineNumber;
}

inline
const char *ReviewViolation::reviewLevel() const
{
    return d_reviewLevel_p;
}

}  // close package namespace
}  // close enterprise namespace

#endif // deeper include guard

          // ========================================================
          // UNDEFINE THE LOCALLY-SCOPED IMPLEMENTATION DETAIL MACROS
          // ========================================================

#undef BSLS_REVIEW_NO_REVIEW_MACROS_DEFINED

                 // =========================================
                 // IMPLEMENTATION USING THE C++ PREPROCESSOR
                 // =========================================
//
// At most one of the following review levels may be set during the compilation
// of any component that includes 'bsls_review.h':
//..
//  BSLS_REVIEW_LEVEL_REVIEW_SAFE
//  BSLS_REVIEW_LEVEL_REVIEW
//  BSLS_REVIEW_LEVEL_REVIEW_OPT
//  BSLS_REVIEW_LEVEL_NONE
//..
// ----------------------------------------------------------------------------

#if defined(BSLS_REVIEW_LEVEL_REVIEW_SAFE) &&                                 \
    defined(BSLS_REVIEW_LEVEL_REVIEW)
#error incompatible BSLS_REVIEW levels:                                       \
..._LEVEL_REVIEW_SAFE and ..._LEVEL_REVIEW
#endif

#if defined(BSLS_REVIEW_LEVEL_REVIEW_SAFE) &&                                 \
    defined(BSLS_REVIEW_LEVEL_REVIEW_OPT)
#error incompatible BSLS_REVIEW levels:                                       \
..._LEVEL_REVIEW_SAFE and ..._LEVEL_REVIEW_OPT
#endif

#if defined(BSLS_REVIEW_LEVEL_REVIEW_SAFE) &&                                 \
    defined(BSLS_REVIEW_LEVEL_NONE)
#error incompatible BSLS_REVIEW levels:                                       \
..._LEVEL_REVIEW_SAFE and ..._LEVEL_NONE
#endif

#if defined(BSLS_REVIEW_LEVEL_REVIEW) &&                                      \
    defined(BSLS_REVIEW_LEVEL_REVIEW_OPT)
#error incompatible BSLS_REVIEW levels:                                       \
..._LEVEL_REVIEW and ..._LEVEL_REVIEW_OPT
#endif

#if defined(BSLS_REVIEW_LEVEL_REVIEW) &&                                      \
    defined(BSLS_REVIEW_LEVEL_NONE)
#error incompatible BSLS_REVIEW levels:                                       \
..._LEVEL_REVIEW and ..._LEVEL_NONE
#endif

#if defined(BSLS_REVIEW_LEVEL_REVIEW_OPT) &&                                  \
    defined(BSLS_REVIEW_LEVEL_NONE)
#error incompatible BSLS_REVIEW levels:                                       \
..._LEVEL_REVIEW_OPT and ..._LEVEL_NONE
#endif

// At most one of the following assert levels may be set during the compilation
// of any component that includes 'bsls_review.h' (this is redundant with
// checks in 'bsls_assert.h', but we want to be sure these are checked even for
// users including only this header):
//..
//  BSLS_ASSERT_LEVEL_ASSERT_SAFE
//  BSLS_ASSERT_LEVEL_ASSERT
//  BSLS_ASSERT_LEVEL_ASSERT_OPT
//  BSLS_ASSERT_LEVEL_NONE
//  BSLS_ASSERT_LEVEL_ASSUME_SAFE
//  BSLS_ASSERT_LEVEL_ASSUME_ASSERT
//  BSLS_ASSERT_LEVEL_ASSUME_OPT
//..
// ----------------------------------------------------------------------------

#if defined(BSLS_ASSERT_LEVEL_ASSERT_SAFE) &&                                 \
    defined(BSLS_ASSERT_LEVEL_ASSERT)
#error incompatible BSLS_ASSERT levels:                                       \
..._LEVEL_ASSERT_SAFE and ..._LEVEL_ASSERT
#endif

#if defined(BSLS_ASSERT_LEVEL_ASSERT_SAFE) &&                                 \
    defined(BSLS_ASSERT_LEVEL_ASSERT_OPT)
#error incompatible BSLS_ASSERT levels:                                       \
..._LEVEL_ASSERT_SAFE and ..._LEVEL_ASSERT_OPT
#endif

#if defined(BSLS_ASSERT_LEVEL_ASSERT_SAFE) &&                                 \
    defined(BSLS_ASSERT_LEVEL_NONE)
#error incompatible BSLS_ASSERT levels:                                       \
..._LEVEL_ASSERT_SAFE and ..._LEVEL_NONE
#endif

#if defined(BSLS_ASSERT_LEVEL_ASSERT_SAFE) &&                                 \
    defined(BSLS_ASSERT_LEVEL_ASSUME_OPT)
#error incompatible BSLS_ASSERT levels:                                       \
..._LEVEL_ASSERT_SAFE and ..._LEVEL_ASSUME_OPT
#endif

#if defined(BSLS_ASSERT_LEVEL_ASSERT_SAFE) &&                                 \
    defined(BSLS_ASSERT_LEVEL_ASSUME_ASSERT)
#error incompatible BSLS_ASSERT levels:                                       \
..._LEVEL_ASSERT_SAFE and ..._LEVEL_ASSUME_ASSERT
#endif

#if defined(BSLS_ASSERT_LEVEL_ASSERT_SAFE) &&                                 \
    defined(BSLS_ASSERT_LEVEL_ASSUME_SAFE)
#error incompatible BSLS_ASSERT levels:                                       \
..._LEVEL_ASSERT_SAFE and ..._LEVEL_ASSUME_SAFE
#endif

#if defined(BSLS_ASSERT_LEVEL_ASSERT) &&                                      \
    defined(BSLS_ASSERT_LEVEL_ASSERT_OPT)
#error incompatible BSLS_ASSERT levels:                                       \
..._LEVEL_ASSERT and ..._LEVEL_ASSERT_OPT
#endif

#if defined(BSLS_ASSERT_LEVEL_ASSERT) &&                                      \
    defined(BSLS_ASSERT_LEVEL_NONE)
#error incompatible BSLS_ASSERT levels:                                       \
..._LEVEL_ASSERT and ..._LEVEL_NONE
#endif

#if defined(BSLS_ASSERT_LEVEL_ASSERT) &&                                      \
    defined(BSLS_ASSERT_LEVEL_ASSUME_OPT)
#error incompatible BSLS_ASSERT levels:                                       \
..._LEVEL_ASSERT and ..._LEVEL_ASSUME_OPT
#endif

#if defined(BSLS_ASSERT_LEVEL_ASSERT) &&                                      \
    defined(BSLS_ASSERT_LEVEL_ASSUME_ASSERT)
#error incompatible BSLS_ASSERT levels:                                       \
..._LEVEL_ASSERT and ..._LEVEL_ASSUME_ASSERT
#endif

#if defined(BSLS_ASSERT_LEVEL_ASSERT) &&                                      \
    defined(BSLS_ASSERT_LEVEL_ASSUME_SAFE)
#error incompatible BSLS_ASSERT levels:                                       \
..._LEVEL_ASSERT and ..._LEVEL_ASSUME_SAFE
#endif

#if defined(BSLS_ASSERT_LEVEL_ASSERT_OPT) &&                                  \
    defined(BSLS_ASSERT_LEVEL_NONE)
#error incompatible BSLS_ASSERT levels:                                       \
..._LEVEL_ASSERT_OPT and ..._LEVEL_NONE
#endif

#if defined(BSLS_ASSERT_LEVEL_ASSERT_OPT) &&                                  \
    defined(BSLS_ASSERT_LEVEL_ASSUME_OPT)
#error incompatible BSLS_ASSERT levels:                                       \
..._LEVEL_ASSERT_OPT and ..._LEVEL_ASSUME_OPT
#endif

#if defined(BSLS_ASSERT_LEVEL_ASSERT_OPT) &&                                  \
    defined(BSLS_ASSERT_LEVEL_ASSUME_ASSERT)
#error incompatible BSLS_ASSERT levels:                                       \
..._LEVEL_ASSERT_OPT and ..._LEVEL_ASSUME_ASSERT
#endif

#if defined(BSLS_ASSERT_LEVEL_ASSERT_OPT) &&                                  \
    defined(BSLS_ASSERT_LEVEL_ASSUME_SAFE)
#error incompatible BSLS_ASSERT levels:                                       \
..._LEVEL_ASSERT_OPT and ..._LEVEL_ASSUME_SAFE
#endif

#if defined(BSLS_ASSERT_LEVEL_NONE) &&                                        \
    defined(BSLS_ASSERT_LEVEL_ASSUME_OPT)
#error incompatible BSLS_ASSERT levels:                                       \
..._LEVEL_NONE and ..._LEVEL_ASSUME_OPT
#endif

#if defined(BSLS_ASSERT_LEVEL_NONE) &&                                        \
    defined(BSLS_ASSERT_LEVEL_ASSUME_ASSERT)
#error incompatible BSLS_ASSERT levels:                                       \
..._LEVEL_NONE and ..._LEVEL_ASSUME_ASSERT
#endif

#if defined(BSLS_ASSERT_LEVEL_NONE) &&                                        \
    defined(BSLS_ASSERT_LEVEL_ASSUME_SAFE)
#error incompatible BSLS_ASSERT levels:                                       \
..._LEVEL_NONE and ..._LEVEL_ASSUME_SAFE
#endif

#if defined(BSLS_ASSERT_LEVEL_ASSUME_OPT) &&                                  \
    defined(BSLS_ASSERT_LEVEL_ASSUME_ASSERT)
#error incompatible BSLS_ASSERT levels:                                       \
..._LEVEL_ASSUME_OPT and ..._LEVEL_ASSUME_ASSERT
#endif

#if defined(BSLS_ASSERT_LEVEL_ASSUME_OPT) &&                                  \
    defined(BSLS_ASSERT_LEVEL_ASSUME_SAFE)
#error incompatible BSLS_ASSERT levels:                                       \
..._LEVEL_ASSUME_OPT and ..._LEVEL_ASSUME_SAFE
#endif

#if defined(BSLS_ASSERT_LEVEL_ASSUME_ASSERT) &&                               \
    defined(BSLS_ASSERT_LEVEL_ASSUME_SAFE)
#error incompatible BSLS_ASSERT levels:                                       \
..._LEVEL_ASSUME_ASSERT and ..._LEVEL_ASSUME_SAFE
#endif

#endif

// ----------------------------------------------------------------------------
// Copyright 2018 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 ----------------------------------
