https://github.com/fmayer updated 
https://github.com/llvm/llvm-project/pull/171188

>From 3a7705624359678edaed5c7b9686cae034cb4bfd Mon Sep 17 00:00:00 2001
From: Florian Mayer <[email protected]>
Date: Mon, 8 Dec 2025 13:10:30 -0800
Subject: [PATCH 1/4] 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 <[email protected]>
Date: Mon, 8 Dec 2025 17:49:15 -0800
Subject: [PATCH 2/4] 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 <type_traits>
+
+namespace absl {
+
+template <typename... Ts>
+struct conjunction : std::true_type {};
+
+template <typename T, typename... Ts>
+struct conjunction<T, Ts...>
+    : std::conditional<T::value, conjunction<Ts...>, T>::type {};
+
+template <typename T>
+struct conjunction<T> : T {};
+
+template <typename... Ts>
+struct disjunction : std::false_type {};
+
+template <typename T, typename... Ts>
+struct disjunction<T, Ts...>
+    : std::conditional<T::value, T, disjunction<Ts...>>::type {};
+
+template <typename T>
+struct disjunction<T> : T {};
+
+template <typename T>
+struct negation : std::integral_constant<bool, !T::value> {};
+
+template <bool B, typename T = void>
+using enable_if_t = typename std::enable_if<B, T>::type;
+
+
+template <bool B, typename T, typename F>
+using conditional_t = typename std::conditional<B, T, F>::type;
+
+template <typename T>
+using remove_cv_t = typename std::remove_cv<T>::type;
+
+template <typename T>
+using remove_reference_t = typename std::remove_reference<T>::type;
+
+template <typename T>
+using decay_t = typename std::decay<T>::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 <absl/meta/type_traits.h>
+#include <initializer_list>
+
+namespace absl {
+
+template <typename T> struct StatusOr;
+
+namespace internal_statusor {
+
+template <typename T, typename U, typename = void>
+struct HasConversionOperatorToStatusOr : std::false_type {};
+
+template <typename T, typename U>
+void test(char (*)[sizeof(std::declval<U>().operator absl::StatusOr<T>())]);
+
+template <typename T, typename U>
+struct HasConversionOperatorToStatusOr<T, U, decltype(test<T, U>(0))>
+    : std::true_type {};
+
+template <typename T, typename U>
+using IsConstructibleOrConvertibleFromStatusOr =
+    absl::disjunction<std::is_constructible<T, StatusOr<U> &>,
+                      std::is_constructible<T, const StatusOr<U> &>,
+                      std::is_constructible<T, StatusOr<U> &&>,
+                      std::is_constructible<T, const StatusOr<U> &&>,
+                      std::is_convertible<StatusOr<U> &, T>,
+                      std::is_convertible<const StatusOr<U> &, T>,
+                      std::is_convertible<StatusOr<U> &&, T>,
+                      std::is_convertible<const StatusOr<U> &&, T>>;
+
+template <typename T, typename U>
+using IsConstructibleOrConvertibleOrAssignableFromStatusOr =
+    absl::disjunction<IsConstructibleOrConvertibleFromStatusOr<T, U>,
+                      std::is_assignable<T &, StatusOr<U> &>,
+                      std::is_assignable<T &, const StatusOr<U> &>,
+                      std::is_assignable<T &, StatusOr<U> &&>,
+                      std::is_assignable<T &, const StatusOr<U> &&>>;
+
+template <typename T, typename U>
+struct IsDirectInitializationAmbiguous
+    : public absl::conditional_t<
+          std::is_same<absl::remove_cv_t<absl::remove_reference_t<U>>,
+                       U>::value,
+          std::false_type,
+          IsDirectInitializationAmbiguous<
+              T, absl::remove_cv_t<absl::remove_reference_t<U>>>> {};
+
+template <typename T, typename V>
+struct IsDirectInitializationAmbiguous<T, absl::StatusOr<V>>
+    : public IsConstructibleOrConvertibleFromStatusOr<T, V> {};
+
+template <typename T, typename U>
+using IsDirectInitializationValid = absl::disjunction<
+    // Short circuits if T is basically U.
+    std::is_same<T, absl::remove_cv_t<absl::remove_reference_t<U>>>,
+    absl::negation<absl::disjunction<
+        std::is_same<absl::StatusOr<T>,
+                     absl::remove_cv_t<absl::remove_reference_t<U>>>,
+        std::is_same<absl::Status,
+                     absl::remove_cv_t<absl::remove_reference_t<U>>>,
+        std::is_same<absl::in_place_t,
+                     absl::remove_cv_t<absl::remove_reference_t<U>>>,
+        IsDirectInitializationAmbiguous<T, U>>>>;
+
+template <typename T, typename U>
+struct IsForwardingAssignmentAmbiguous
+    : public absl::conditional_t<
+          std::is_same<absl::remove_cv_t<absl::remove_reference_t<U>>,
+                       U>::value,
+          std::false_type,
+          IsForwardingAssignmentAmbiguous<
+              T, absl::remove_cv_t<absl::remove_reference_t<U>>>> {};
+
+template <typename T, typename U>
+struct IsForwardingAssignmentAmbiguous<T, absl::StatusOr<U>>
+    : public IsConstructibleOrConvertibleOrAssignableFromStatusOr<T, U> {};
+
+template <typename T, typename U>
+using IsForwardingAssignmentValid = absl::disjunction<
+    // Short circuits if T is basically U.
+    std::is_same<T, absl::remove_cv_t<absl::remove_reference_t<U>>>,
+    absl::negation<absl::disjunction<
+        std::is_same<absl::StatusOr<T>,
+                     absl::remove_cv_t<absl::remove_reference_t<U>>>,
+        std::is_same<absl::Status,
+                     absl::remove_cv_t<absl::remove_reference_t<U>>>,
+        std::is_same<absl::in_place_t,
+                     absl::remove_cv_t<absl::remove_reference_t<U>>>,
+        IsForwardingAssignmentAmbiguous<T, U>>>>;
+
+template <typename T, typename U>
+using IsForwardingAssignmentValid = absl::disjunction<
+    // Short circuits if T is basically U.
+    std::is_same<T, absl::remove_cv_t<absl::remove_reference_t<U>>>,
+    absl::negation<absl::disjunction<
+        std::is_same<absl::StatusOr<T>,
+                     absl::remove_cv_t<absl::remove_reference_t<U>>>,
+        std::is_same<absl::Status,
+                     absl::remove_cv_t<absl::remove_reference_t<U>>>,
+        std::is_same<absl::in_place_t,
+                     absl::remove_cv_t<absl::remove_reference_t<U>>>,
+        IsForwardingAssignmentAmbiguous<T, U>>>>;
+
+template <typename T> 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 <typename T>
+struct StatusOr : private internal_statusor::OperatorBase<T> {
+  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_same<T, U>>,
+              std::is_constructible<T, const U &>,
+              std::is_convertible<const U &, T>,
+              absl::negation<
+                  internal_statusor::IsConstructibleOrConvertibleFromStatusOr<
+                      T, U>>>::value,
+          int> = 0>
+  StatusOr(const StatusOr<U> &);
+
+  template <
+      typename U,
+      absl::enable_if_t<
+          absl::conjunction<
+              absl::negation<std::is_same<T, U>>,
+              std::is_constructible<T, const U &>,
+              absl::negation<std::is_convertible<const U &, T>>,
+              absl::negation<
+                  internal_statusor::IsConstructibleOrConvertibleFromStatusOr<
+                      T, U>>>::value,
+          int> = 0>
+  explicit StatusOr(const StatusOr<U> &);
+
+  template <
+      typename U,
+      absl::enable_if_t<
+          absl::conjunction<
+              absl::negation<std::is_same<T, U>>,
+              std::is_constructible<T, U &&>, std::is_convertible<U &&, T>,
+              absl::negation<
+                  internal_statusor::IsConstructibleOrConvertibleFromStatusOr<
+                      T, U>>>::value,
+          int> = 0>
+  StatusOr(StatusOr<U> &&);
+
+  template <
+      typename U,
+      absl::enable_if_t<
+          absl::conjunction<
+              absl::negation<std::is_same<T, U>>,
+              std::is_constructible<T, U &&>,
+              absl::negation<std::is_convertible<U &&, T>>,
+              absl::negation<
+                  internal_statusor::IsConstructibleOrConvertibleFromStatusOr<
+                      T, U>>>::value,
+          int> = 0>
+  explicit StatusOr(StatusOr<U> &&);
+
+  template <
+      typename U,
+      absl::enable_if_t<
+          absl::conjunction<
+              absl::negation<std::is_same<T, U>>,
+              std::is_constructible<T, const U &>,
+              std::is_assignable<T, const U &>,
+              absl::negation<
+                  internal_statusor::
+                      IsConstructibleOrConvertibleOrAssignableFromStatusOr<
+                          T, U>>>::value,
+          int> = 0>
+  StatusOr &operator=(const StatusOr<U> &);
+
+  template <
+      typename U,
+      absl::enable_if_t<
+          absl::conjunction<
+              absl::negation<std::is_same<T, U>>,
+              std::is_constructible<T, U &&>, std::is_assignable<T, U &&>,
+              absl::negation<
+                  internal_statusor::
+                      IsConstructibleOrConvertibleOrAssignableFromStatusOr<
+                          T, U>>>::value,
+          int> = 0>
+  StatusOr &operator=(StatusOr<U> &&);
+
+  template <
+      typename U = absl::Status,
+      absl::enable_if_t<
+          absl::conjunction<
+              std::is_convertible<U &&, absl::Status>,
+              std::is_constructible<absl::Status, U &&>,
+              absl::negation<std::is_same<absl::decay_t<U>, 
absl::StatusOr<T>>>,
+              absl::negation<std::is_same<absl::decay_t<U>, T>>,
+              absl::negation<std::is_same<absl::decay_t<U>, absl::in_place_t>>,
+              
absl::negation<internal_statusor::HasConversionOperatorToStatusOr<
+                  T, U &&>>>::value,
+          int> = 0>
+  StatusOr(U &&);
+
+  template <
+      typename U = absl::Status,
+      absl::enable_if_t<
+          absl::conjunction<
+              absl::negation<std::is_convertible<U &&, absl::Status>>,
+              std::is_constructible<absl::Status, U &&>,
+              absl::negation<std::is_same<absl::decay_t<U>, 
absl::StatusOr<T>>>,
+              absl::negation<std::is_same<absl::decay_t<U>, T>>,
+              absl::negation<std::is_same<absl::decay_t<U>, absl::in_place_t>>,
+              
absl::negation<internal_statusor::HasConversionOperatorToStatusOr<
+                  T, U &&>>>::value,
+          int> = 0>
+  explicit StatusOr(U &&);
+
+  template <
+      typename U = absl::Status,
+      absl::enable_if_t<
+          absl::conjunction<
+              std::is_convertible<U &&, absl::Status>,
+              std::is_constructible<absl::Status, U &&>,
+              absl::negation<std::is_same<absl::decay_t<U>, 
absl::StatusOr<T>>>,
+              absl::negation<std::is_same<absl::decay_t<U>, T>>,
+              absl::negation<std::is_same<absl::decay_t<U>, absl::in_place_t>>,
+              
absl::negation<internal_statusor::HasConversionOperatorToStatusOr<
+                  T, U &&>>>::value,
+          int> = 0>
+  StatusOr &operator=(U &&);
+
+  template <
+      typename U = T,
+      typename = typename std::enable_if<absl::conjunction<
+          std::is_constructible<T, U &&>, std::is_assignable<T &, U &&>,
+          absl::disjunction<
+              std::is_same<absl::remove_cv_t<absl::remove_reference_t<U>>, T>,
+              absl::conjunction<
+                  absl::negation<std::is_convertible<U &&, absl::Status>>,
+                  absl::negation<
+                      internal_statusor::HasConversionOperatorToStatusOr<
+                          T, U &&>>>>,
+          internal_statusor::IsForwardingAssignmentValid<T, U &&>>::value>::
+          type>
+  StatusOr &operator=(U &&);
+
+  template <typename... Args> explicit StatusOr(absl::in_place_t, Args &&...);
+
+  template <typename U, typename... Args>
+  explicit StatusOr(absl::in_place_t, std::initializer_list<U>, Args &&...);
+
+  template <
+      typename U = T,
+      absl::enable_if_t<
+          absl::conjunction<
+              internal_statusor::IsDirectInitializationValid<T, U &&>,
+              std::is_constructible<T, U &&>, std::is_convertible<U &&, T>,
+              absl::disjunction<
+                  std::is_same<absl::remove_cv_t<absl::remove_reference_t<U>>,
+                               T>,
+                  absl::conjunction<
+                      absl::negation<std::is_convertible<U &&, absl::Status>>,
+                      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<T, U &&>,
+              absl::disjunction<
+                  std::is_same<absl::remove_cv_t<absl::remove_reference_t<U>>,
+                               T>,
+                  absl::conjunction<
+                      absl::negation<std::is_constructible<absl::Status, U 
&&>>,
+                      absl::negation<
+                          internal_statusor::HasConversionOperatorToStatusOr<
+                              T, U &&>>>>,
+              std::is_constructible<T, U &&>,
+              absl::negation<std::is_convertible<U &&, T>>>::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 <typename U> T value_or(U &&default_value) const &;
+  template <typename U> T value_or(U &&default_value) &&;
+
+  template <typename... Args> T &emplace(Args &&...args);
+
+  template <
+      typename U, typename... Args,
+      absl::enable_if_t<std::is_constructible<T, std::initializer_list<U> &,
+                                              Args &&...>::value,
+                        int> = 0>
+  T &emplace(std::initializer_list<U> ilist, Args &&...args);
+
+private:
+  absl::Status status_;
+};
+
+template <typename T>
+bool operator==(const StatusOr<T> &lhs, const StatusOr<T> &rhs);
+
+template <typename T>
+bool operator!=(const StatusOr<T> &lhs, const StatusOr<T> &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 <typename T>
+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 <typename T, T V>
+struct integral_constant {
+  static constexpr T value = V;
+};
+
+using true_type = integral_constant<bool, true>;
+using false_type = integral_constant<bool, false>;
+
+template< class T > struct remove_reference      {typedef T type;};
+template< class T > struct remove_reference<T&>  {typedef T type;};
+template< class T > struct remove_reference<T&&> {typedef T type;};
+
+template <class T>
+  using remove_reference_t = typename remove_reference<T>::type;
+
+template <class T>
+struct remove_extent {
+  typedef T type;
+};
+
+template <class T>
+struct remove_extent<T[]> {
+  typedef T type;
+};
+
+template <class T, size_t N>
+struct remove_extent<T[N]> {
+  typedef T type;
+};
+
+template <class T>
+struct is_array : false_type {};
+
+template <class T>
+struct is_array<T[]> : true_type {};
+
+template <class T, size_t N>
+struct is_array<T[N]> : true_type {};
+
+template <class>
+struct is_function : false_type {};
+
+template <class Ret, class... Args>
+struct is_function<Ret(Args...)> : true_type {};
+
+namespace detail {
+
+template <class T>
+struct type_identity {
+  using type = T;
+};  // or use type_identity (since C++20)
+
+template <class T>
+auto try_add_pointer(int) -> type_identity<typename 
remove_reference<T>::type*>;
+template <class T>
+auto try_add_pointer(...) -> type_identity<T>;
+
+}  // namespace detail
+
+template <class T>
+struct add_pointer : decltype(detail::try_add_pointer<T>(0)) {};
+
+template <bool B, class T, class F>
+struct conditional {
+  typedef T type;
+};
+
+template <class T, class F>
+struct conditional<false, T, F> {
+  typedef F type;
+};
+
+template <class T>
+struct remove_cv {
+  typedef T type;
+};
+template <class T>
+struct remove_cv<const T> {
+  typedef T type;
+};
+template <class T>
+struct remove_cv<volatile T> {
+  typedef T type;
+};
+template <class T>
+struct remove_cv<const volatile T> {
+  typedef T type;
+};
+
+template <class T>
+using remove_cv_t = typename remove_cv<T>::type;
+
+template <class T>
+struct decay {
+ private:
+  typedef typename remove_reference<T>::type U;
+
+ public:
+  typedef typename conditional<
+      is_array<U>::value, typename remove_extent<U>::type*,
+      typename conditional<is_function<U>::value, typename 
add_pointer<U>::type,
+                           typename remove_cv<U>::type>::type>::type type;
+};
+
+template <bool B, class T = void>
+struct enable_if {};
+
+template <class T>
+struct enable_if<true, T> {
+  typedef T type;
+};
+
+template <bool B, class T = void>
+using enable_if_t = typename enable_if<B, T>::type;
+
+template <class T, class U>
+struct is_same : false_type {};
+
+template <class T>
+struct is_same<T, T> : true_type {};
+
+template <class T>
+struct is_void : is_same<void, typename remove_cv<T>::type> {};
+
+namespace detail {
+
+template <class T>
+auto try_add_lvalue_reference(int) -> type_identity<T&>;
+template <class T>
+auto try_add_lvalue_reference(...) -> type_identity<T>;
+
+template <class T>
+auto try_add_rvalue_reference(int) -> type_identity<T&&>;
+template <class T>
+auto try_add_rvalue_reference(...) -> type_identity<T>;
+
+}  // namespace detail
+
+template <class T>
+struct add_lvalue_reference : decltype(detail::try_add_lvalue_reference<T>(0)) 
{
+};
+
+template <class T>
+struct add_rvalue_reference : decltype(detail::try_add_rvalue_reference<T>(0)) 
{
+};
+
+template <class T>
+typename add_rvalue_reference<T>::type declval() noexcept;
+
+namespace detail {
+
+template <class T>
+auto test_returnable(int)
+    -> decltype(void(static_cast<T (*)()>(nullptr)), true_type{});
+template <class>
+auto test_returnable(...) -> false_type;
+
+template <class From, class To>
+auto test_implicitly_convertible(int)
+    -> decltype(void(declval<void (&)(To)>()(declval<From>())), true_type{});
+template <class, class>
+auto test_implicitly_convertible(...) -> false_type;
+
+}  // namespace detail
+
+template <class From, class To>
+struct is_convertible
+    : integral_constant<bool,
+                        (decltype(detail::test_returnable<To>(0))::value &&
+                         decltype(detail::test_implicitly_convertible<From, 
To>(
+                             0))::value) ||
+                            (is_void<From>::value && is_void<To>::value)> {};
+
+template <class From, class To>
+inline constexpr bool is_convertible_v = is_convertible<From, To>::value;
+
+template <class...>
+using void_t = void;
+
+template <class, class T, class... Args>
+struct is_constructible_ : false_type {};
+
+template <class T, class... Args>
+struct is_constructible_<void_t<decltype(T(declval<Args>()...))>, T, Args...>
+    : true_type {};
+
+template <class T, class... Args>
+using is_constructible = is_constructible_<void_t<>, T, Args...>;
+
+template <class T, class... Args>
+inline constexpr bool is_constructible_v = is_constructible<T, Args...>::value;
+
+template <class _Tp>
+struct __uncvref {
+  typedef typename remove_cv<typename remove_reference<_Tp>::type>::type type;
+};
+
+template <class _Tp>
+using __uncvref_t = typename __uncvref<_Tp>::type;
+
+template <bool _Val>
+using _BoolConstant = integral_constant<bool, _Val>;
+
+template <class _Tp, class _Up>
+using _IsSame = _BoolConstant<__is_same(_Tp, _Up)>;
+
+template <class _Tp, class _Up>
+using _IsNotSame = _BoolConstant<!__is_same(_Tp, _Up)>;
+
+template <bool>
+struct _MetaBase;
+template <>
+struct _MetaBase<true> {
+  template <class _Tp, class _Up>
+  using _SelectImpl = _Tp;
+  template <template <class...> class _FirstFn, template <class...> class,
+            class... _Args>
+  using _SelectApplyImpl = _FirstFn<_Args...>;
+  template <class _First, class...>
+  using _FirstImpl = _First;
+  template <class, class _Second, class...>
+  using _SecondImpl = _Second;
+  template <class _Result, class _First, class... _Rest>
+  using _OrImpl =
+      typename _MetaBase<_First::value != true && sizeof...(_Rest) != 0>::
+          template _OrImpl<_First, _Rest...>;
+};
+
+template <>
+struct _MetaBase<false> {
+  template <class _Tp, class _Up>
+  using _SelectImpl = _Up;
+  template <template <class...> class, template <class...> class _SecondFn,
+            class... _Args>
+  using _SelectApplyImpl = _SecondFn<_Args...>;
+  template <class _Result, class...>
+  using _OrImpl = _Result;
+};
+
+template <bool _Cond, class _IfRes, class _ElseRes>
+using _If = typename _MetaBase<_Cond>::template _SelectImpl<_IfRes, _ElseRes>;
+
+template <class... _Rest>
+using _Or = typename _MetaBase<sizeof...(_Rest) !=
+                               0>::template _OrImpl<false_type, _Rest...>;
+
+template <bool _Bp, class _Tp = void>
+using __enable_if_t = typename enable_if<_Bp, _Tp>::type;
+
+template <class...>
+using __expand_to_true = true_type;
+template <class... _Pred>
+__expand_to_true<__enable_if_t<_Pred::value>...> __and_helper(int);
+template <class...>
+false_type __and_helper(...);
+template <class... _Pred>
+using _And = decltype(__and_helper<_Pred...>(0));
+
+template <class _Pred>
+struct _Not : _BoolConstant<!_Pred::value> {};
+
+struct __check_tuple_constructor_fail {
+  static constexpr bool __enable_explicit_default() { return false; }
+  static constexpr bool __enable_implicit_default() { return false; }
+  template <class...>
+  static constexpr bool __enable_explicit() {
+    return false;
+  }
+  template <class...>
+  static constexpr bool __enable_implicit() {
+    return false;
+  }
+};
+
+template <typename, typename _Tp>
+struct __select_2nd {
+  typedef _Tp type;
+};
+template <class _Tp, class _Arg>
+typename __select_2nd<decltype((declval<_Tp>() = declval<_Arg>())),
+                      true_type>::type
+__is_assignable_test(int);
+template <class, class>
+false_type __is_assignable_test(...);
+template <class _Tp, class _Arg,
+          bool = is_void<_Tp>::value || is_void<_Arg>::value>
+struct __is_assignable_imp
+    : public decltype((__is_assignable_test<_Tp, _Arg>(0))) {};
+template <class _Tp, class _Arg>
+struct __is_assignable_imp<_Tp, _Arg, true> : public false_type {};
+template <class _Tp, class _Arg>
+struct is_assignable : public __is_assignable_imp<_Tp, _Arg> {};
+
+template <class _Tp>
+struct __libcpp_is_integral : public false_type {};
+template <>
+struct __libcpp_is_integral<bool> : public true_type {};
+template <>
+struct __libcpp_is_integral<char> : public true_type {};
+template <>
+struct __libcpp_is_integral<signed char> : public true_type {};
+template <>
+struct __libcpp_is_integral<unsigned char> : public true_type {};
+template <>
+struct __libcpp_is_integral<wchar_t> : public true_type {};
+template <>
+struct __libcpp_is_integral<short> : public true_type {};  // NOLINT
+template <>
+struct __libcpp_is_integral<unsigned short> : public true_type {};  // NOLINT
+template <>
+struct __libcpp_is_integral<int> : public true_type {};
+template <>
+struct __libcpp_is_integral<unsigned int> : public true_type {};
+template <>
+struct __libcpp_is_integral<long> : public true_type {};  // NOLINT
+template <>
+struct __libcpp_is_integral<unsigned long> : public true_type {};  // NOLINT
+template <>
+struct __libcpp_is_integral<long long> : public true_type {};  // NOLINT
+template <>                                                    // 
NOLINTNEXTLINE
+struct __libcpp_is_integral<unsigned long long> : public true_type {};
+template <class _Tp>
+struct is_integral
+    : public __libcpp_is_integral<typename remove_cv<_Tp>::type> {};
+
+template <class _Tp>
+struct __libcpp_is_floating_point : public false_type {};
+template <>
+struct __libcpp_is_floating_point<float> : public true_type {};
+template <>
+struct __libcpp_is_floating_point<double> : public true_type {};
+template <>
+struct __libcpp_is_floating_point<long double> : public true_type {};
+template <class _Tp>
+struct is_floating_point
+    : public __libcpp_is_floating_point<typename remove_cv<_Tp>::type> {};
+
+template <class _Tp>
+struct is_arithmetic
+    : public integral_constant<bool, is_integral<_Tp>::value ||
+                                         is_floating_point<_Tp>::value> {};
+
+template <class _Tp>
+struct __libcpp_is_pointer : public false_type {};
+template <class _Tp>
+struct __libcpp_is_pointer<_Tp*> : public true_type {};
+template <class _Tp>
+struct is_pointer : public __libcpp_is_pointer<typename remove_cv<_Tp>::type> {
+};
+
+template <class _Tp>
+struct __libcpp_is_member_pointer : public false_type {};
+template <class _Tp, class _Up>
+struct __libcpp_is_member_pointer<_Tp _Up::*> : public true_type {};
+template <class _Tp>
+struct is_member_pointer
+    : public __libcpp_is_member_pointer<typename remove_cv<_Tp>::type> {};
+
+template <class _Tp>
+struct __libcpp_union : public false_type {};
+template <class _Tp>
+struct is_union : public __libcpp_union<typename remove_cv<_Tp>::type> {};
+
+template <class T>
+struct is_reference : false_type {};
+template <class T>
+struct is_reference<T&> : true_type {};
+template <class T>
+struct is_reference<T&&> : true_type {};
+
+template <class T>
+inline constexpr bool is_reference_v = is_reference<T>::value;
+
+struct __two {
+  char __lx[2];
+};
+
+namespace __is_class_imp {
+template <class _Tp>
+char __test(int _Tp::*);
+template <class _Tp>
+__two __test(...);
+}  // namespace __is_class_imp
+template <class _Tp>
+struct is_class
+    : public integral_constant<bool,
+                               sizeof(__is_class_imp::__test<_Tp>(0)) == 1 &&
+                                   !is_union<_Tp>::value> {};
+
+template <class _Tp>
+struct __is_nullptr_t_impl : public false_type {};
+template <>
+struct __is_nullptr_t_impl<nullptr_t> : public true_type {};
+template <class _Tp>
+struct __is_nullptr_t
+    : public __is_nullptr_t_impl<typename remove_cv<_Tp>::type> {};
+template <class _Tp>
+struct is_null_pointer
+    : public __is_nullptr_t_impl<typename remove_cv<_Tp>::type> {};
+
+template <class _Tp>
+struct is_enum
+    : public integral_constant<
+          bool, !is_void<_Tp>::value && !is_integral<_Tp>::value &&
+                    !is_floating_point<_Tp>::value && !is_array<_Tp>::value &&
+                    !is_pointer<_Tp>::value && !is_reference<_Tp>::value &&
+                    !is_member_pointer<_Tp>::value && !is_union<_Tp>::value &&
+                    !is_class<_Tp>::value && !is_function<_Tp>::value> {};
+
+template <class _Tp>
+struct is_scalar
+    : public integral_constant<
+          bool, is_arithmetic<_Tp>::value || is_member_pointer<_Tp>::value ||
+                    is_pointer<_Tp>::value || __is_nullptr_t<_Tp>::value ||
+                    is_enum<_Tp>::value> {};
+template <>
+struct is_scalar<nullptr_t> : public true_type {};
+
+struct in_place_t {};
+
+constexpr in_place_t in_place;
+
+} // namespace std
diff --git 
a/clang-tools-extra/test/clang-tidy/checkers/abseil/abseil-unchecked-statusor-access.cpp
 
b/clang-tools-extra/test/clang-tidy/checkers/abseil/abseil-unchecked-statusor-access.cpp
new file mode 100644
index 0000000000000..865c5aa1124d3
--- /dev/null
+++ 
b/clang-tools-extra/test/clang-tidy/checkers/abseil/abseil-unchecked-statusor-access.cpp
@@ -0,0 +1,138 @@
+// RUN: %check_clang_tidy %s abseil-unchecked-statusor-access %t -- 
-header-filter='' -- -I %S/Inputs
+
+#include "absl/status/statusor.h"
+void unchecked_value_access(const absl::StatusOr<int>& sor) {
+  sor.value();
+  // CHECK-MESSAGES: :[[@LINE-1]]:7: warning: unchecked access to 
'absl::StatusOr' value [abseil-unchecked-statusor-access]
+}
+
+void unchecked_value_or_die_access(const absl::StatusOr<int>& sor) {
+  sor.ValueOrDie();
+  // CHECK-MESSAGES: :[[@LINE-1]]:7: warning: unchecked access to 
'absl::StatusOr' value [abseil-unchecked-statusor-access]
+}
+
+void unchecked_deref_operator_access(const absl::StatusOr<int>& sor) {
+  *sor;
+  // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: unchecked access to 
'absl::StatusOr' value [abseil-unchecked-statusor-access]
+}
+
+struct Foo {
+  void foo() const {}
+};
+
+void unchecked_arrow_operator_access(const absl::StatusOr<Foo>& sor) {
+  sor->foo();
+  // CHECK-MESSAGES: :[[@LINE-1]]:6: warning: unchecked access to 
'absl::StatusOr' value [abseil-unchecked-statusor-access]
+}
+
+void f2(const absl::StatusOr<int>& sor) {
+  if (sor.ok()) {
+    sor.value();
+  }
+}
+
+template <typename T>
+void function_template_without_user(const absl::StatusOr<T>& sor) {
+  sor.value(); // no-warning
+}
+
+template <typename T>
+void function_template_with_user(const absl::StatusOr<T>& sor) {
+  sor.value();
+  // CHECK-MESSAGES: :[[@LINE-1]]:7: warning: unchecked access to 
'absl::StatusOr' value [abseil-unchecked-statusor-access]
+}
+
+void function_template_user(const absl::StatusOr<int>& sor) {
+  // Instantiate the f3 function template so that it gets matched by the check.
+  function_template_with_user(sor);
+}
+
+template<typename T>
+void function_template_with_specialization(const absl::StatusOr<int>& sor) {
+  sor.value(); // no-warning
+}
+
+template<>
+void function_template_with_specialization<int>(const absl::StatusOr<int>& 
sor) {
+  sor.value();
+  // CHECK-MESSAGES: :[[@LINE-1]]:7: warning: unchecked access to 
'absl::StatusOr' value [abseil-unchecked-statusor-access]
+}
+
+
+template <typename T>
+class ClassTemplateWithSpecializations {
+  void f(const absl::StatusOr<int>& sor) {
+    sor.value(); // no-warning
+  }
+};
+
+template<typename T>
+class ClassTemplateWithSpecializations<T*> {
+  void f(const absl::StatusOr<int>& sor) {
+    sor.value(); // no-warning
+  }
+};
+
+template<>
+class ClassTemplateWithSpecializations<int> {
+  void f(const absl::StatusOr<int>& sor) {
+    sor.value();
+    // CHECK-MESSAGES: :[[@LINE-1]]:9: warning: unchecked access to 
'absl::StatusOr' value [abseil-unchecked-statusor-access]
+  }
+};
+
+// The templates below are not instantiated and CFGs can not be properly built
+// for them. They are here to make sure that the checker does not crash, but
+// instead ignores non-instantiated templates.
+
+template <typename T>
+struct C1 {};
+
+template <typename T>
+struct C2 : public C1<T> {
+  ~C2() {}
+};
+
+template <typename T, template <class> class B>
+struct C3 : public B<T> {
+  ~C3() {}
+};
+
+void multiple_unchecked_accesses(absl::StatusOr<int> sor1,
+                                 absl::StatusOr<int> sor2) {
+  for (int i = 0; i < 10; i++) {
+    sor1.ValueOrDie();
+    // CHECK-MESSAGES: :[[@LINE-1]]:10: warning: unchecked access to 
'absl::StatusOr' value [abseil-unchecked-statusor-access]
+  }
+  sor2.ValueOrDie();
+  // CHECK-MESSAGES: :[[@LINE-1]]:8: warning: unchecked access to 
'absl::StatusOr' value [abseil-unchecked-statusor-access]
+}
+
+class C4 {
+  explicit C4(absl::StatusOr<int> sor) : foo_(sor.value()) {
+    // CHECK-MESSAGES: :[[@LINE-1]]:51: warning: unchecked access to 
'absl::StatusOr' value [abseil-unchecked-statusor-access]
+  }
+  int foo_;
+};
+
+void lambda(const absl::StatusOr<int>& sor) {
+  [&sor]() {
+    sor.value();
+    // CHECK-MESSAGES: :[[@LINE-1]]:9: warning: unchecked access to 
'absl::StatusOr' value [abseil-unchecked-statusor-access]
+  }();
+
+  [&]() {
+    if (sor.ok()) {
+      sor.value();
+    }
+  }();
+
+  // Information from the surrounding context is not propagated through the
+  // lambda.
+  if (sor.ok()) {
+    [&sor]() {
+      sor.value();
+      // CHECK-MESSAGES: :[[@LINE-1]]:11: warning: unchecked access to 
'absl::StatusOr' value [abseil-unchecked-statusor-access]
+    }();
+  }
+}

>From 6580c094fff2ff4189c8b1d4c67a420e05316f9a Mon Sep 17 00:00:00 2001
From: Florian Mayer <[email protected]>
Date: Mon, 8 Dec 2025 18:10:48 -0800
Subject: [PATCH 3/4] doc

Created using spr 1.3.7
---
 .../abseil/unchecked-statusor-access.rst      | 377 ++++++++++++++++++
 1 file changed, 377 insertions(+)
 create mode 100644 
clang-tools-extra/docs/clang-tidy/checks/abseil/unchecked-statusor-access.rst

diff --git 
a/clang-tools-extra/docs/clang-tidy/checks/abseil/unchecked-statusor-access.rst 
b/clang-tools-extra/docs/clang-tidy/checks/abseil/unchecked-statusor-access.rst
new file mode 100644
index 0000000000000..95f534c390901
--- /dev/null
+++ 
b/clang-tools-extra/docs/clang-tidy/checks/abseil/unchecked-statusor-access.rst
@@ -0,0 +1,377 @@
+.. title:: clang-tidy - abseil-unchecked-statusor-access
+
+abseil-unchecked-statusor-access
+================================
+
+This check identifies unsafe accesses to values contained in
+``absl::StatusOr<T>`` objects. Below we will refer to this type as
+``StatusOr<T>``.
+
+An access to the value of an ``StatusOr<T>`` occurs when one of its
+``value``, ``operator*``, or ``operator->`` member functions is invoked.
+To align with common misconceptions, the check considers these member
+functions as equivalent, even though there are subtle differences
+related to exceptions vs. undefined behavior.
+
+An access to the value of a ``StatusOr<T>`` is considered safe if and
+only if code in the local scope (e.g. function body) ensures that the
+status of the ``StatusOr<T>`` is ok in all possible execution paths that
+can reach the access. That should happen either through an explicit
+check, using the ``StatusOr<T>::ok`` member function, or by constructing
+the ``StatusOr<T>`` in a way that shows that its status is unambiguously
+ok (e.g. by passing a value to its constructor).
+
+Below we list some examples of safe and unsafe ``StatusOr<T>`` access
+patterns.
+
+Note: If the check isn’t behaving as you would have expected on a code
+snippet, please `report it <http://github.com/llvm/llvm-project/issues/new>`__.
+
+False negatives
+---------------
+
+This check generally does **not** generate false negatives. If it cannot
+prove an access safe, it is assumed to be unsafe. That being said, there
+are some heuristics used that in very rare cases might be incorrect:
+
+-  `a const method accessor (without arguments) that returns different
+   values when called multiple times <#functionstability>`__.
+
+If you think the check generated a false negative, please `report
+it <http://github.com/llvm/llvm-project/issues/new>`__.
+
+Known limitations
+-----------------
+
+This is a non-exhaustive list of constructs that are currently not
+modelled in the check and will lead to false positives:
+
+-  `Checking a StatusOr and then capturing it in a lambda <#lambdas>`__
+-  `Indexing into a container with the same index <#containers>`__
+-  `Project specific helper-functions <#uncommonapi>`__,
+-  `Functions with a stable return value <#functionstability>`__
+-  **Any** `cross-function reasoning <#crossfunction>`__. This is by
+   design and will not change in the future.
+
+Checking if the status is ok, then accessing the value
+------------------------------------------------------
+
+The check recognizes all straightforward ways for checking the status
+and accessing the value contained in a ``StatusOr<T>`` object. For
+example:
+
+.. code:: cpp
+
+   void f(absl::StatusOr<int> sor) {
+     if (sor.ok()) {
+       use(*sor);
+     }
+   }
+
+Checking if the status is ok, then accessing the value from a copy
+------------------------------------------------------------------
+
+The criteria that the check uses is semantic, not syntactic. It
+recognizes when a copy of the ``StatusOr<T>`` object being accessed is
+known to have ok status. For example:
+
+.. code:: cpp
+
+   void f(absl::StatusOr<int> sor1) {
+     if (sor1.ok()) {
+       absl::optional<int> sor2 = sor1;
+       use(*sor2);
+     }
+   }
+
+Ensuring that the status is ok using common macros
+--------------------------------------------------
+
+The check is aware of common macros like ``ABSL_CHECK`` and ``ASSERT_THAT``.
+Those can be used to ensure that the status of a ``StatusOr<T>`` object
+is ok. For example:
+
+.. code:: cpp
+
+   void f(absl::StatusOr<int> sor) {
+     ABSL_DCHECK_OK(sor);
+     use(*sor);
+   }
+
+Ensuring that the status is ok, then accessing the value in a correlated branch
+-------------------------------------------------------------------------------
+
+The check is aware of correlated branches in the code and can figure out
+when a ``StatusOr<T>`` object is ensured to have ok status on all
+execution paths that lead to an access. For example:
+
+.. code:: cpp
+
+   void f(absl::StatusOr<int> sor) {
+     bool safe = false;
+     if (sor.ok() && SomeOtherCondition()) {
+       safe = true;
+     }
+     // ... more code...
+     if (safe) {
+       use(*sor);
+     }
+   }
+
+Accessing the value without checking the status
+-----------------------------------------------
+
+The check flags accesses to the value that are not locally guarded by a
+status check:
+
+.. code:: cpp
+
+   void f1(absl::StatusOr<int> sor) {
+     use(*sor); // unsafe: it is unclear whether the status of `sor` is ok.
+   }
+
+   void f2(absl::StatusOr<MyStruct> sor) {
+     use(sor->member); // unsafe: it is unclear whether the status of `sor` is 
ok.
+   }
+
+   void f3(absl::StatusOr<int> sor) {
+     use(sor.value()); // unsafe: it is unclear whether the status of `sor` is 
ok.
+   }
+
+Use ``ABSL_CHECK_OK`` to signal that you knowingly want to crash on
+non-OK values.
+
+NOTE: Even though using ``.value()``  on a ``nullopt`` is defined to crash,
+it is often unintentional. That is why our checker flags those as well.
+
+Accessing the value in the wrong branch
+---------------------------------------
+
+The check is aware of the state of a ``StatusOr<T>`` object in different
+branches of the code. For example:
+
+.. code:: cpp
+
+   void f(absl::StatusOr<int> sor) {
+     if (sor.ok()) {
+     } else {
+       use(*sor); // unsafe: it is clear that the status of `sor` is *not* ok.
+     }
+   }
+
+.. _functionstability:
+
+Assuming a function result to be stable
+---------------------------------------
+
+The check is aware that function results might not be stable. That is,
+consecutive calls to the same function might return different values.
+For example:
+
+.. code:: cpp
+
+   void f(Foo foo) {
+     if (foo.sor().ok()) {
+       use(*foo.sor()); // unsafe: it is unclear whether the status of 
`foo.sor()` is ok.
+     }
+   }
+
+In such cases it is best to store the result of the function call in a
+local variable and use it to access the value. For example:
+
+.. code:: cpp
+
+   void f(Foo foo) {
+     if (const auto& foo_sor = foo.sor(); foo_sor.ok()) {
+       use(*foo_sor);
+     }
+   }
+
+The check **does** assume that ``const``-qualified accessor functions
+return a stable value if no non-const function was called between the
+two calls:
+
+.. code:: cpp
+
+   class Foo {
+     const absl::StatusOr<int>& get() const {
+       [...];
+     }
+   }
+   void f(Foo foo) {
+     if (foo.get().ok()) {
+       use(*get.get());
+     }
+   }
+
+If there is a call to a non-``const``-qualified function, the check
+assumes the return value of the accessor was mutated.
+
+.. code:: cpp
+
+   class Foo {
+     const absl::StatusOr<int>& get() const {
+       [...];
+     }
+     void mutate();
+   }
+   void f(Foo foo) {
+     if (foo.get().ok()) {
+       foo.mutate();
+       use(*get.get()); // unsafe: mutate might have changed the state of the 
object
+     }
+   }
+
+.. _uncommonapi:
+
+Relying on invariants of uncommon APIs
+--------------------------------------
+
+The check is unaware of invariants of uncommon APIs. For example:
+
+.. code:: cpp
+
+   void f(Foo foo) {
+     if (foo.HasProperty("bar")) {
+       use(*foo.GetProperty("bar")); // unsafe: it is unclear whether the 
status of `foo.GetProperty("bar")` is ok.
+     }
+   }
+
+In such cases it is best to check explicitly that the status of the
+``StatusOr<T>`` object is ok. For example:
+
+.. code:: cpp
+
+   void f(Foo foo) {
+     if (const auto& property = foo.GetProperty("bar"); property.ok()) {
+       use(*property);
+     }
+   }
+
+.. _crossfunction:
+
+Checking if the status is ok, then passing the ``StatusOr<T>`` to another 
function
+----------------------------------------------------------------------------------
+
+The check relies on local reasoning. The check and value access must
+both happen in the same function. An access is considered unsafe even if
+the caller of the function performing the access ensures that the status
+of the ``StatusOr<T>`` is ok. For example:
+
+.. code:: cpp
+
+   void g(absl::StatusOr<int> sor) {
+     use(*sor); // unsafe: it is unclear whether the status of `sor` is ok.
+   }
+
+   void f(absl::StatusOr<int> sor) {
+     if (sor.ok()) {
+       g(sor);
+     }
+   }
+
+In such cases it is best to either pass the value directly when calling
+a function or check that the status of the ``StatusOr<T>`` is ok in the
+local scope of the callee. For example:
+
+.. code:: cpp
+
+   void g(int val) {
+     use(val);
+   }
+
+   void f(absl::StatusOr<int> sor) {
+     if (sor.ok()) {
+       g(*sor);
+     }
+   }
+
+Aliases created via ``using`` declarations
+------------------------------------------
+
+The check is aware of aliases of ``StatusOr<T>`` types that are created
+via ``using`` declarations. For example:
+
+.. code:: cpp
+
+   using StatusOrInt = absl::StatusOr<int>;
+
+   void f(StatusOrInt sor) {
+     use(*sor); // unsafe: it is unclear whether the status of `sor` is ok.
+   }
+
+Containers
+----------
+
+The check is more strict than necessary when it comes to containers of
+``StatusOr<T>`` values. Simply checking that the status of an element of
+a container is ok is not sufficient to deem accessing it safe. For
+example:
+
+.. code:: cpp
+
+   void f(std::vector<absl::StatusOr<int>> sors) {
+     if (sors[0].ok()) {
+       use(*sors[0]); // unsafe: it is unclear whether the status of `sors[0]` 
is ok.
+     }
+   }
+
+One needs to grab a reference to a particular object and use that
+instead:
+
+.. code:: cpp
+
+   void f(std::vector<absl::StatusOr<int>> sors) {
+     absl::StatusOr<int>& sor0 = sors[0];
+     if (sor0.ok()) {
+       use(*sor0);
+     }
+   }
+
+A future version could improve the understanding of more safe usage
+patterns that involve containers.
+
+Lambdas
+-------
+
+The check is capable of reporting unsafe ``StatusOr<T>`` accesses in
+lambdas, but isn’t smart enough to propagate information from the
+surrounding context through the lambda. This means that the following
+pattern will be reported as an unsafe access:
+
+.. code:: cpp
+
+   void f(absl::StatusOr<int> sor) {
+     if (sor.ok()) {
+       [&sor]() {
+         use(*sor); // unsafe: it is unclear whether the status of `sor` is ok.
+       }
+     }
+   }
+
+To avoid the issue, you should grab a reference to the contained object
+and capture that instead
+
+.. code:: cpp
+
+   void f(absl::StatusOr<int> sor) {
+     if (sor.ok()) {
+       auto& s = *sor;
+       [&s]() {
+         use(s);
+       }
+     }
+   }
+
+Alternatively you could add a check inside the lambda where the value is
+accessed:
+
+.. code:: cpp
+
+   void f(absl::StatusOr<int> sor) {
+     [&sor]() {
+       if (sor.ok()) {
+         use(*sor);
+       }
+     }
+   }

>From 77c75b452a8b5ad54009d3fa01cc7863d102dde1 Mon Sep 17 00:00:00 2001
From: Florian Mayer <[email protected]>
Date: Mon, 8 Dec 2025 18:24:56 -0800
Subject: [PATCH 4/4] release notes

Created using spr 1.3.7
---
 clang-tools-extra/docs/ReleaseNotes.rst | 5 +++++
 1 file changed, 5 insertions(+)

diff --git a/clang-tools-extra/docs/ReleaseNotes.rst 
b/clang-tools-extra/docs/ReleaseNotes.rst
index d1fb1cba3e45a..50b46dc15db58 100644
--- a/clang-tools-extra/docs/ReleaseNotes.rst
+++ b/clang-tools-extra/docs/ReleaseNotes.rst
@@ -199,6 +199,11 @@ Improvements to clang-tidy
 New checks
 ^^^^^^^^^^
 
+- New :doc:`abseil-unchecked-statusor-access
+  <clang-tidy/checks/abseil/unchecked-statusor-access>` check.
+
+  Finds uses of ``absl::StatusOr`` without checking if a value is present.
+
 - New :doc:`bugprone-derived-method-shadowing-base-method
   <clang-tidy/checks/bugprone/derived-method-shadowing-base-method>` check.
 

_______________________________________________
llvm-branch-commits mailing list
[email protected]
https://lists.llvm.org/cgi-bin/mailman/listinfo/llvm-branch-commits

Reply via email to