From 3a7705624359678edaed5c7b9686cae034cb4bfd Mon Sep 17 00:00:00 2001 From: Florian Mayer Date: Mon, 8 Dec 2025 13:10:30 -0800 Subject: [PATCH 1/6] change Created using spr 1.3.7 --- .../clang-tidy/abseil/UncheckedStatusOrAccessCheck.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/clang-tools-extra/clang-tidy/abseil/UncheckedStatusOrAccessCheck.h b/clang-tools-extra/clang-tidy/abseil/UncheckedStatusOrAccessCheck.h index cf47703f0a972..8fefee4691be6 100644 --- a/clang-tools-extra/clang-tidy/abseil/UncheckedStatusOrAccessCheck.h +++ b/clang-tools-extra/clang-tidy/abseil/UncheckedStatusOrAccessCheck.h @@ -10,7 +10,7 @@ namespace clang::tidy::abseil { // assuring that it contains a value. // // For details on the dataflow analysis implemented in this check see: -// http://google3/devtools/cymbal/nullability/statusor +// clang/lib/Analysis/FlowSensitive/Models/UncheckedStatusOrAccessModel.cpp class UncheckedStatusOrAccessCheck : public ClangTidyCheck { public: using ClangTidyCheck::ClangTidyCheck; From d020c4b77da52906b21b382650e664439c3aaa65 Mon Sep 17 00:00:00 2001 From: Florian Mayer Date: Mon, 8 Dec 2025 17:49:15 -0800 Subject: [PATCH 2/6] test Created using spr 1.3.7 --- .../abseil/Inputs/absl/meta/type_traits.h | 46 ++ .../abseil/Inputs/absl/status/status.h | 69 +++ .../abseil/Inputs/absl/status/statusor.h | 346 ++++++++++++++ .../checkers/abseil/Inputs/cstddef.h | 10 + .../checkers/abseil/Inputs/initializer_list | 11 + .../checkers/abseil/Inputs/type_traits | 427 ++++++++++++++++++ .../abseil-unchecked-statusor-access.cpp | 138 ++++++ 7 files changed, 1047 insertions(+) create mode 100644 clang-tools-extra/test/clang-tidy/checkers/abseil/Inputs/absl/meta/type_traits.h create mode 100644 clang-tools-extra/test/clang-tidy/checkers/abseil/Inputs/absl/status/status.h create mode 100644 clang-tools-extra/test/clang-tidy/checkers/abseil/Inputs/absl/status/statusor.h create mode 100644 clang-tools-extra/test/clang-tidy/checkers/abseil/Inputs/cstddef.h create mode 100644 clang-tools-extra/test/clang-tidy/checkers/abseil/Inputs/initializer_list create mode 100644 clang-tools-extra/test/clang-tidy/checkers/abseil/Inputs/type_traits create mode 100644 clang-tools-extra/test/clang-tidy/checkers/abseil/abseil-unchecked-statusor-access.cpp diff --git a/clang-tools-extra/test/clang-tidy/checkers/abseil/Inputs/absl/meta/type_traits.h b/clang-tools-extra/test/clang-tidy/checkers/abseil/Inputs/absl/meta/type_traits.h new file mode 100644 index 0000000000000..06ce61dbcc1e7 --- /dev/null +++ b/clang-tools-extra/test/clang-tidy/checkers/abseil/Inputs/absl/meta/type_traits.h @@ -0,0 +1,46 @@ +#include + +namespace absl { + +template +struct conjunction : std::true_type {}; + +template +struct conjunction + : std::conditional, T>::type {}; + +template +struct conjunction : T {}; + +template +struct disjunction : std::false_type {}; + +template +struct disjunction + : std::conditional>::type {}; + +template +struct disjunction : T {}; + +template +struct negation : std::integral_constant {}; + +template +using enable_if_t = typename std::enable_if::type; + + +template +using conditional_t = typename std::conditional::type; + +template +using remove_cv_t = typename std::remove_cv::type; + +template +using remove_reference_t = typename std::remove_reference::type; + +template +using decay_t = typename std::decay::type; + +using std::in_place; +using std::in_place_t; +} // namespace absl diff --git a/clang-tools-extra/test/clang-tidy/checkers/abseil/Inputs/absl/status/status.h b/clang-tools-extra/test/clang-tidy/checkers/abseil/Inputs/absl/status/status.h new file mode 100644 index 0000000000000..fd0910e81436a --- /dev/null +++ b/clang-tools-extra/test/clang-tidy/checkers/abseil/Inputs/absl/status/status.h @@ -0,0 +1,69 @@ +namespace absl { +struct SourceLocation { + static constexpr SourceLocation current(); + static constexpr SourceLocation + DoNotInvokeDirectlyNoSeriouslyDont(int line, const char *file_name); +}; +} // namespace absl +namespace absl { +enum class StatusCode : int { + kOk, + kCancelled, + kUnknown, + kInvalidArgument, + kDeadlineExceeded, + kNotFound, + kAlreadyExists, + kPermissionDenied, + kResourceExhausted, + kFailedPrecondition, + kAborted, + kOutOfRange, + kUnimplemented, + kInternal, + kUnavailable, + kDataLoss, + kUnauthenticated, +}; +} // namespace absl + +namespace absl { +enum class StatusToStringMode : int { + kWithNoExtraData = 0, + kWithPayload = 1 << 0, + kWithSourceLocation = 1 << 1, + kWithEverything = ~kWithNoExtraData, + kDefault = kWithPayload, +}; +class Status { +public: + Status(); + Status(const Status &base_status, absl::SourceLocation loc); + Status(Status &&base_status, absl::SourceLocation loc); + ~Status() {} + + Status(const Status &); + Status &operator=(const Status &x); + + Status(Status &&) noexcept; + Status &operator=(Status &&); + + friend bool operator==(const Status &, const Status &); + friend bool operator!=(const Status &, const Status &); + + bool ok() const { return true; } + void CheckSuccess() const; + void IgnoreError() const; + int error_code() const; + absl::Status ToCanonical() const; + void Update(const Status &new_status); + void Update(Status &&new_status); +}; + +bool operator==(const Status &lhs, const Status &rhs); +bool operator!=(const Status &lhs, const Status &rhs); + +Status OkStatus(); +Status InvalidArgumentError(const char *); + +} // namespace absl diff --git a/clang-tools-extra/test/clang-tidy/checkers/abseil/Inputs/absl/status/statusor.h b/clang-tools-extra/test/clang-tidy/checkers/abseil/Inputs/absl/status/statusor.h new file mode 100644 index 0000000000000..0151dda0cb97d --- /dev/null +++ b/clang-tools-extra/test/clang-tidy/checkers/abseil/Inputs/absl/status/statusor.h @@ -0,0 +1,346 @@ +#include "status.h" +#include +#include + +namespace absl { + +template struct StatusOr; + +namespace internal_statusor { + +template +struct HasConversionOperatorToStatusOr : std::false_type {}; + +template +void test(char (*)[sizeof(std::declval().operator absl::StatusOr())]); + +template +struct HasConversionOperatorToStatusOr(0))> + : std::true_type {}; + +template +using IsConstructibleOrConvertibleFromStatusOr = + absl::disjunction &>, + std::is_constructible &>, + std::is_constructible &&>, + std::is_constructible &&>, + std::is_convertible &, T>, + std::is_convertible &, T>, + std::is_convertible &&, T>, + std::is_convertible &&, T>>; + +template +using IsConstructibleOrConvertibleOrAssignableFromStatusOr = + absl::disjunction, + std::is_assignable &>, + std::is_assignable &>, + std::is_assignable &&>, + std::is_assignable &&>>; + +template +struct IsDirectInitializationAmbiguous + : public absl::conditional_t< + std::is_same>, + U>::value, + std::false_type, + IsDirectInitializationAmbiguous< + T, absl::remove_cv_t>>> {}; + +template +struct IsDirectInitializationAmbiguous> + : public IsConstructibleOrConvertibleFromStatusOr {}; + +template +using IsDirectInitializationValid = absl::disjunction< + // Short circuits if T is basically U. + std::is_same>>, + absl::negation, + absl::remove_cv_t>>, + std::is_same>>, + std::is_same>>, + IsDirectInitializationAmbiguous>>>; + +template +struct IsForwardingAssignmentAmbiguous + : public absl::conditional_t< + std::is_same>, + U>::value, + std::false_type, + IsForwardingAssignmentAmbiguous< + T, absl::remove_cv_t>>> {}; + +template +struct IsForwardingAssignmentAmbiguous> + : public IsConstructibleOrConvertibleOrAssignableFromStatusOr {}; + +template +using IsForwardingAssignmentValid = absl::disjunction< + // Short circuits if T is basically U. + std::is_same>>, + absl::negation, + absl::remove_cv_t>>, + std::is_same>>, + std::is_same>>, + IsForwardingAssignmentAmbiguous>>>; + +template +using IsForwardingAssignmentValid = absl::disjunction< + // Short circuits if T is basically U. + std::is_same>>, + absl::negation, + absl::remove_cv_t>>, + std::is_same>>, + std::is_same>>, + IsForwardingAssignmentAmbiguous>>>; + +template struct OperatorBase { + const T &value() const &; + T &value() &; + const T &&value() const &&; + T &&value() &&; + + const T &operator*() const &; + T &operator*() &; + const T &&operator*() const &&; + T &&operator*() &&; + + // To test that analyses are okay if there is a use of operator* + // within this base class. + const T *operator->() const { return __builtin_addressof(**this); } + T *operator->() { return __builtin_addressof(**this); } +}; + +} // namespace internal_statusor + +template +struct StatusOr : private internal_statusor::OperatorBase { + explicit StatusOr(); + + StatusOr(const StatusOr &) = default; + StatusOr &operator=(const StatusOr &) = default; + + StatusOr(StatusOr &&) = default; + StatusOr &operator=(StatusOr &&) = default; + + template < + typename U, + absl::enable_if_t< + absl::conjunction< + absl::negation>, + std::is_constructible, + std::is_convertible, + absl::negation< + internal_statusor::IsConstructibleOrConvertibleFromStatusOr< + T, U>>>::value, + int> = 0> + StatusOr(const StatusOr &); + + template < + typename U, + absl::enable_if_t< + absl::conjunction< + absl::negation>, + std::is_constructible, + absl::negation>, + absl::negation< + internal_statusor::IsConstructibleOrConvertibleFromStatusOr< + T, U>>>::value, + int> = 0> + explicit StatusOr(const StatusOr &); + + template < + typename U, + absl::enable_if_t< + absl::conjunction< + absl::negation>, + std::is_constructible, std::is_convertible, + absl::negation< + internal_statusor::IsConstructibleOrConvertibleFromStatusOr< + T, U>>>::value, + int> = 0> + StatusOr(StatusOr &&); + + template < + typename U, + absl::enable_if_t< + absl::conjunction< + absl::negation>, + std::is_constructible, + absl::negation>, + absl::negation< + internal_statusor::IsConstructibleOrConvertibleFromStatusOr< + T, U>>>::value, + int> = 0> + explicit StatusOr(StatusOr &&); + + template < + typename U, + absl::enable_if_t< + absl::conjunction< + absl::negation>, + std::is_constructible, + std::is_assignable, + absl::negation< + internal_statusor:: + IsConstructibleOrConvertibleOrAssignableFromStatusOr< + T, U>>>::value, + int> = 0> + StatusOr &operator=(const StatusOr &); + + template < + typename U, + absl::enable_if_t< + absl::conjunction< + absl::negation>, + std::is_constructible, std::is_assignable, + absl::negation< + internal_statusor:: + IsConstructibleOrConvertibleOrAssignableFromStatusOr< + T, U>>>::value, + int> = 0> + StatusOr &operator=(StatusOr &&); + + template < + typename U = absl::Status, + absl::enable_if_t< + absl::conjunction< + std::is_convertible, + std::is_constructible, + absl::negation, absl::StatusOr>>, + absl::negation, T>>, + absl::negation, absl::in_place_t>>, + absl::negation>>::value, + int> = 0> + StatusOr(U &&); + + template < + typename U = absl::Status, + absl::enable_if_t< + absl::conjunction< + absl::negation>, + std::is_constructible, + absl::negation, absl::StatusOr>>, + absl::negation, T>>, + absl::negation, absl::in_place_t>>, + absl::negation>>::value, + int> = 0> + explicit StatusOr(U &&); + + template < + typename U = absl::Status, + absl::enable_if_t< + absl::conjunction< + std::is_convertible, + std::is_constructible, + absl::negation, absl::StatusOr>>, + absl::negation, T>>, + absl::negation, absl::in_place_t>>, + absl::negation>>::value, + int> = 0> + StatusOr &operator=(U &&); + + template < + typename U = T, + typename = typename std::enable_if, std::is_assignable, + absl::disjunction< + std::is_same>, T>, + absl::conjunction< + absl::negation>, + absl::negation< + internal_statusor::HasConversionOperatorToStatusOr< + T, U &&>>>>, + internal_statusor::IsForwardingAssignmentValid>::value>:: + type> + StatusOr &operator=(U &&); + + template explicit StatusOr(absl::in_place_t, Args &&...); + + template + explicit StatusOr(absl::in_place_t, std::initializer_list, Args &&...); + + template < + typename U = T, + absl::enable_if_t< + absl::conjunction< + internal_statusor::IsDirectInitializationValid, + std::is_constructible, std::is_convertible, + absl::disjunction< + std::is_same>, + T>, + absl::conjunction< + absl::negation>, + absl::negation< + internal_statusor::HasConversionOperatorToStatusOr< + T, U &&>>>>>::value, + int> = 0> + StatusOr(U &&); + + template < + typename U = T, + absl::enable_if_t< + absl::conjunction< + internal_statusor::IsDirectInitializationValid, + absl::disjunction< + std::is_same>, + T>, + absl::conjunction< + absl::negation>, + absl::negation< + internal_statusor::HasConversionOperatorToStatusOr< + T, U &&>>>>, + std::is_constructible, + absl::negation>>::value, + int> = 0> + explicit StatusOr(U &&); + + bool ok() const; + + const Status &status() const & { return status_; } + Status status() &&; + + using StatusOr::OperatorBase::value; + + const T &ValueOrDie() const &; + T &ValueOrDie() &; + const T &&ValueOrDie() const &&; + T &&ValueOrDie() &&; + + using StatusOr::OperatorBase::operator*; + using StatusOr::OperatorBase::operator->; + + template T value_or(U &&default_value) const &; + template T value_or(U &&default_value) &&; + + template T &emplace(Args &&...args); + + template < + typename U, typename... Args, + absl::enable_if_t &, + Args &&...>::value, + int> = 0> + T &emplace(std::initializer_list ilist, Args &&...args); + +private: + absl::Status status_; +}; + +template +bool operator==(const StatusOr &lhs, const StatusOr &rhs); + +template +bool operator!=(const StatusOr &lhs, const StatusOr &rhs); + +} // namespace absl diff --git a/clang-tools-extra/test/clang-tidy/checkers/abseil/Inputs/cstddef.h b/clang-tools-extra/test/clang-tidy/checkers/abseil/Inputs/cstddef.h new file mode 100644 index 0000000000000..633260f24f99b --- /dev/null +++ b/clang-tools-extra/test/clang-tidy/checkers/abseil/Inputs/cstddef.h @@ -0,0 +1,10 @@ +namespace std { + +typedef decltype(sizeof(char)) size_t; + +using nullptr_t = decltype(nullptr); + +} // namespace std + +typedef decltype(sizeof(char)) size_t; +typedef decltype(sizeof(char*)) ptrdiff_t; diff --git a/clang-tools-extra/test/clang-tidy/checkers/abseil/Inputs/initializer_list b/clang-tools-extra/test/clang-tidy/checkers/abseil/Inputs/initializer_list new file mode 100644 index 0000000000000..886a54fe217f4 --- /dev/null +++ b/clang-tools-extra/test/clang-tidy/checkers/abseil/Inputs/initializer_list @@ -0,0 +1,11 @@ + +namespace std { + +template +class initializer_list { + public: + const T *a, *b; + initializer_list() noexcept; +}; + +} // namespace std \ No newline at end of file diff --git a/clang-tools-extra/test/clang-tidy/checkers/abseil/Inputs/type_traits b/clang-tools-extra/test/clang-tidy/checkers/abseil/Inputs/type_traits new file mode 100644 index 0000000000000..c97ae9c2d14bd --- /dev/null +++ b/clang-tools-extra/test/clang-tidy/checkers/abseil/Inputs/type_traits @@ -0,0 +1,427 @@ +#include "cstddef.h" + +namespace std { + +template +struct integral_constant { + static constexpr T value = V; +}; + +using true_type = integral_constant; +using false_type = integral_constant; + +template< class T > struct remove_reference {typedef T type;}; +template< class T > struct remove_reference {typedef T type;}; +template< class T > struct remove_reference {typedef T type;}; + +template + using remove_reference_t = typename remove_reference::type; + +template +struct remove_extent { + typedef T type; +}; + +template +struct remove_extent { + typedef T type; +}; + +template +struct remove_extent { + typedef T type; +}; + +template +struct is_array : false_type {}; + +template +struct is_array : true_type {}; + +template +struct is_array : true_type {}; + +template +struct is_function : false_type {}; + +template +struct is_function : true_type {}; + +namespace detail { + +template +struct type_identity { + using type = T; +}; // or use type_identity (since C++20) + +template +auto try_add_pointer(int) -> type_identity::type*>; +template +auto try_add_pointer(...) -> type_identity; + +} // namespace detail + +template +struct add_pointer : decltype(detail::try_add_pointer(0)) {}; + +template +struct conditional { + typedef T type; +}; + +template +struct conditional { + typedef F type; +}; + +template +struct remove_cv { + typedef T type; +}; +template +struct remove_cv { + typedef T type; +}; +template +struct remove_cv { + typedef T type; +}; +template +struct remove_cv { + typedef T type; +}; + +template +using remove_cv_t = typename remove_cv::type; + +template +struct decay { + private: + typedef typename remove_reference::type U; + + public: + typedef typename conditional< + is_array::value, typename remove_extent::type*, + typename conditional::value, typename add_pointer::type, + typename remove_cv::type>::type>::type type; +}; + +template +struct enable_if {}; + +template +struct enable_if { + typedef T type; +}; + +template +using enable_if_t = typename enable_if::type; + +template +struct is_same : false_type {}; + +template +struct is_same : true_type {}; + +template +struct is_void : is_same::type> {}; + +namespace detail { + +template +auto try_add_lvalue_reference(int) -> type_identity; +template +auto try_add_lvalue_reference(...) -> type_identity; + +template +auto try_add_rvalue_reference(int) -> type_identity; +template +auto try_add_rvalue_reference(...) -> type_identity; + +} // namespace detail + +template +struct add_lvalue_reference : decltype(detail::try_add_lvalue_reference(0)) { +}; + +template +struct add_rvalue_reference : decltype(detail::try_add_rvalue_reference(0)) { +}; + +template +typename add_rvalue_reference::type declval() noexcept; + +namespace detail { + +template +auto test_returnable(int) + -> decltype(void(static_cast(nullptr)), true_type{}); +template +auto test_returnable(...) -> false_type; + +template +auto test_implicitly_convertible(int) + -> decltype(void(declval()(declval())), true_type{}); +template +auto test_implicitly_convertible(...) -> false_type; + +} // namespace detail + +template +struct is_convertible + : integral_constant(0))::value && + decltype(detail::test_implicitly_convertible( + 0))::value) || + (is_void::value && is_void::value)> {}; + +template +inline constexpr bool is_convertible_v = is_convertible::value; + +template +using void_t = void; + +template +struct is_constructible_ : false_type {}; + +template +struct is_constructible_()...))>, T, Args...> + : true_type {}; + +template +using is_constructible = is_constructible_, T, Args...>; + +template +inline constexpr bool is_constructible_v = is_constructible::value; + +template +struct __uncvref { + typedef typename remove_cv::type>::type type; +}; + +template +using __uncvref_t = typename __uncvref<_Tp>::type; + +template +using _BoolConstant = integral_constant; + +template +using _IsSame = _BoolConstant<__is_same(_Tp, _Up)>; + +template +using _IsNotSame = _BoolConstant; + +template +struct _MetaBase; +template <> +struct _MetaBase { + template + using _SelectImpl = _Tp; + template