| // Part of the Crubit project, under the Apache License v2.0 with LLVM | |
| // Exceptions. See /LICENSE for license information. | |
| // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception | |
| // This header defines functions available in nullability_tests. | |
| // | |
| // A test is a C++ source file that contains code to be analyzed. | |
| // Any functions marked with TEST are analysis targets. | |
| // These can include calls to assertion functions like nullable() defined here. | |
| // Such calls assert details of analysis results (nullability of expressions). | |
| // | |
| // The nullability_test tool parses the code, runs the analysis, checks the | |
| // assertions, and reports results. | |
| // | |
| // Example: | |
| // #include "nullability_test.h" | |
| // TEST void controlFlow(int* _Nullable x) { | |
| // if (x) { | |
| // nonnull(x); | |
| // } else { | |
| // nullable(x); | |
| // } | |
| // } | |
| #ifndef CRUBIT_NULLABILITY_TEST_NULLABILITY_TEST_H_ | |
| #define CRUBIT_NULLABILITY_TEST_NULLABILITY_TEST_H_ | |
| #include "nullability_annotations.h" // IWYU pragma: export | |
| namespace preamble_detail { | |
| template <typename, typename> | |
| struct require_same; | |
| template <typename T> | |
| struct require_same<T, T> { | |
| using type = T; | |
| }; | |
| } // namespace preamble_detail | |
| // Attribute applied to tests to be analyzed. | |
| // For now, only functions are supported (including constructors). | |
| // If TEST is applied to an unsupported construct, the test will fail. | |
| #define TEST [[clang::annotate("test")]] | |
| ////////////// Assertion functions interpreted by the test driver ///////////// | |
| // Non-flow-sensitive analysis assertions. | |
| // (These check the nullability vector of an expression's type). | |
| // Asserts the exact static type and nullability of an expression. | |
| // e.g. type<int* _Nonnull>(&i); | |
| // | |
| // Types written inside type<...> do not respect nullability pragmas! | |
| template < | |
| typename Expected, typename Actual, | |
| // Statically verify that the canonical types are the same. | |
| typename = typename preamble_detail::require_same<Expected, Actual>::type> | |
| void type(Actual) {} | |
| // Assertions for the full (flow-sensitive) analysis results. | |
| // (These check whether from_nullable and is_null are implied by the flow | |
| // condition. In addition, we provide general-purpose assertions for booleans.) | |
| // Asserts that its argument is considered nullable. | |
| template <typename T> | |
| void nullable(const T &) {} | |
| // Asserts that its argument is considered non-null. | |
| template <typename T> | |
| void nonnull(const T &) {} | |
| // Asserts that its argument is neither considered nullable nor non-null. | |
| template <typename T> | |
| void unknown(const T &) {} | |
| // Asserts that the analysis can prove `b` must be true at this point. | |
| inline void provable(bool b) {} | |
| // Asserts that the analysis can show `b` may be true at this point. | |
| inline void possible(bool b) {} | |
| ///////////////// Helpers to make writing tests more convenient //////////////// | |
| // Marker annotations for pointer types whose nullability is symbolic. | |
| // This means we track it as a variable: without assuming a specific value. | |
| // | |
| // Example: | |
| // void target(symbolic::X<int *> p) { | |
| // type<symbolic::X<int *> * _Nonnull>(&p); | |
| // } | |
| // | |
| // When this appears: | |
| // - in a declaration (e.g. a function param): the decl's nullability is bound | |
| // to a variable | |
| // - in a type<...>() assertion: asserts that the nullability of the | |
| // expression matches that variable. | |
| // | |
| // (For now we only provide two symbolic variables, this can be extended). | |
| namespace symbolic { | |
| template <typename T> | |
| using X [[clang::annotate("symbolic_nullability:X")]] = T; | |
| template <typename T> | |
| using Y [[clang::annotate("symbolic_nullability:Y")]] = T; | |
| } // namespace symbolic | |
| // Generic factory for generating values of arbitrary types and nullability. | |
| // | |
| // `make<int* _Nullable>()` is a value whose type in the AST is `int*` (no | |
| // nullability sugar) and whose static nullability is [Nullable]. | |
| template <typename T> | |
| static T make() | |
| // suppresses 'undefined' error when instantiated with no-linkage type. | |
| __attribute__((weakref(""))); | |
| // Tests tend to contain unused expressions like *x, so don't warn on them. | |
| #pragma clang diagnostic ignored "-Wunused-value" | |
| // Tests define functions that are not declared in any header. | |
| #pragma clang diagnostic ignored "-Wmissing-prototypes" | |
| #endif // CRUBIT_NULLABILITY_TEST_NULLABILITY_TEST_H_ |