[clang] [llvm] [FlowSensitive] [StatusOr] [5/N] Support absl::OkStatus et al (PR #163872)

Florian Mayer via llvm-commits llvm-commits at lists.llvm.org
Wed Oct 22 13:00:19 PDT 2025


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

>From 0db0df45e4c682a3a698c07a0e16b8d6ec6c9bbf Mon Sep 17 00:00:00 2001
From: Florian Mayer <fmayer at google.com>
Date: Thu, 16 Oct 2025 14:47:29 -0700
Subject: [PATCH 1/3] =?UTF-8?q?[=F0=9D=98=80=F0=9D=97=BD=F0=9D=97=BF]=20ch?=
 =?UTF-8?q?anges=20to=20main=20this=20commit=20is=20based=20on?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

Created using spr 1.3.7

[skip ci]
---
 .../Models/UncheckedStatusOrAccessModel.h     |  114 +
 .../FlowSensitive/Models/CMakeLists.txt       |    1 +
 .../Models/UncheckedStatusOrAccessModel.cpp   |  478 +++
 .../Analysis/FlowSensitive/CMakeLists.txt     |    2 +
 .../UncheckedStatusOrAccessModelTest.cpp      |   34 +
 ...ncheckedStatusOrAccessModelTestFixture.cpp | 2894 +++++++++++++++++
 .../UncheckedStatusOrAccessModelTestFixture.h |  157 +
 .../Analysis/FlowSensitive/Models/BUILD.gn    |    1 +
 .../unittests/Analysis/FlowSensitive/BUILD.gn |    2 +
 9 files changed, 3683 insertions(+)
 create mode 100644 clang/include/clang/Analysis/FlowSensitive/Models/UncheckedStatusOrAccessModel.h
 create mode 100644 clang/lib/Analysis/FlowSensitive/Models/UncheckedStatusOrAccessModel.cpp
 create mode 100644 clang/unittests/Analysis/FlowSensitive/UncheckedStatusOrAccessModelTest.cpp
 create mode 100644 clang/unittests/Analysis/FlowSensitive/UncheckedStatusOrAccessModelTestFixture.cpp
 create mode 100644 clang/unittests/Analysis/FlowSensitive/UncheckedStatusOrAccessModelTestFixture.h

diff --git a/clang/include/clang/Analysis/FlowSensitive/Models/UncheckedStatusOrAccessModel.h b/clang/include/clang/Analysis/FlowSensitive/Models/UncheckedStatusOrAccessModel.h
new file mode 100644
index 0000000000000..14f28543fc04c
--- /dev/null
+++ b/clang/include/clang/Analysis/FlowSensitive/Models/UncheckedStatusOrAccessModel.h
@@ -0,0 +1,114 @@
+//===- UncheckedStatusOrAccessModel.h -------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef CLANG_ANALYSIS_FLOWSENSITIVE_MODELS_UNCHECKEDSTATUSORACCESSMODEL_H
+#define CLANG_ANALYSIS_FLOWSENSITIVE_MODELS_UNCHECKEDSTATUSORACCESSMODEL_H
+
+#include "clang/AST/Type.h"
+#include "clang/ASTMatchers/ASTMatchers.h"
+#include "clang/Analysis/CFG.h"
+#include "clang/Analysis/FlowSensitive/CFGMatchSwitch.h"
+#include "clang/Analysis/FlowSensitive/DataflowAnalysis.h"
+#include "clang/Analysis/FlowSensitive/DataflowEnvironment.h"
+#include "clang/Analysis/FlowSensitive/MatchSwitch.h"
+#include "clang/Analysis/FlowSensitive/NoopLattice.h"
+#include "clang/Analysis/FlowSensitive/StorageLocation.h"
+#include "clang/Analysis/FlowSensitive/Value.h"
+#include "clang/Basic/SourceLocation.h"
+#include "llvm/ADT/SmallVector.h"
+#include "llvm/ADT/StringMap.h"
+#include "llvm/ADT/StringRef.h"
+
+namespace clang::dataflow::statusor_model {
+
+// The helper functions exported here are for use of downstream vendor
+// extensions of this model.
+
+// Match declaration of `absl::StatusOr<T>` and bind `T` to "T".
+clang::ast_matchers::DeclarationMatcher statusOrClass();
+// Match declaration of `absl::Status`.
+clang::ast_matchers::DeclarationMatcher statusClass();
+// Match declaration of `absl::internal_statusor::OperatorBase`.
+clang::ast_matchers::DeclarationMatcher statusOrOperatorBaseClass();
+clang::ast_matchers::TypeMatcher possiblyAliasedStatusType();
+clang::ast_matchers::TypeMatcher possiblyAliasedStatusOrType();
+clang::ast_matchers::TypeMatcher statusOrType();
+
+// Get RecordStorageLocation for the `Status` contained in the `StatusOr`
+RecordStorageLocation &locForStatus(RecordStorageLocation &StatusOrLoc);
+// Get the StorageLocation for the OK boolean in the `Status`
+StorageLocation &locForOk(RecordStorageLocation &StatusLoc);
+// Get the OK boolean in the `Status`, and initialize it if necessary.
+BoolValue &valForOk(RecordStorageLocation &StatusLoc, Environment &Env);
+// Get synthetic fields for the types modelled by
+// `UncheckedStatusOrAccessModel`.
+llvm::StringMap<QualType> getSyntheticFields(QualType Ty, QualType StatusType,
+                                             const CXXRecordDecl &RD);
+
+// Initialize the synthetic fields of the `StatusOr`.
+// N.B. if it is already initialized, the value gets reset.
+BoolValue &initializeStatusOr(RecordStorageLocation &StatusOrLoc,
+                              Environment &Env);
+// Initialize the synthetic fields of the `Status`.
+// N.B. if it is already initialized, the value gets reset.
+BoolValue &initializeStatus(RecordStorageLocation &StatusLoc, Environment &Env);
+
+bool isRecordTypeWithName(QualType Type, llvm::StringRef TypeName);
+// Return true if `Type` is instantiation of `absl::StatusOr<T>`
+bool isStatusOrType(QualType Type);
+// Return true if `Type` is `absl::Status`
+bool isStatusType(QualType Type);
+
+// Get `QualType` for `absl::Status`, or default-constructed
+// QualType if it does not exist.
+QualType findStatusType(const ASTContext &Ctx);
+
+struct UncheckedStatusOrAccessModelOptions {};
+
+// Dataflow analysis that discovers unsafe uses of StatusOr values.
+class UncheckedStatusOrAccessModel
+    : public DataflowAnalysis<UncheckedStatusOrAccessModel, NoopLattice> {
+public:
+  explicit UncheckedStatusOrAccessModel(ASTContext &Ctx, Environment &Env);
+
+  static Lattice initialElement() { return {}; }
+
+  void transfer(const CFGElement &Elt, Lattice &L, Environment &Env);
+
+private:
+  CFGMatchSwitch<TransferState<Lattice>> TransferMatchSwitch;
+};
+
+using LatticeTransferState =
+    TransferState<UncheckedStatusOrAccessModel::Lattice>;
+
+// Extend the Builder with the transfer functions for
+// `UncheckedStatusOrAccessModel`. This is useful to write downstream models
+// that extend the model.
+CFGMatchSwitch<LatticeTransferState>
+buildTransferMatchSwitch(ASTContext &Ctx,
+                         CFGMatchSwitchBuilder<LatticeTransferState> Builder);
+
+class UncheckedStatusOrAccessDiagnoser {
+public:
+  explicit UncheckedStatusOrAccessDiagnoser(
+      UncheckedStatusOrAccessModelOptions Options = {});
+
+  llvm::SmallVector<SourceLocation> operator()(
+      const CFGElement &Elt, ASTContext &Ctx,
+      const TransferStateForDiagnostics<UncheckedStatusOrAccessModel::Lattice>
+          &State);
+
+private:
+  CFGMatchSwitch<const Environment, llvm::SmallVector<SourceLocation>>
+      DiagnoseMatchSwitch;
+};
+
+} // namespace clang::dataflow::statusor_model
+
+#endif // CLANG_ANALYSIS_FLOWSENSITIVE_MODELS_UNCHECKEDSTATUSORACCESSMODEL_H
diff --git a/clang/lib/Analysis/FlowSensitive/Models/CMakeLists.txt b/clang/lib/Analysis/FlowSensitive/Models/CMakeLists.txt
index 89bbe8791eb2c..d1236f5714881 100644
--- a/clang/lib/Analysis/FlowSensitive/Models/CMakeLists.txt
+++ b/clang/lib/Analysis/FlowSensitive/Models/CMakeLists.txt
@@ -1,6 +1,7 @@
 add_clang_library(clangAnalysisFlowSensitiveModels
   ChromiumCheckModel.cpp
   UncheckedOptionalAccessModel.cpp
+  UncheckedStatusOrAccessModel.cpp
 
   LINK_LIBS
   clangAnalysis
diff --git a/clang/lib/Analysis/FlowSensitive/Models/UncheckedStatusOrAccessModel.cpp b/clang/lib/Analysis/FlowSensitive/Models/UncheckedStatusOrAccessModel.cpp
new file mode 100644
index 0000000000000..4ebf3e4251dd6
--- /dev/null
+++ b/clang/lib/Analysis/FlowSensitive/Models/UncheckedStatusOrAccessModel.cpp
@@ -0,0 +1,478 @@
+//===- UncheckedStatusOrAccessModel.cpp -----------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "clang/Analysis/FlowSensitive/Models/UncheckedStatusOrAccessModel.h"
+
+#include <cassert>
+#include <utility>
+
+#include "clang/AST/DeclCXX.h"
+#include "clang/AST/DeclTemplate.h"
+#include "clang/AST/Expr.h"
+#include "clang/AST/ExprCXX.h"
+#include "clang/AST/TypeBase.h"
+#include "clang/ASTMatchers/ASTMatchFinder.h"
+#include "clang/ASTMatchers/ASTMatchers.h"
+#include "clang/ASTMatchers/ASTMatchersInternal.h"
+#include "clang/Analysis/CFG.h"
+#include "clang/Analysis/FlowSensitive/CFGMatchSwitch.h"
+#include "clang/Analysis/FlowSensitive/DataflowAnalysis.h"
+#include "clang/Analysis/FlowSensitive/DataflowEnvironment.h"
+#include "clang/Analysis/FlowSensitive/MatchSwitch.h"
+#include "clang/Analysis/FlowSensitive/RecordOps.h"
+#include "clang/Analysis/FlowSensitive/StorageLocation.h"
+#include "clang/Analysis/FlowSensitive/Value.h"
+#include "clang/Basic/LLVM.h"
+#include "clang/Basic/SourceLocation.h"
+#include "llvm/ADT/StringMap.h"
+
+namespace clang::dataflow::statusor_model {
+namespace {
+
+using ::clang::ast_matchers::MatchFinder;
+using ::clang::ast_matchers::StatementMatcher;
+
+} // namespace
+
+static bool isStatusOrOperatorBaseType(QualType type) {
+  return isRecordTypeWithName(type, "absl::internal_statusor::OperatorBase");
+}
+
+static bool isSafeUnwrap(RecordStorageLocation *StatusOrLoc,
+                         const Environment &Env) {
+  if (!StatusOrLoc)
+    return false;
+  auto &StatusLoc = locForStatus(*StatusOrLoc);
+  auto *OkVal = Env.get<BoolValue>(locForOk(StatusLoc));
+  return OkVal != nullptr && Env.proves(OkVal->formula());
+}
+
+static ClassTemplateSpecializationDecl *
+getStatusOrBaseClass(const QualType &Ty) {
+  auto *RD = Ty->getAsCXXRecordDecl();
+  if (RD == nullptr)
+    return nullptr;
+  if (isStatusOrType(Ty) ||
+      // In case we are analyzing code under OperatorBase itself that uses
+      // operator* (e.g. to implement operator->).
+      isStatusOrOperatorBaseType(Ty))
+    return cast<ClassTemplateSpecializationDecl>(RD);
+  if (!RD->hasDefinition())
+    return nullptr;
+  for (const auto &Base : RD->bases())
+    if (auto *QT = getStatusOrBaseClass(Base.getType()))
+      return QT;
+  return nullptr;
+}
+
+static QualType getStatusOrValueType(ClassTemplateSpecializationDecl *TRD) {
+  return TRD->getTemplateArgs().get(0).getAsType();
+}
+
+static auto ofClassStatus() {
+  using namespace ::clang::ast_matchers; // NOLINT: Too many names
+  return ofClass(hasName("::absl::Status"));
+}
+
+static auto isStatusMemberCallWithName(llvm::StringRef member_name) {
+  using namespace ::clang::ast_matchers; // NOLINT: Too many names
+  return cxxMemberCallExpr(
+      on(expr(unless(cxxThisExpr()))),
+      callee(cxxMethodDecl(hasName(member_name), ofClassStatus())));
+}
+
+static auto isStatusOrMemberCallWithName(llvm::StringRef member_name) {
+  using namespace ::clang::ast_matchers; // NOLINT: Too many names
+  return cxxMemberCallExpr(
+      on(expr(unless(cxxThisExpr()))),
+      callee(cxxMethodDecl(
+          hasName(member_name),
+          ofClass(anyOf(statusOrClass(), statusOrOperatorBaseClass())))));
+}
+
+static auto isStatusOrOperatorCallWithName(llvm::StringRef operator_name) {
+  using namespace ::clang::ast_matchers; // NOLINT: Too many names
+  return cxxOperatorCallExpr(
+      hasOverloadedOperatorName(operator_name),
+      callee(cxxMethodDecl(
+          ofClass(anyOf(statusOrClass(), statusOrOperatorBaseClass())))));
+}
+
+static auto valueCall() {
+  using namespace ::clang::ast_matchers; // NOLINT: Too many names
+  return anyOf(isStatusOrMemberCallWithName("value"),
+               isStatusOrMemberCallWithName("ValueOrDie"));
+}
+
+static auto valueOperatorCall() {
+  using namespace ::clang::ast_matchers; // NOLINT: Too many names
+  return expr(anyOf(isStatusOrOperatorCallWithName("*"),
+                    isStatusOrOperatorCallWithName("->")));
+}
+
+static clang::ast_matchers::TypeMatcher statusType() {
+  using namespace ::clang::ast_matchers;  // NOLINT: Too many names
+  return hasCanonicalType(qualType(hasDeclaration(statusClass())));
+}
+
+
+static auto isComparisonOperatorCall(llvm::StringRef operator_name) {
+  using namespace ::clang::ast_matchers;  // NOLINT: Too many names
+  return cxxOperatorCallExpr(
+      hasOverloadedOperatorName(operator_name), argumentCountIs(2),
+      hasArgument(0, anyOf(hasType(statusType()), hasType(statusOrType()))),
+      hasArgument(1, anyOf(hasType(statusType()), hasType(statusOrType()))));
+}
+
+static auto
+buildDiagnoseMatchSwitch(const UncheckedStatusOrAccessModelOptions &Options) {
+  return CFGMatchSwitchBuilder<const Environment,
+                               llvm::SmallVector<SourceLocation>>()
+      // StatusOr::value, StatusOr::ValueOrDie
+      .CaseOfCFGStmt<CXXMemberCallExpr>(
+          valueCall(),
+          [](const CXXMemberCallExpr *E,
+             const ast_matchers::MatchFinder::MatchResult &,
+             const Environment &Env) {
+            if (!isSafeUnwrap(getImplicitObjectLocation(*E, Env), Env))
+              return llvm::SmallVector<SourceLocation>({E->getExprLoc()});
+            return llvm::SmallVector<SourceLocation>();
+          })
+
+      // StatusOr::operator*, StatusOr::operator->
+      .CaseOfCFGStmt<CXXOperatorCallExpr>(
+          valueOperatorCall(),
+          [](const CXXOperatorCallExpr *E,
+             const ast_matchers::MatchFinder::MatchResult &,
+             const Environment &Env) {
+            RecordStorageLocation *StatusOrLoc =
+                Env.get<RecordStorageLocation>(*E->getArg(0));
+            if (!isSafeUnwrap(StatusOrLoc, Env))
+              return llvm::SmallVector<SourceLocation>({E->getOperatorLoc()});
+            return llvm::SmallVector<SourceLocation>();
+          })
+      .Build();
+}
+
+UncheckedStatusOrAccessDiagnoser::UncheckedStatusOrAccessDiagnoser(
+    UncheckedStatusOrAccessModelOptions Options)
+    : DiagnoseMatchSwitch(buildDiagnoseMatchSwitch(Options)) {}
+
+llvm::SmallVector<SourceLocation> UncheckedStatusOrAccessDiagnoser::operator()(
+    const CFGElement &Elt, ASTContext &Ctx,
+    const TransferStateForDiagnostics<UncheckedStatusOrAccessModel::Lattice>
+        &State) {
+  return DiagnoseMatchSwitch(Elt, Ctx, State.Env);
+}
+
+BoolValue &initializeStatus(RecordStorageLocation &StatusLoc,
+                            Environment &Env) {
+  auto &OkVal = Env.makeAtomicBoolValue();
+  Env.setValue(locForOk(StatusLoc), OkVal);
+  return OkVal;
+}
+
+BoolValue &initializeStatusOr(RecordStorageLocation &StatusOrLoc,
+                              Environment &Env) {
+  return initializeStatus(locForStatus(StatusOrLoc), Env);
+}
+
+clang::ast_matchers::DeclarationMatcher statusOrClass() {
+  using namespace ::clang::ast_matchers; // NOLINT: Too many names
+  return classTemplateSpecializationDecl(
+      hasName("absl::StatusOr"),
+      hasTemplateArgument(0, refersToType(type().bind("T"))));
+}
+
+clang::ast_matchers::DeclarationMatcher statusClass() {
+  using namespace ::clang::ast_matchers; // NOLINT: Too many names
+  return cxxRecordDecl(hasName("absl::Status"));
+}
+
+clang::ast_matchers::DeclarationMatcher statusOrOperatorBaseClass() {
+  using namespace ::clang::ast_matchers; // NOLINT: Too many names
+  return classTemplateSpecializationDecl(
+      hasName("absl::internal_statusor::OperatorBase"));
+}
+
+clang::ast_matchers::TypeMatcher possiblyAliasedStatusOrType() {
+  using namespace ::clang::ast_matchers; // NOLINT: Too many names
+  return hasUnqualifiedDesugaredType(
+      recordType(hasDeclaration(statusOrClass())));
+}
+
+clang::ast_matchers::TypeMatcher possiblyAliasedStatusType() {
+  using namespace ::clang::ast_matchers; // NOLINT: Too many names
+  return hasUnqualifiedDesugaredType(recordType(hasDeclaration(statusClass())));
+}
+
+clang::ast_matchers::TypeMatcher statusOrType() {
+  using namespace ::clang::ast_matchers; // NOLINT: Too many names
+  return hasCanonicalType(qualType(hasDeclaration(statusOrClass())));
+}
+
+bool isRecordTypeWithName(QualType Type, llvm::StringRef TypeName) {
+  return Type->isRecordType() &&
+         Type->getAsCXXRecordDecl()->getQualifiedNameAsString() == TypeName;
+}
+
+bool isStatusOrType(QualType Type) {
+  return isRecordTypeWithName(Type, "absl::StatusOr");
+}
+
+bool isStatusType(QualType Type) {
+  return isRecordTypeWithName(Type, "absl::Status");
+}
+
+llvm::StringMap<QualType> getSyntheticFields(QualType Ty, QualType StatusType,
+                                             const CXXRecordDecl &RD) {
+  if (auto *TRD = getStatusOrBaseClass(Ty))
+    return {{"status", StatusType}, {"value", getStatusOrValueType(TRD)}};
+  if (isStatusType(Ty) || (RD.hasDefinition() &&
+                           RD.isDerivedFrom(StatusType->getAsCXXRecordDecl())))
+    return {{"ok", RD.getASTContext().BoolTy}};
+  return {};
+}
+
+RecordStorageLocation &locForStatus(RecordStorageLocation &StatusOrLoc) {
+  return cast<RecordStorageLocation>(StatusOrLoc.getSyntheticField("status"));
+}
+
+StorageLocation &locForOk(RecordStorageLocation &StatusLoc) {
+  return StatusLoc.getSyntheticField("ok");
+}
+
+BoolValue &valForOk(RecordStorageLocation &StatusLoc, Environment &Env) {
+  if (auto *Val = Env.get<BoolValue>(locForOk(StatusLoc)))
+    return *Val;
+  return initializeStatus(StatusLoc, Env);
+}
+
+static void transferStatusOrOkCall(const CXXMemberCallExpr *Expr,
+                                   const MatchFinder::MatchResult &,
+                                   LatticeTransferState &State) {
+  RecordStorageLocation *StatusOrLoc =
+      getImplicitObjectLocation(*Expr, State.Env);
+  if (StatusOrLoc == nullptr)
+    return;
+
+  auto &OkVal = valForOk(locForStatus(*StatusOrLoc), State.Env);
+  State.Env.setValue(*Expr, OkVal);
+}
+
+static void transferStatusCall(const CXXMemberCallExpr *Expr,
+                               const MatchFinder::MatchResult &,
+                               LatticeTransferState &State) {
+  RecordStorageLocation *StatusOrLoc =
+      getImplicitObjectLocation(*Expr, State.Env);
+  if (StatusOrLoc == nullptr)
+    return;
+
+  RecordStorageLocation &StatusLoc = locForStatus(*StatusOrLoc);
+
+  if (State.Env.getValue(locForOk(StatusLoc)) == nullptr)
+    initializeStatusOr(*StatusOrLoc, State.Env);
+
+  if (Expr->isPRValue())
+    copyRecord(StatusLoc, State.Env.getResultObjectLocation(*Expr), State.Env);
+  else
+    State.Env.setStorageLocation(*Expr, StatusLoc);
+}
+
+static void transferStatusOkCall(const CXXMemberCallExpr *Expr,
+                                 const MatchFinder::MatchResult &,
+                                 LatticeTransferState &State) {
+  RecordStorageLocation *StatusLoc =
+      getImplicitObjectLocation(*Expr, State.Env);
+  if (StatusLoc == nullptr)
+    return;
+
+  if (Value *Val = State.Env.getValue(locForOk(*StatusLoc)))
+    State.Env.setValue(*Expr, *Val);
+}
+
+static void transferStatusUpdateCall(const CXXMemberCallExpr *Expr,
+                                     const MatchFinder::MatchResult &,
+                                     LatticeTransferState &State) {
+  assert(Expr->getNumArgs() == 1);
+  auto *Arg = Expr->getArg(0);
+  RecordStorageLocation *ArgRecord =
+      Arg->isPRValue() ? &State.Env.getResultObjectLocation(*Arg)
+                       : State.Env.get<RecordStorageLocation>(*Arg);
+  RecordStorageLocation *ThisLoc = getImplicitObjectLocation(*Expr, State.Env);
+  if (ThisLoc == nullptr || ArgRecord == nullptr)
+    return;
+
+  auto &ThisOkVal = valForOk(*ThisLoc, State.Env);
+  auto &ArgOkVal = valForOk(*ArgRecord, State.Env);
+  auto &A = State.Env.arena();
+  auto &NewVal = State.Env.makeAtomicBoolValue();
+  State.Env.assume(A.makeImplies(A.makeNot(ThisOkVal.formula()),
+                                 A.makeNot(NewVal.formula())));
+  State.Env.assume(A.makeImplies(NewVal.formula(), ArgOkVal.formula()));
+  State.Env.setValue(locForOk(*ThisLoc), NewVal);
+}
+
+static BoolValue* evaluateStatusEquality(RecordStorageLocation& LhsStatusLoc,
+                                         RecordStorageLocation& RhsStatusLoc,
+                                         Environment& Env) {
+  auto& A = Env.arena();
+  // Logically, a Status object is composed of an error code that could take one
+  // of multiple possible values, including the "ok" value. We track whether a
+  // Status object has an "ok" value and represent this as an `ok` bit. Equality
+  // of Status objects compares their error codes. Therefore, merely comparing
+  // the `ok` bits isn't sufficient: when two Status objects are assigned non-ok
+  // error codes the equality of their respective error codes matters. Since we
+  // only track the `ok` bits, we can't make any conclusions about equality when
+  // we know that two Status objects have non-ok values.
+
+  auto& LhsOkVal = valForOk(LhsStatusLoc, Env);
+  auto& RhsOkVal = valForOk(RhsStatusLoc, Env);
+
+  auto& Res = Env.makeAtomicBoolValue();
+
+  // lhs && rhs => res (a.k.a. !res => !lhs || !rhs)
+  Env.assume(A.makeImplies(A.makeAnd(LhsOkVal.formula(), RhsOkVal.formula()),
+                           Res.formula()));
+  // res => (lhs == rhs)
+  Env.assume(A.makeImplies(
+      Res.formula(), A.makeEquals(LhsOkVal.formula(), RhsOkVal.formula())));
+
+  return &Res;
+}
+
+static BoolValue* evaluateStatusOrEquality(
+    RecordStorageLocation& LhsStatusOrLoc,
+    RecordStorageLocation& RhsStatusOrLoc, Environment& Env) {
+  auto& A = Env.arena();
+  // Logically, a StatusOr<T> object is composed of two values - a Status and a
+  // value of type T. Equality of StatusOr objects compares both values.
+  // Therefore, merely comparing the `ok` bits of the Status values isn't
+  // sufficient. When two StatusOr objects are engaged, the equality of their
+  // respective values of type T matters. Similarly, when two StatusOr objects
+  // have Status values that have non-ok error codes, the equality of the error
+  // codes matters. Since we only track the `ok` bits of the Status values, we
+  // can't make any conclusions about equality when we know that two StatusOr
+  // objects are engaged or when their Status values contain non-ok error codes.
+  auto& LhsOkVal = valForOk(locForStatus(LhsStatusOrLoc), Env);
+  auto& RhsOkVal = valForOk(locForStatus(RhsStatusOrLoc), Env);
+  auto& res = Env.makeAtomicBoolValue();
+
+  // res => (lhs == rhs)
+  Env.assume(A.makeImplies(
+      res.formula(), A.makeEquals(LhsOkVal.formula(), RhsOkVal.formula())));
+  return &res;
+}
+
+
+static BoolValue* evaluateEquality(const Expr* LhsExpr, const Expr* RhsExpr,
+                                   Environment& Env) {
+  // Check the type of both sides in case an operator== is added that admits
+  // different types.
+  if (isStatusOrType(LhsExpr->getType()) &&
+      isStatusOrType(RhsExpr->getType())) {
+    auto* LhsStatusOrLoc = Env.get<RecordStorageLocation>(*LhsExpr);
+    if (LhsStatusOrLoc == nullptr) return nullptr;
+    auto* RhsStatusOrLoc = Env.get<RecordStorageLocation>(*RhsExpr);
+    if (RhsStatusOrLoc == nullptr) return nullptr;
+
+    return evaluateStatusOrEquality(*LhsStatusOrLoc, *RhsStatusOrLoc, Env);
+
+    // Check the type of both sides in case an operator== is added that admits
+    // different types.
+  }
+  if (isStatusType(LhsExpr->getType()) && isStatusType(RhsExpr->getType())) {
+    auto* LhsStatusLoc = Env.get<RecordStorageLocation>(*LhsExpr);
+    if (LhsStatusLoc == nullptr) return nullptr;
+
+    auto* RhsStatusLoc = Env.get<RecordStorageLocation>(*RhsExpr);
+    if (RhsStatusLoc == nullptr) return nullptr;
+
+    return evaluateStatusEquality(*LhsStatusLoc, *RhsStatusLoc, Env);
+  }
+  return nullptr;
+}
+
+static void transferComparisonOperator(const CXXOperatorCallExpr* Expr,
+                                       LatticeTransferState& State,
+                                       bool IsNegative) {
+  auto* LhsAndRhsVal =
+      evaluateEquality(Expr->getArg(0), Expr->getArg(1), State.Env);
+  if (LhsAndRhsVal == nullptr) return;
+
+  if (IsNegative)
+    State.Env.setValue(*Expr, State.Env.makeNot(*LhsAndRhsVal));
+  else
+    State.Env.setValue(*Expr, *LhsAndRhsVal);
+}
+
+
+CFGMatchSwitch<LatticeTransferState>
+buildTransferMatchSwitch(ASTContext &Ctx,
+                         CFGMatchSwitchBuilder<LatticeTransferState> Builder) {
+  using namespace ::clang::ast_matchers; // NOLINT: Too many names
+  return std::move(Builder)
+      .CaseOfCFGStmt<CXXMemberCallExpr>(isStatusOrMemberCallWithName("ok"),
+                                        transferStatusOrOkCall)
+      .CaseOfCFGStmt<CXXMemberCallExpr>(isStatusOrMemberCallWithName("status"),
+                                        transferStatusCall)
+      .CaseOfCFGStmt<CXXMemberCallExpr>(isStatusMemberCallWithName("ok"),
+                                        transferStatusOkCall)
+      .CaseOfCFGStmt<CXXMemberCallExpr>(isStatusMemberCallWithName("Update"),
+                                        transferStatusUpdateCall)
+      .CaseOfCFGStmt<CXXOperatorCallExpr>(
+          isComparisonOperatorCall("=="),
+          [](const CXXOperatorCallExpr* Expr, const MatchFinder::MatchResult&,
+             LatticeTransferState& State) {
+            transferComparisonOperator(Expr, State,
+                                       /*IsNegative=*/false);
+          })
+      .CaseOfCFGStmt<CXXOperatorCallExpr>(
+          isComparisonOperatorCall("!="),
+          [](const CXXOperatorCallExpr* Expr, const MatchFinder::MatchResult&,
+             LatticeTransferState& State) {
+            transferComparisonOperator(Expr, State,
+                                       /*IsNegative=*/true);
+          })
+      .Build();
+}
+
+QualType findStatusType(const ASTContext &Ctx) {
+  for (Type *Ty : Ctx.getTypes())
+    if (isStatusType(QualType(Ty, 0)))
+      return QualType(Ty, 0);
+
+  return QualType();
+}
+
+UncheckedStatusOrAccessModel::UncheckedStatusOrAccessModel(ASTContext &Ctx,
+                                                           Environment &Env)
+    : DataflowAnalysis<UncheckedStatusOrAccessModel,
+                       UncheckedStatusOrAccessModel::Lattice>(Ctx),
+      TransferMatchSwitch(buildTransferMatchSwitch(Ctx, {})) {
+  QualType StatusType = findStatusType(Ctx);
+  Env.getDataflowAnalysisContext().setSyntheticFieldCallback(
+      [StatusType](QualType Ty) -> llvm::StringMap<QualType> {
+        CXXRecordDecl *RD = Ty->getAsCXXRecordDecl();
+        if (RD == nullptr)
+          return {};
+
+        if (auto Fields = getSyntheticFields(Ty, StatusType, *RD);
+            !Fields.empty())
+          return Fields;
+        return {};
+      });
+}
+
+void UncheckedStatusOrAccessModel::transfer(const CFGElement &Elt, Lattice &L,
+                                            Environment &Env) {
+  LatticeTransferState State(L, Env);
+  TransferMatchSwitch(Elt, getASTContext(), State);
+}
+
+} // namespace clang::dataflow::statusor_model
diff --git a/clang/unittests/Analysis/FlowSensitive/CMakeLists.txt b/clang/unittests/Analysis/FlowSensitive/CMakeLists.txt
index 35082387b46e9..1d932ec6e8a96 100644
--- a/clang/unittests/Analysis/FlowSensitive/CMakeLists.txt
+++ b/clang/unittests/Analysis/FlowSensitive/CMakeLists.txt
@@ -25,6 +25,8 @@ add_clang_unittest(ClangAnalysisFlowSensitiveTests
   TransferTest.cpp
   TypeErasedDataflowAnalysisTest.cpp
   UncheckedOptionalAccessModelTest.cpp
+  UncheckedStatusOrAccessModelTest.cpp
+  UncheckedStatusOrAccessModelTestFixture.cpp
   ValueTest.cpp
   WatchedLiteralsSolverTest.cpp
   CLANG_LIBS
diff --git a/clang/unittests/Analysis/FlowSensitive/UncheckedStatusOrAccessModelTest.cpp b/clang/unittests/Analysis/FlowSensitive/UncheckedStatusOrAccessModelTest.cpp
new file mode 100644
index 0000000000000..07d3f2412e842
--- /dev/null
+++ b/clang/unittests/Analysis/FlowSensitive/UncheckedStatusOrAccessModelTest.cpp
@@ -0,0 +1,34 @@
+//===- UncheckedStatusOrAccessModelTest.cpp -------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include <utility>
+
+#include "UncheckedStatusOrAccessModelTestFixture.h"
+#include "clang/Analysis/FlowSensitive/Models/UncheckedStatusOrAccessModel.h"
+#include "gtest/gtest.h"
+
+namespace clang::dataflow::statusor_model {
+namespace {
+
+INSTANTIATE_TEST_SUITE_P(
+    UncheckedStatusOrAccessModelTest, UncheckedStatusOrAccessModelTest,
+    testing::Values(
+        std::make_pair(new UncheckedStatusOrAccessModelTestExecutor<
+                           UncheckedStatusOrAccessModel>(),
+                       UncheckedStatusOrAccessModelTestAliasKind::kUnaliased),
+        std::make_pair(
+            new UncheckedStatusOrAccessModelTestExecutor<
+                UncheckedStatusOrAccessModel>(),
+            UncheckedStatusOrAccessModelTestAliasKind::kPartiallyAliased),
+        std::make_pair(
+            new UncheckedStatusOrAccessModelTestExecutor<
+                UncheckedStatusOrAccessModel>(),
+            UncheckedStatusOrAccessModelTestAliasKind::kFullyAliased)));
+} // namespace
+
+} // namespace clang::dataflow::statusor_model
diff --git a/clang/unittests/Analysis/FlowSensitive/UncheckedStatusOrAccessModelTestFixture.cpp b/clang/unittests/Analysis/FlowSensitive/UncheckedStatusOrAccessModelTestFixture.cpp
new file mode 100644
index 0000000000000..99f04cc8fe7e7
--- /dev/null
+++ b/clang/unittests/Analysis/FlowSensitive/UncheckedStatusOrAccessModelTestFixture.cpp
@@ -0,0 +1,2894 @@
+//===- UncheckedStatusOrAccessModelTestFixture.cpp ------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "UncheckedStatusOrAccessModelTestFixture.h"
+#include "MockHeaders.h"
+#include "llvm/Support/ErrorHandling.h"
+
+#include <string>
+#include <utility>
+#include <vector>
+
+#include "gtest/gtest.h"
+
+namespace clang::dataflow::statusor_model {
+namespace {
+
+TEST_P(UncheckedStatusOrAccessModelTest, NoStatusOrMention) {
+  ExpectDiagnosticsFor(R"cc(
+    void target() { "nop"; }
+  )cc");
+}
+
+TEST_P(UncheckedStatusOrAccessModelTest,
+       UnwrapWithoutCheck_Lvalue_CallToValue) {
+  ExpectDiagnosticsFor(R"cc(
+#include "unchecked_statusor_access_test_defs.h"
+
+    void target(STATUSOR_INT sor) {
+      sor.value();  // [[unsafe]]
+    }
+  )cc");
+}
+
+TEST_P(UncheckedStatusOrAccessModelTest, NonExplicitInitialization) {
+  ExpectDiagnosticsFor(R"cc(
+#include "unchecked_statusor_access_test_defs.h"
+    STATUSOR_INT target() {
+      STATUSOR_INT x = Make<STATUSOR_INT>();
+      return x.value();  // [[unsafe]]
+    }
+  )cc");
+}
+
+TEST_P(UncheckedStatusOrAccessModelTest,
+       UnwrapWithoutCheck_Lvalue_CallToValue_NewLine) {
+  ExpectDiagnosticsFor(R"cc(
+#include "unchecked_statusor_access_test_defs.h"
+
+    void target(STATUSOR_INT sor) {
+      sor.  // force newline
+      value();  // [[unsafe]]
+    }
+  )cc");
+}
+
+TEST_P(UncheckedStatusOrAccessModelTest,
+       UnwrapWithoutCheck_Rvalue_CallToValue) {
+  ExpectDiagnosticsFor(R"cc(
+#include "unchecked_statusor_access_test_defs.h"
+
+    void target(STATUSOR_INT sor) {
+      std::move(sor).value();  // [[unsafe]]
+    }
+  )cc");
+}
+
+TEST_P(UncheckedStatusOrAccessModelTest,
+       UnwrapWithoutCheck_Lvalue_CallToValueOrDie) {
+  ExpectDiagnosticsFor(R"cc(
+#include "unchecked_statusor_access_test_defs.h"
+
+    void target(STATUSOR_INT sor) {
+      sor.ValueOrDie();  // [[unsafe]]
+    }
+  )cc");
+}
+
+TEST_P(UncheckedStatusOrAccessModelTest,
+       UnwrapWithoutCheck_Rvalue_CallToValueOrDie) {
+  ExpectDiagnosticsFor(R"cc(
+#include "unchecked_statusor_access_test_defs.h"
+
+    void target(STATUSOR_INT sor) {
+      std::move(sor).ValueOrDie();  // [[unsafe]]
+    }
+  )cc");
+}
+
+TEST_P(UncheckedStatusOrAccessModelTest,
+       UnwrapWithoutCheck_Lvalue_CallToOperatorStar) {
+  ExpectDiagnosticsFor(R"cc(
+#include "unchecked_statusor_access_test_defs.h"
+
+    void target(STATUSOR_INT sor) {
+      *sor;  // [[unsafe]]
+    }
+  )cc");
+}
+
+TEST_P(UncheckedStatusOrAccessModelTest,
+       UnwrapWithoutCheck_Lvalue_CallToOperatorStarSeparateLine) {
+  ExpectDiagnosticsFor(R"cc(
+#include "unchecked_statusor_access_test_defs.h"
+
+    void target(STATUSOR_INT sor) {
+      *  // [[unsafe]]
+          sor;
+    }
+  )cc");
+}
+
+TEST_P(UncheckedStatusOrAccessModelTest,
+       UnwrapWithoutCheck_Rvalue_CallToOperatorStar) {
+  ExpectDiagnosticsFor(R"cc(
+#include "unchecked_statusor_access_test_defs.h"
+
+    void target(STATUSOR_INT sor) {
+      *std::move(sor);  // [[unsafe]]
+    }
+  )cc");
+}
+
+TEST_P(UncheckedStatusOrAccessModelTest,
+       UnwrapWithoutCheck_Lvalue_CallToOperatorArrow) {
+  ExpectDiagnosticsFor(R"cc(
+#include "unchecked_statusor_access_test_defs.h"
+
+    struct Foo {
+      void foo();
+    };
+
+    void target(absl::StatusOr<Foo> sor) {
+      sor->foo();  // [[unsafe]]
+    }
+  )cc");
+}
+
+TEST_P(UncheckedStatusOrAccessModelTest,
+       UnwrapWithoutCheck_Rvalue_CallToOperatorArrow) {
+  ExpectDiagnosticsFor(R"cc(
+#include "unchecked_statusor_access_test_defs.h"
+
+    struct Foo {
+      void foo();
+    };
+
+    void target(absl::StatusOr<Foo> sor) {
+      std::move(sor)->foo();  // [[unsafe]]
+    }
+  )cc");
+}
+
+TEST_P(UncheckedStatusOrAccessModelTest, UnwrapRvalueWithCheck) {
+  ExpectDiagnosticsFor(R"cc(
+#include "unchecked_statusor_access_test_defs.h"
+
+    void target(STATUSOR_INT sor) {
+      if (sor.ok()) std::move(sor).value();
+    }
+  )cc");
+}
+
+TEST_P(UncheckedStatusOrAccessModelTest, ParensInDeclInitExpr) {
+  ExpectDiagnosticsFor(R"cc(
+#include "unchecked_statusor_access_test_defs.h"
+
+    void target() {
+      auto sor = (Make<STATUSOR_INT>());
+      if (sor.ok()) sor.value();
+    }
+  )cc");
+}
+
+TEST_P(UncheckedStatusOrAccessModelTest, ReferenceInDeclInitExpr) {
+  ExpectDiagnosticsFor(R"cc(
+#include "unchecked_statusor_access_test_defs.h"
+
+    struct Foo {
+      const STATUSOR_INT& GetStatusOrInt() const;
+    };
+
+    void target(Foo foo) {
+      auto sor = foo.GetStatusOrInt();
+      if (sor.ok()) sor.value();
+    }
+  )cc");
+  ExpectDiagnosticsFor(R"cc(
+#include "unchecked_statusor_access_test_defs.h"
+
+    struct Foo {
+      STATUSOR_INT& GetStatusOrInt();
+    };
+
+    void target(Foo foo) {
+      auto sor = foo.GetStatusOrInt();
+      if (sor.ok()) sor.value();
+    }
+  )cc");
+  ExpectDiagnosticsFor(R"cc(
+#include "unchecked_statusor_access_test_defs.h"
+
+    struct Foo {
+      STATUSOR_INT&& GetStatusOrInt() &&;
+    };
+
+    void target(Foo foo) {
+      auto sor = std::move(foo).GetStatusOrInt();
+      if (sor.ok()) sor.value();
+    }
+  )cc");
+}
+
+TEST_P(UncheckedStatusOrAccessModelTest, IfThenElse) {
+  ExpectDiagnosticsFor(
+      R"cc(
+#include "unchecked_statusor_access_test_defs.h"
+
+        void target(STATUSOR_INT sor) {
+          if (sor.ok())
+            sor.value();
+          else
+            sor.value();  // [[unsafe]]
+
+          sor.value();  // [[unsafe]]
+        }
+      )cc");
+  ExpectDiagnosticsFor(R"cc(
+#include "unchecked_statusor_access_test_defs.h"
+
+    void target() {
+      if (auto sor = Make<STATUSOR_INT>(); sor.ok())
+        sor.value();
+      else
+        sor.value();  // [[unsafe]]
+    }
+  )cc");
+}
+
+TEST_P(UncheckedStatusOrAccessModelTest, JoinSafeSafe) {
+  ExpectDiagnosticsFor(
+      R"cc(
+#include "unchecked_statusor_access_test_defs.h"
+
+        void target(STATUSOR_INT sor, bool b) {
+          if (sor.ok()) {
+            if (b)
+              sor.value();
+            else
+              sor.value();
+          }
+        }
+      )cc");
+}
+
+TEST_P(UncheckedStatusOrAccessModelTest, JoinUnsafeUnsafe) {
+  ExpectDiagnosticsFor(R"cc(
+#include "unchecked_statusor_access_test_defs.h"
+
+    void target(STATUSOR_INT sor, bool b) {
+      if (b)
+        sor.value();  // [[unsafe]]
+      else
+        sor.value();  // [[unsafe]]
+    }
+  )cc");
+}
+
+TEST_P(UncheckedStatusOrAccessModelTest, InversedIfThenElse) {
+  ExpectDiagnosticsFor(
+      R"cc(
+#include "unchecked_statusor_access_test_defs.h"
+
+        void target(STATUSOR_INT sor) {
+          if (!sor.ok())
+            sor.value();  // [[unsafe]]
+          else
+            sor.value();
+
+          sor.value();  // [[unsafe]]
+        }
+      )cc");
+}
+
+TEST_P(UncheckedStatusOrAccessModelTest, DoubleInversedIfThenElse) {
+  ExpectDiagnosticsFor(R"cc(
+#include "unchecked_statusor_access_test_defs.h"
+
+    void target(STATUSOR_INT sor) {
+      if (!!sor.ok())
+        sor.value();
+      else
+        sor.value();  // [[unsafe]]
+    }
+  )cc");
+}
+
+TEST_P(UncheckedStatusOrAccessModelTest, TripleInversedIfThenElse) {
+  ExpectDiagnosticsFor(R"cc(
+#include "unchecked_statusor_access_test_defs.h"
+
+    void target(STATUSOR_INT sor) {
+      if (!!!sor.ok())
+        sor.value();  // [[unsafe]]
+      else
+        sor.value();
+    }
+  )cc");
+}
+
+TEST_P(UncheckedStatusOrAccessModelTest, IfThenElse_LhsAndRhs) {
+  ExpectDiagnosticsFor(R"cc(
+#include "unchecked_statusor_access_test_defs.h"
+
+    void target(STATUSOR_INT x, STATUSOR_INT y) {
+      if (x.ok() && y.ok()) {
+        x.value();
+
+        y.value();
+      } else {
+        x.value();  // [[unsafe]]
+
+        y.value();  // [[unsafe]]
+      }
+    }
+  )cc");
+}
+
+TEST_P(UncheckedStatusOrAccessModelTest, IfThenElse_NotLhsAndRhs) {
+  ExpectDiagnosticsFor(
+      R"cc(
+#include "unchecked_statusor_access_test_defs.h"
+
+        void target(STATUSOR_INT x, STATUSOR_INT y) {
+          if (!x.ok() && y.ok()) {
+            y.value();
+
+            x.value();  // [[unsafe]]
+          } else {
+            x.value();  // [[unsafe]]
+
+            y.value();  // [[unsafe]]
+          }
+        }
+      )cc");
+}
+
+TEST_P(UncheckedStatusOrAccessModelTest, IfThenElse_LhsAndNotRhs) {
+  ExpectDiagnosticsFor(
+      R"cc(
+#include "unchecked_statusor_access_test_defs.h"
+
+        void target(STATUSOR_INT x, STATUSOR_INT y) {
+          if (x.ok() && !y.ok()) {
+            x.value();
+
+            y.value();  // [[unsafe]]
+          } else {
+            x.value();  // [[unsafe]]
+
+            y.value();  // [[unsafe]]
+          }
+        }
+      )cc");
+}
+
+TEST_P(UncheckedStatusOrAccessModelTest, IfThenElse_NotLhsAndNotRhs) {
+  ExpectDiagnosticsFor(R"cc(
+#include "unchecked_statusor_access_test_defs.h"
+
+    void target(STATUSOR_INT x, STATUSOR_INT y) {
+      if (!x.ok() && !y.ok()) {
+        x.value();  // [[unsafe]]
+
+        y.value();  // [[unsafe]]
+      } else {
+        x.value();  // [[unsafe]]
+
+        y.value();  // [[unsafe]]
+      }
+    }
+  )cc");
+}
+
+TEST_P(UncheckedStatusOrAccessModelTest, IfThenElse_Not_LhsAndRhs) {
+  ExpectDiagnosticsFor(
+      R"cc(
+#include "unchecked_statusor_access_test_defs.h"
+
+        void target(STATUSOR_INT x, STATUSOR_INT y) {
+          if (!(x.ok() && y.ok())) {
+            x.value();  // [[unsafe]]
+
+            y.value();  // [[unsafe]]
+          } else {
+            x.value();
+
+            y.value();
+          }
+        }
+      )cc");
+}
+
+TEST_P(UncheckedStatusOrAccessModelTest, IfThenElse_Not_NotLhsAndRhs) {
+  ExpectDiagnosticsFor(
+      R"cc(
+#include "unchecked_statusor_access_test_defs.h"
+
+        void target(STATUSOR_INT x, STATUSOR_INT y) {
+          if (!(!x.ok() && y.ok())) {
+            x.value();  // [[unsafe]]
+
+            y.value();  // [[unsafe]]
+          } else {
+            y.value();
+
+            x.value();  // [[unsafe]]
+          }
+        }
+      )cc");
+}
+
+TEST_P(UncheckedStatusOrAccessModelTest, IfThenElse_Not_LhsAndNotRhs) {
+  ExpectDiagnosticsFor(
+      R"cc(
+#include "unchecked_statusor_access_test_defs.h"
+
+        void target(STATUSOR_INT x, STATUSOR_INT y) {
+          if (!(x.ok() && !y.ok())) {
+            x.value();  // [[unsafe]]
+
+            y.value();  // [[unsafe]]
+          } else {
+            x.value();
+
+            y.value();  // [[unsafe]]
+          }
+        }
+      )cc");
+}
+
+TEST_P(UncheckedStatusOrAccessModelTest, IfThenElse_Not_NotLhsAndNotRhs) {
+  ExpectDiagnosticsFor(R"cc(
+#include "unchecked_statusor_access_test_defs.h"
+
+    void target(STATUSOR_INT x, STATUSOR_INT y) {
+      if (!(!x.ok() && !y.ok())) {
+        x.value();  // [[unsafe]]
+
+        y.value();  // [[unsafe]]
+      } else {
+        x.value();  // [[unsafe]]
+
+        y.value();  // [[unsafe]]
+      }
+    }
+  )cc");
+}
+
+TEST_P(UncheckedStatusOrAccessModelTest, IfThenElse_LhsOrRhs) {
+  ExpectDiagnosticsFor(R"cc(
+#include "unchecked_statusor_access_test_defs.h"
+
+    void target(STATUSOR_INT x, STATUSOR_INT y) {
+      if (x.ok() || y.ok()) {
+        x.value();  // [[unsafe]]
+
+        y.value();  // [[unsafe]]
+      } else {
+        x.value();  // [[unsafe]]
+
+        y.value();  // [[unsafe]]
+      }
+    }
+  )cc");
+}
+
+TEST_P(UncheckedStatusOrAccessModelTest, IfThenElse_NotLhsOrRhs) {
+  ExpectDiagnosticsFor(
+      R"cc(
+#include "unchecked_statusor_access_test_defs.h"
+
+        void target(STATUSOR_INT x, STATUSOR_INT y) {
+          if (!x.ok() || y.ok()) {
+            x.value();  // [[unsafe]]
+
+            y.value();  // [[unsafe]]
+          } else {
+            x.value();
+
+            y.value();  // [[unsafe]]
+          }
+        }
+      )cc");
+}
+
+TEST_P(UncheckedStatusOrAccessModelTest, IfThenElse_LhsOrNotRhs) {
+  ExpectDiagnosticsFor(
+      R"cc(
+#include "unchecked_statusor_access_test_defs.h"
+
+        void target(STATUSOR_INT x, STATUSOR_INT y) {
+          if (x.ok() || !y.ok()) {
+            x.value();  // [[unsafe]]
+
+            y.value();  // [[unsafe]]
+          } else {
+            y.value();
+
+            x.value();  // [[unsafe]]
+          }
+        }
+      )cc");
+}
+
+TEST_P(UncheckedStatusOrAccessModelTest, IfThenElse_NotLhsOrNotRhs) {
+  ExpectDiagnosticsFor(
+      R"cc(
+#include "unchecked_statusor_access_test_defs.h"
+
+        void target(STATUSOR_INT x, STATUSOR_INT y) {
+          if (!x.ok() || !y.ok()) {
+            x.value();  // [[unsafe]]
+
+            y.value();  // [[unsafe]]
+          } else {
+            x.value();
+
+            y.value();
+          }
+        }
+      )cc");
+}
+
+TEST_P(UncheckedStatusOrAccessModelTest, IfThenElse_Not_LhsOrRhs) {
+  ExpectDiagnosticsFor(R"cc(
+#include "unchecked_statusor_access_test_defs.h"
+
+    void target(STATUSOR_INT x, STATUSOR_INT y) {
+      if (!(x.ok() || y.ok())) {
+        x.value();  // [[unsafe]]
+
+        y.value();  // [[unsafe]]
+      } else {
+        x.value();  // [[unsafe]]
+
+        y.value();  // [[unsafe]]
+      }
+    }
+  )cc");
+}
+
+TEST_P(UncheckedStatusOrAccessModelTest, IfThenElse_Not_NotLhsOrRhs) {
+  ExpectDiagnosticsFor(
+      R"cc(
+#include "unchecked_statusor_access_test_defs.h"
+
+        void target(STATUSOR_INT x, STATUSOR_INT y) {
+          if (!(!x.ok() || y.ok())) {
+            x.value();
+
+            y.value();  // [[unsafe]]
+          } else {
+            x.value();  // [[unsafe]]
+
+            y.value();  // [[unsafe]]
+          }
+        }
+      )cc");
+}
+
+TEST_P(UncheckedStatusOrAccessModelTest, IfThenElse_Not_LhsOrNotRhs) {
+  ExpectDiagnosticsFor(
+      R"cc(
+#include "unchecked_statusor_access_test_defs.h"
+
+        void target(STATUSOR_INT x, STATUSOR_INT y) {
+          if (!(x.ok() || !y.ok())) {
+            y.value();
+
+            x.value();  // [[unsafe]]
+          } else {
+            x.value();  // [[unsafe]]
+
+            y.value();  // [[unsafe]]
+          }
+        }
+      )cc");
+}
+
+TEST_P(UncheckedStatusOrAccessModelTest, IfThenElse_Not_NotLhsOrNotRhs) {
+  ExpectDiagnosticsFor(R"cc(
+#include "unchecked_statusor_access_test_defs.h"
+
+    void target(STATUSOR_INT x, STATUSOR_INT y) {
+      if (!(!x.ok() || !y.ok())) {
+        x.value();
+
+        y.value();
+      } else {
+        x.value();  // [[unsafe]]
+
+        y.value();  // [[unsafe]]
+      }
+    }
+  )cc");
+}
+
+TEST_P(UncheckedStatusOrAccessModelTest, TerminatingIfThenBranch) {
+  ExpectDiagnosticsFor(R"cc(
+#include "unchecked_statusor_access_test_defs.h"
+
+    void target(STATUSOR_INT sor) {
+      if (!sor.ok()) return;
+
+      sor.value();
+    }
+  )cc");
+  ExpectDiagnosticsFor(R"cc(
+#include "unchecked_statusor_access_test_defs.h"
+
+    void target(STATUSOR_INT sor) {
+      if (sor.ok()) return;
+
+      sor.value();  // [[unsafe]]
+    }
+  )cc");
+  ExpectDiagnosticsFor(
+      R"cc(
+#include "unchecked_statusor_access_test_defs.h"
+
+        void target(STATUSOR_INT x, STATUSOR_INT y) {
+          if (!x.ok() || !y.ok()) return;
+
+          x.value();
+
+          y.value();
+        }
+      )cc");
+}
+
+TEST_P(UncheckedStatusOrAccessModelTest, TerminatingIfElseBranch) {
+  ExpectDiagnosticsFor(R"cc(
+#include "unchecked_statusor_access_test_defs.h"
+
+    void target(STATUSOR_INT sor) {
+      if (sor.ok()) {
+      } else {
+        return;
+      }
+
+      sor.value();
+    }
+  )cc");
+  ExpectDiagnosticsFor(R"cc(
+#include "unchecked_statusor_access_test_defs.h"
+
+    void target(STATUSOR_INT sor) {
+      if (!sor.ok()) {
+      } else {
+        return;
+      }
+
+      sor.value();  // [[unsafe]]
+    }
+  )cc");
+}
+
+TEST_P(UncheckedStatusOrAccessModelTest, TerminatingIfThenBranchInLoop) {
+  ExpectDiagnosticsFor(R"cc(
+#include "unchecked_statusor_access_test_defs.h"
+
+    void target(STATUSOR_INT sor) {
+      while (Make<bool>()) {
+        if (!sor.ok()) continue;
+
+        sor.value();
+      }
+
+      sor.value();  // [[unsafe]]
+    }
+  )cc");
+  ExpectDiagnosticsFor(R"cc(
+#include "unchecked_statusor_access_test_defs.h"
+
+    void target(STATUSOR_INT sor) {
+      while (Make<bool>()) {
+        if (!sor.ok()) break;
+
+        sor.value();
+      }
+
+      sor.value();  // [[unsafe]]
+    }
+  )cc");
+}
+
+TEST_P(UncheckedStatusOrAccessModelTest, TernaryConditionalOperator) {
+  ExpectDiagnosticsFor(R"cc(
+#include "unchecked_statusor_access_test_defs.h"
+
+    void target(STATUSOR_INT sor) {
+      sor.ok() ? sor.value() : 21;
+
+      sor.ok() ? 21 : sor.value();  // [[unsafe]]
+    }
+  )cc");
+  ExpectDiagnosticsFor(R"cc(
+#include "unchecked_statusor_access_test_defs.h"
+
+    void target(STATUSOR_INT sor) {
+      !sor.ok() ? 21 : sor.value();
+
+      !sor.ok() ? sor.value() : 21;  // [[unsafe]]
+    }
+  )cc");
+  ExpectDiagnosticsFor(R"cc(
+#include "unchecked_statusor_access_test_defs.h"
+
+    void target(STATUSOR_INT sor1, STATUSOR_INT sor2) {
+      !((__builtin_expect(false || (!(sor1.ok() && sor2.ok())), false)))
+          ? (void)0
+          : (void)1;
+      do {
+        sor1.value();  // [[unsafe]]
+        sor2.value();  // [[unsafe]]
+      } while (true);
+    }
+  )cc");
+}
+
+TEST_P(UncheckedStatusOrAccessModelTest, While) {
+  ExpectDiagnosticsFor(R"cc(
+#include "unchecked_statusor_access_test_defs.h"
+
+    void target(STATUSOR_INT sor) {
+      while (Make<bool>()) sor.value();  // [[unsafe]]
+    }
+  )cc");
+  ExpectDiagnosticsFor(R"cc(
+#include "unchecked_statusor_access_test_defs.h"
+
+    void target(STATUSOR_INT sor) {
+      while (sor.ok()) sor.value();
+    }
+  )cc");
+  ExpectDiagnosticsFor(R"cc(
+#include "unchecked_statusor_access_test_defs.h"
+
+    void target(STATUSOR_INT sor) {
+      while (!sor.ok()) sor.value();  // [[unsafe]]
+    }
+  )cc");
+  ExpectDiagnosticsFor(R"cc(
+#include "unchecked_statusor_access_test_defs.h"
+
+    void target(STATUSOR_INT sor) {
+      while (!!sor.ok()) sor.value();
+    }
+  )cc");
+  ExpectDiagnosticsFor(R"cc(
+#include "unchecked_statusor_access_test_defs.h"
+
+    void target(STATUSOR_INT sor) {
+      while (!!!sor.ok()) sor.value();  // [[unsafe]]
+    }
+  )cc");
+}
+
+TEST_P(UncheckedStatusOrAccessModelTest, While_LhsAndRhs) {
+  ExpectDiagnosticsFor(
+      R"cc(
+#include "unchecked_statusor_access_test_defs.h"
+
+        void target(STATUSOR_INT x, STATUSOR_INT y) {
+          while (x.ok() && y.ok()) {
+            x.value();
+
+            y.value();
+          }
+        }
+      )cc");
+}
+
+TEST_P(UncheckedStatusOrAccessModelTest, While_NotLhsAndRhs) {
+  ExpectDiagnosticsFor(R"cc(
+#include "unchecked_statusor_access_test_defs.h"
+
+    void target(STATUSOR_INT x, STATUSOR_INT y) {
+      while (!x.ok() && y.ok()) x.value();  // [[unsafe]]
+    }
+  )cc");
+  ExpectDiagnosticsFor(R"cc(
+#include "unchecked_statusor_access_test_defs.h"
+
+    void target(STATUSOR_INT x, STATUSOR_INT y) {
+      while (!x.ok() && y.ok()) y.value();
+    }
+  )cc");
+}
+
+TEST_P(UncheckedStatusOrAccessModelTest, While_LhsAndNotRhs) {
+  ExpectDiagnosticsFor(R"cc(
+#include "unchecked_statusor_access_test_defs.h"
+
+    void target(STATUSOR_INT x, STATUSOR_INT y) {
+      while (x.ok() && !y.ok()) x.value();
+    }
+  )cc");
+  ExpectDiagnosticsFor(R"cc(
+#include "unchecked_statusor_access_test_defs.h"
+
+    void target(STATUSOR_INT x, STATUSOR_INT y) {
+      while (x.ok() && !y.ok()) y.value();  // [[unsafe]]
+    }
+  )cc");
+}
+
+TEST_P(UncheckedStatusOrAccessModelTest, While_NotLhsAndNotRhs) {
+  ExpectDiagnosticsFor(R"cc(
+#include "unchecked_statusor_access_test_defs.h"
+
+    void target(STATUSOR_INT x, STATUSOR_INT y) {
+      while (!x.ok() && !y.ok()) x.value();  // [[unsafe]]
+    }
+  )cc");
+  ExpectDiagnosticsFor(R"cc(
+#include "unchecked_statusor_access_test_defs.h"
+
+    void target(STATUSOR_INT x, STATUSOR_INT y) {
+      while (!x.ok() && !y.ok()) y.value();  // [[unsafe]]
+    }
+  )cc");
+}
+
+TEST_P(UncheckedStatusOrAccessModelTest, While_Not_LhsAndRhs) {
+  ExpectDiagnosticsFor(R"cc(
+#include "unchecked_statusor_access_test_defs.h"
+
+    void target(STATUSOR_INT x, STATUSOR_INT y) {
+      while (!(x.ok() && y.ok())) x.value();  // [[unsafe]]
+    }
+  )cc");
+  ExpectDiagnosticsFor(R"cc(
+#include "unchecked_statusor_access_test_defs.h"
+
+    void target(STATUSOR_INT x, STATUSOR_INT y) {
+      while (!(x.ok() && y.ok())) y.value();  // [[unsafe]]
+    }
+  )cc");
+}
+
+TEST_P(UncheckedStatusOrAccessModelTest, While_Not_NotLhsAndRhs) {
+  ExpectDiagnosticsFor(R"cc(
+#include "unchecked_statusor_access_test_defs.h"
+
+    void target(STATUSOR_INT x, STATUSOR_INT y) {
+      while (!(!x.ok() && y.ok())) x.value();  // [[unsafe]]
+    }
+  )cc");
+  ExpectDiagnosticsFor(R"cc(
+#include "unchecked_statusor_access_test_defs.h"
+
+    void target(STATUSOR_INT x, STATUSOR_INT y) {
+      while (!(!x.ok() && y.ok())) y.value();  // [[unsafe]]
+    }
+  )cc");
+}
+
+TEST_P(UncheckedStatusOrAccessModelTest, While_Not_LhsAndNotRhs) {
+  ExpectDiagnosticsFor(R"cc(
+#include "unchecked_statusor_access_test_defs.h"
+
+    void target(STATUSOR_INT x, STATUSOR_INT y) {
+      while (!(x.ok() && !y.ok())) x.value();  // [[unsafe]]
+    }
+  )cc");
+  ExpectDiagnosticsFor(R"cc(
+#include "unchecked_statusor_access_test_defs.h"
+
+    void target(STATUSOR_INT x, STATUSOR_INT y) {
+      while (!(x.ok() && !y.ok())) y.value();  // [[unsafe]]
+    }
+  )cc");
+}
+
+TEST_P(UncheckedStatusOrAccessModelTest, While_Not_NotLhsAndNotRhs) {
+  ExpectDiagnosticsFor(R"cc(
+#include "unchecked_statusor_access_test_defs.h"
+
+    void target(STATUSOR_INT x, STATUSOR_INT y) {
+      while (!(!x.ok() && !y.ok())) x.value();  // [[unsafe]]
+    }
+  )cc");
+  ExpectDiagnosticsFor(R"cc(
+#include "unchecked_statusor_access_test_defs.h"
+
+    void target(STATUSOR_INT x, STATUSOR_INT y) {
+      while (!(!x.ok() && !y.ok())) y.value();  // [[unsafe]]
+    }
+  )cc");
+}
+
+TEST_P(UncheckedStatusOrAccessModelTest, While_LhsOrRhs) {
+  ExpectDiagnosticsFor(R"cc(
+#include "unchecked_statusor_access_test_defs.h"
+
+    void target(STATUSOR_INT x, STATUSOR_INT y) {
+      while (x.ok() || y.ok()) x.value();  // [[unsafe]]
+    }
+  )cc");
+  ExpectDiagnosticsFor(R"cc(
+#include "unchecked_statusor_access_test_defs.h"
+
+    void target(STATUSOR_INT x, STATUSOR_INT y) {
+      while (x.ok() || y.ok()) y.value();  // [[unsafe]]
+    }
+  )cc");
+}
+
+TEST_P(UncheckedStatusOrAccessModelTest, While_NotLhsOrRhs) {
+  ExpectDiagnosticsFor(R"cc(
+#include "unchecked_statusor_access_test_defs.h"
+
+    void target(STATUSOR_INT x, STATUSOR_INT y) {
+      while (!x.ok() || y.ok()) x.value();  // [[unsafe]]
+    }
+  )cc");
+  ExpectDiagnosticsFor(R"cc(
+#include "unchecked_statusor_access_test_defs.h"
+
+    void target(STATUSOR_INT x, STATUSOR_INT y) {
+      while (!x.ok() || y.ok()) y.value();  // [[unsafe]]
+    }
+  )cc");
+}
+
+TEST_P(UncheckedStatusOrAccessModelTest, While_LhsOrNotRhs) {
+  ExpectDiagnosticsFor(R"cc(
+#include "unchecked_statusor_access_test_defs.h"
+
+    void target(STATUSOR_INT x, STATUSOR_INT y) {
+      while (x.ok() || !y.ok()) x.value();  // [[unsafe]]
+    }
+  )cc");
+  ExpectDiagnosticsFor(R"cc(
+#include "unchecked_statusor_access_test_defs.h"
+
+    void target(STATUSOR_INT x, STATUSOR_INT y) {
+      while (x.ok() || !y.ok()) y.value();  // [[unsafe]]
+    }
+  )cc");
+}
+
+TEST_P(UncheckedStatusOrAccessModelTest, While_NotLhsOrNotRhs) {
+  ExpectDiagnosticsFor(R"cc(
+#include "unchecked_statusor_access_test_defs.h"
+
+    void target(STATUSOR_INT x, STATUSOR_INT y) {
+      while (!x.ok() || !y.ok()) x.value();  // [[unsafe]]
+    }
+  )cc");
+  ExpectDiagnosticsFor(R"cc(
+#include "unchecked_statusor_access_test_defs.h"
+
+    void target(STATUSOR_INT x, STATUSOR_INT y) {
+      while (!x.ok() || !y.ok()) y.value();  // [[unsafe]]
+    }
+  )cc");
+}
+
+TEST_P(UncheckedStatusOrAccessModelTest, While_Not_LhsOrRhs) {
+  ExpectDiagnosticsFor(R"cc(
+#include "unchecked_statusor_access_test_defs.h"
+
+    void target(STATUSOR_INT x, STATUSOR_INT y) {
+      while (!(x.ok() || y.ok())) x.value();  // [[unsafe]]
+    }
+  )cc");
+  ExpectDiagnosticsFor(R"cc(
+#include "unchecked_statusor_access_test_defs.h"
+
+    void target(STATUSOR_INT x, STATUSOR_INT y) {
+      while (!(x.ok() || y.ok())) y.value();  // [[unsafe]]
+    }
+  )cc");
+}
+
+TEST_P(UncheckedStatusOrAccessModelTest, While_Not_NotLhsOrRhs) {
+  ExpectDiagnosticsFor(R"cc(
+#include "unchecked_statusor_access_test_defs.h"
+
+    void target(STATUSOR_INT x, STATUSOR_INT y) {
+      while (!(!x.ok() || y.ok())) x.value();
+    }
+  )cc");
+  ExpectDiagnosticsFor(R"cc(
+#include "unchecked_statusor_access_test_defs.h"
+
+    void target(STATUSOR_INT x, STATUSOR_INT y) {
+      while (!(!x.ok() || y.ok())) y.value();  // [[unsafe]]
+    }
+  )cc");
+}
+
+TEST_P(UncheckedStatusOrAccessModelTest, While_Not_LhsOrNotRhs) {
+  ExpectDiagnosticsFor(R"cc(
+#include "unchecked_statusor_access_test_defs.h"
+
+    void target(STATUSOR_INT x, STATUSOR_INT y) {
+      while (!(x.ok() || !y.ok())) x.value();  // [[unsafe]]
+    }
+  )cc");
+  ExpectDiagnosticsFor(R"cc(
+#include "unchecked_statusor_access_test_defs.h"
+
+    void target(STATUSOR_INT x, STATUSOR_INT y) {
+      while (!(x.ok() || !y.ok())) y.value();
+    }
+  )cc");
+}
+
+TEST_P(UncheckedStatusOrAccessModelTest, While_Not_NotLhsOrNotRhs) {
+  ExpectDiagnosticsFor(
+      R"cc(
+#include "unchecked_statusor_access_test_defs.h"
+
+        void target(STATUSOR_INT x, STATUSOR_INT y) {
+          while (!(!x.ok() || !y.ok())) {
+            x.value();
+
+            y.value();
+          }
+        }
+      )cc");
+}
+
+TEST_P(UncheckedStatusOrAccessModelTest, While_AccessAfterStmt) {
+  ExpectDiagnosticsFor(R"cc(
+#include "unchecked_statusor_access_test_defs.h"
+
+    void target(STATUSOR_INT sor) {
+      while (sor.ok()) {
+      }
+
+      sor.value();  // [[unsafe]]
+    }
+  )cc");
+  ExpectDiagnosticsFor(R"cc(
+#include "unchecked_statusor_access_test_defs.h"
+
+    void target(STATUSOR_INT sor) {
+      while (!sor.ok()) {
+      }
+
+      sor.value();
+    }
+  )cc");
+}
+
+TEST_P(UncheckedStatusOrAccessModelTest, While_TerminatingBranch_Return) {
+  ExpectDiagnosticsFor(R"cc(
+#include "unchecked_statusor_access_test_defs.h"
+
+    void target(STATUSOR_INT sor) {
+      while (!sor.ok()) return;
+
+      sor.value();
+    }
+  )cc");
+  ExpectDiagnosticsFor(R"cc(
+#include "unchecked_statusor_access_test_defs.h"
+
+    void target(STATUSOR_INT sor) {
+      while (sor.ok()) return;
+
+      sor.value();  // [[unsafe]]
+    }
+  )cc");
+}
+
+TEST_P(UncheckedStatusOrAccessModelTest, While_NestedIfWithBinaryCondition) {
+  ExpectDiagnosticsFor(
+      R"cc(
+#include "unchecked_statusor_access_test_defs.h"
+
+        void target(STATUSOR_INT x, STATUSOR_INT y) {
+          while (Make<bool>()) {
+            if (x.ok() && y.ok()) {
+              x.value();
+
+              y.value();
+            }
+          }
+        }
+      )cc");
+  ExpectDiagnosticsFor(
+      R"cc(
+#include "unchecked_statusor_access_test_defs.h"
+
+        void target(STATUSOR_INT x, STATUSOR_INT y) {
+          while (Make<bool>()) {
+            if (!(!x.ok() || !y.ok())) {
+              x.value();
+
+              y.value();
+            }
+          }
+        }
+      )cc");
+}
+
+TEST_P(UncheckedStatusOrAccessModelTest, BuiltinExpect) {
+  ExpectDiagnosticsFor(
+      R"cc(
+#include "unchecked_statusor_access_test_defs.h"
+
+        void target(STATUSOR_INT x, STATUSOR_INT y) {
+          if (!__builtin_expect(!x.ok() || __builtin_expect(!y.ok(), true), false)) {
+            x.value();
+
+            y.value();
+          }
+        }
+      )cc");
+}
+
+TEST_P(UncheckedStatusOrAccessModelTest, CopyAssignment) {
+  ExpectDiagnosticsFor(R"cc(
+#include "unchecked_statusor_access_test_defs.h"
+
+    void target() {
+      STATUSOR_INT sor = Make<STATUSOR_INT>();
+      if (sor.ok()) {
+        sor = Make<STATUSOR_INT>();
+        sor.value();  // [[unsafe]]
+      }
+    }
+  )cc");
+  ExpectDiagnosticsFor(R"cc(
+#include "unchecked_statusor_access_test_defs.h"
+
+    void target() {
+      STATUSOR_INT sor = Make<STATUSOR_INT>();
+      if (!sor.ok()) return;
+
+      sor = Make<STATUSOR_INT>();
+      sor.value();  // [[unsafe]]
+    }
+  )cc");
+  ExpectDiagnosticsFor(R"cc(
+#include "unchecked_statusor_access_test_defs.h"
+
+    void target() {
+      STATUSOR_INT x = Make<STATUSOR_INT>();
+      if (x.ok()) {
+        STATUSOR_INT y = x;
+        x = Make<STATUSOR_INT>();
+
+        y.value();
+
+        x.value();  // [[unsafe]]
+      }
+    }
+  )cc");
+  ExpectDiagnosticsFor(
+      R"cc(
+#include "unchecked_statusor_access_test_defs.h"
+
+        void target() {
+          STATUSOR_INT x = Make<STATUSOR_INT>();
+          STATUSOR_INT y = x;
+          if (!y.ok()) return;
+
+          x.value();
+
+          y = Make<STATUSOR_INT>();
+          x.value();
+        }
+      )cc");
+  ExpectDiagnosticsFor(R"cc(
+#include "unchecked_statusor_access_test_defs.h"
+
+    struct Foo {
+      STATUSOR_INT bar;
+    };
+
+    void target(Foo foo) {
+      foo.bar = Make<STATUSOR_INT>();
+      if (foo.bar.ok()) {
+        foo.bar.value();
+
+        foo.bar = Make<STATUSOR_INT>();
+        foo.bar.value();  // [[unsafe]]
+      }
+    }
+  )cc");
+}
+
+TEST_P(UncheckedStatusOrAccessModelTest, ShortCircuitingBinaryOperators) {
+  ExpectDiagnosticsFor(R"cc(
+#include "unchecked_statusor_access_test_defs.h"
+
+    void target(STATUSOR_BOOL sor) {
+      bool b = sor.ok() & sor.value();  // [[unsafe]]
+    }
+  )cc");
+  ExpectDiagnosticsFor(R"cc(
+#include "unchecked_statusor_access_test_defs.h"
+
+    void target(STATUSOR_BOOL sor) {
+      bool b = sor.ok() && sor.value();
+    }
+  )cc");
+  ExpectDiagnosticsFor(R"cc(
+#include "unchecked_statusor_access_test_defs.h"
+
+    void target(STATUSOR_BOOL sor) {
+      bool b = !sor.ok() && sor.value();  // [[unsafe]]
+    }
+  )cc");
+  ExpectDiagnosticsFor(R"cc(
+#include "unchecked_statusor_access_test_defs.h"
+
+    void target(STATUSOR_BOOL sor) {
+      bool b = sor.ok() || sor.value();  // [[unsafe]]
+    }
+  )cc");
+  ExpectDiagnosticsFor(R"cc(
+#include "unchecked_statusor_access_test_defs.h"
+
+    void target(STATUSOR_BOOL sor) {
+      bool b = !sor.ok() || sor.value();
+    }
+  )cc");
+  ExpectDiagnosticsFor(R"cc(
+#include "unchecked_statusor_access_test_defs.h"
+
+    void target(bool b, STATUSOR_INT sor) {
+      if (b || sor.ok()) {
+        do {
+          sor.value();  // [[unsafe]]
+        } while (true);
+      }
+    }
+  )cc");
+  ExpectDiagnosticsFor(R"cc(
+#include "unchecked_statusor_access_test_defs.h"
+
+    void target(bool b, STATUSOR_INT sor) {
+      if (__builtin_expect(b || sor.ok(), false)) {
+        do {
+          sor.value();  // [[unsafe]]
+        } while (false);
+      }
+    }
+  )cc");
+  ExpectDiagnosticsFor(
+      R"cc(
+#include "unchecked_statusor_access_test_defs.h"
+
+        void target(STATUSOR_INT sor1, STATUSOR_INT sor2) {
+          while (sor1.ok() && sor2.ok()) sor1.value();
+          while (sor1.ok() && sor2.ok()) sor2.value();
+        }
+      )cc");
+}
+
+TEST_P(UncheckedStatusOrAccessModelTest, References) {
+  ExpectDiagnosticsFor(R"cc(
+#include "unchecked_statusor_access_test_defs.h"
+
+    void target() {
+      STATUSOR_INT x = Make<STATUSOR_INT>();
+      STATUSOR_INT& y = x;
+      if (x.ok()) {
+        x.value();
+
+        y.value();
+      } else {
+        x.value();  // [[unsafe]]
+
+        y.value();  // [[unsafe]]
+      }
+    }
+  )cc");
+  ExpectDiagnosticsFor(
+      R"cc(
+#include "unchecked_statusor_access_test_defs.h"
+
+        void target() {
+          STATUSOR_INT x = Make<STATUSOR_INT>();
+          STATUSOR_INT& y = x;
+          if (y.ok()) {
+            x.value();
+
+            y.value();
+          } else {
+            x.value();  // [[unsafe]]
+
+            y.value();  // [[unsafe]]
+          }
+        }
+      )cc");
+  ExpectDiagnosticsFor(
+      R"cc(
+#include "unchecked_statusor_access_test_defs.h"
+
+        void target() {
+          STATUSOR_INT x = Make<STATUSOR_INT>();
+          STATUSOR_INT& y = x;
+          if (!y.ok()) return;
+
+          x.value();
+
+          y = Make<STATUSOR_INT>();
+          x.value();  // [[unsafe]]
+        }
+      )cc");
+  ExpectDiagnosticsFor(
+      R"cc(
+#include "unchecked_statusor_access_test_defs.h"
+
+        void target() {
+          STATUSOR_INT x = Make<STATUSOR_INT>();
+          const STATUSOR_INT& y = x;
+          if (!y.ok()) return;
+
+          y.value();
+
+          x = Make<STATUSOR_INT>();
+          y.value();  // [[unsafe]]
+        }
+      )cc");
+}
+
+TEST_P(UncheckedStatusOrAccessModelTest, NoReturnAttribute) {
+  ExpectDiagnosticsFor(R"cc(
+#include "unchecked_statusor_access_test_defs.h"
+
+    __attribute__((noreturn)) void f();
+
+    void target(STATUSOR_INT sor) {
+      if (!sor.ok()) f();
+
+      sor.value();
+    }
+  )cc");
+  ExpectDiagnosticsFor(R"cc(
+#include "unchecked_statusor_access_test_defs.h"
+
+    void f();
+
+    void target(STATUSOR_INT sor) {
+      if (!sor.ok()) f();
+
+      sor.value();  // [[unsafe]]
+    }
+  )cc");
+  ExpectDiagnosticsFor(R"cc(
+#include "unchecked_statusor_access_test_defs.h"
+
+    struct Foo {
+      __attribute__((noreturn)) ~Foo();
+      void Bar();
+    };
+
+    void target(STATUSOR_INT sor) {
+      if (!sor.ok()) Foo().Bar();
+
+      sor.value();
+    }
+  )cc");
+  ExpectDiagnosticsFor(R"cc(
+#include "unchecked_statusor_access_test_defs.h"
+
+    struct Foo {
+      ~Foo();
+      void Bar();
+    };
+
+    void target(STATUSOR_INT sor) {
+      if (!sor.ok()) Foo().Bar();
+
+      sor.value();  // [[unsafe]]
+    }
+  )cc");
+  ExpectDiagnosticsFor(R"cc(
+#include "unchecked_statusor_access_test_defs.h"
+
+    void f();
+    __attribute__((noreturn)) void g();
+
+    void target(STATUSOR_INT sor) {
+      sor.ok() ? f() : g();
+
+      sor.value();
+    }
+  )cc");
+  ExpectDiagnosticsFor(R"cc(
+#include "unchecked_statusor_access_test_defs.h"
+
+    __attribute__((noreturn)) void f();
+    void g();
+
+    void target(STATUSOR_INT sor) {
+      !sor.ok() ? f() : g();
+
+      sor.value();
+    }
+  )cc");
+  ExpectDiagnosticsFor(R"cc(
+#include "unchecked_statusor_access_test_defs.h"
+
+    void f();
+    void g();
+
+    void target(STATUSOR_INT sor) {
+      sor.ok() ? f() : g();
+
+      sor.value();  // [[unsafe]]
+    }
+  )cc");
+  ExpectDiagnosticsFor(R"cc(
+#include "unchecked_statusor_access_test_defs.h"
+
+    void terminate() __attribute__((noreturn));
+
+    void target(STATUSOR_INT sor) {
+      sor.value();  // [[unsafe]]
+      terminate();
+    }
+  )cc");
+  ExpectDiagnosticsFor(R"cc(
+#include "unchecked_statusor_access_test_defs.h"
+
+    void terminate() __attribute__((noreturn));
+
+    void target(STATUSOR_INT sor) {
+      if (sor.ok()) sor.value();
+      terminate();
+    }
+  )cc");
+  ExpectDiagnosticsFor(R"cc(
+#include "unchecked_statusor_access_test_defs.h"
+
+    void terminate() __attribute__((noreturn));
+
+    struct Foo {
+      ~Foo() __attribute__((noreturn));
+    };
+
+    void target() {
+      auto sor = Make<absl::StatusOr<Foo>>();
+      !(false || !(sor.ok())) ? (void)0 : terminate();
+      sor.value();
+      terminate();
+    }
+  )cc");
+}
+
+TEST_P(UncheckedStatusOrAccessModelTest, DeclInLoop) {
+  ExpectDiagnosticsFor(R"cc(
+#include "unchecked_statusor_access_test_defs.h"
+
+    void target(STATUSOR_INT sor) {
+      while (auto ok = sor.ok()) sor.value();
+    }
+  )cc");
+  ExpectDiagnosticsFor(R"cc(
+#include "unchecked_statusor_access_test_defs.h"
+
+    using BoolAlias = bool;
+
+    void target(STATUSOR_INT sor) {
+      while (BoolAlias ok = sor.ok()) sor.value();
+    }
+  )cc");
+  ExpectDiagnosticsFor(R"cc(
+#include "unchecked_statusor_access_test_defs.h"
+
+    void target() {
+      while (Make<bool>()) {
+        STATUSOR_INT sor = Make<STATUSOR_INT>();
+        sor.value();  // [[unsafe]]
+      }
+    }
+  )cc");
+  ExpectDiagnosticsFor(R"cc(
+#include "unchecked_statusor_access_test_defs.h"
+
+    using StatusOrInt = STATUSOR_INT;
+
+    void target() {
+      while (Make<bool>()) {
+        StatusOrInt sor = Make<STATUSOR_INT>();
+        sor.value();  // [[unsafe]]
+      }
+    }
+  )cc");
+}
+
+TEST_P(UncheckedStatusOrAccessModelTest, NonEvaluatedExprInCondition) {
+  ExpectDiagnosticsFor(
+      R"cc(
+#include "unchecked_statusor_access_test_defs.h"
+
+        bool unknown();
+
+        void target(STATUSOR_INT sor) {
+          if (unknown() && sor.ok()) sor.value();
+          if (sor.ok() && unknown()) sor.value();
+        }
+      )cc");
+  ExpectDiagnosticsFor(
+      R"cc(
+#include "unchecked_statusor_access_test_defs.h"
+
+        bool unknown();
+
+        void target(STATUSOR_INT sor) {
+          if (!(!unknown() || !sor.ok())) sor.value();
+          if (!(!sor.ok() || !unknown())) sor.value();
+        }
+      )cc");
+  ExpectDiagnosticsFor(R"cc(
+#include "unchecked_statusor_access_test_defs.h"
+
+    bool unknown();
+
+    void target(STATUSOR_INT sor) {
+      if (unknown() || sor.ok()) sor.value();  // [[unsafe]]
+    }
+  )cc");
+  ExpectDiagnosticsFor(R"cc(
+#include "unchecked_statusor_access_test_defs.h"
+
+    bool unknown();
+
+    void target(STATUSOR_INT sor) {
+      if (sor.ok() || unknown()) sor.value();  // [[unsafe]]
+    }
+  )cc");
+}
+
+TEST_P(UncheckedStatusOrAccessModelTest, CorrelatedBranches) {
+  ExpectDiagnosticsFor(R"cc(
+#include "unchecked_statusor_access_test_defs.h"
+
+    void target(bool b, STATUSOR_INT sor) {
+      if (b || sor.ok()) {
+        if (!b) sor.value();
+      }
+    }
+  )cc");
+  ExpectDiagnosticsFor(R"cc(
+#include "unchecked_statusor_access_test_defs.h"
+
+    void target(bool b, STATUSOR_INT sor) {
+      if (b && !sor.ok()) return;
+      if (b) sor.value();
+    }
+  )cc");
+  ExpectDiagnosticsFor(R"cc(
+#include "unchecked_statusor_access_test_defs.h"
+
+    void target(bool b, STATUSOR_INT sor) {
+      if (sor.ok()) b = true;
+      if (b) sor.value();  // [[unsafe]]
+    }
+  )cc");
+  ExpectDiagnosticsFor(R"cc(
+#include "unchecked_statusor_access_test_defs.h"
+
+    void target(bool b, STATUSOR_INT sor) {
+      if (b) return;
+      if (sor.ok()) b = true;
+      if (b) sor.value();
+    }
+  )cc");
+}
+
+TEST_P(UncheckedStatusOrAccessModelTest, ConditionWithInitStmt) {
+  ExpectDiagnosticsFor(R"cc(
+#include "unchecked_statusor_access_test_defs.h"
+
+    void target() {
+      if (STATUSOR_INT sor = Make<STATUSOR_INT>(); sor.ok()) sor.value();
+    }
+  )cc");
+  ExpectDiagnosticsFor(R"cc(
+#include "unchecked_statusor_access_test_defs.h"
+
+    void target() {
+      if (STATUSOR_INT sor = Make<STATUSOR_INT>(); !sor.ok())
+        sor.value();  // [[unsafe]]
+    }
+  )cc");
+}
+
+TEST_P(UncheckedStatusOrAccessModelTest, DeadCode) {
+  ExpectDiagnosticsFor(R"cc(
+#include "unchecked_statusor_access_test_defs.h"
+
+    void target(STATUSOR_INT sor) {
+      bool b = false;
+      if (b) sor.value();
+    }
+  )cc");
+  ExpectDiagnosticsFor(R"cc(
+#include "unchecked_statusor_access_test_defs.h"
+
+    void target(STATUSOR_INT sor) {
+      bool b;
+      b = false;
+      if (b) sor.value();
+    }
+  )cc");
+}
+
+TEST_P(UncheckedStatusOrAccessModelTest, TemporaryDestructors) {
+  ExpectDiagnosticsFor(R"cc(
+#include "unchecked_statusor_access_test_defs.h"
+
+    void target(STATUSOR_INT sor) {
+      sor.ok() ? sor.value() : Fatal().value();
+
+      sor.value();
+    }
+  )cc");
+  ExpectDiagnosticsFor(R"cc(
+#include "unchecked_statusor_access_test_defs.h"
+
+    void target(STATUSOR_INT sor) {
+      !sor.ok() ? Fatal().value() : sor.value();
+
+      sor.value();
+    }
+  )cc");
+  ExpectDiagnosticsFor(R"cc(
+#include "unchecked_statusor_access_test_defs.h"
+
+    void target(bool b, STATUSOR_INT sor) {
+      b ? 0 : sor.ok() ? sor.value() : Fatal().value();
+
+      sor.value();  // [[unsafe]]
+    }
+  )cc");
+  ExpectDiagnosticsFor(R"cc(
+#include "unchecked_statusor_access_test_defs.h"
+
+    void target(bool b, STATUSOR_INT sor) {
+      for (int i = 0; i < 10; i++) {
+        (b && sor.ok()) ? sor.value() : Fatal().value();
+
+        if (b) sor.value();
+      }
+    }
+  )cc");
+  ExpectDiagnosticsFor(R"cc(
+#include "unchecked_statusor_access_test_defs.h"
+
+    void target(bool b, STATUSOR_INT sor) {
+      for (int i = 0; i < 10; i++) {
+        (b || !sor.ok()) ? Fatal().value() : 0;
+
+        if (!b) sor.value();
+      }
+    }
+  )cc");
+  ExpectDiagnosticsFor(R"cc(
+#include "unchecked_statusor_access_test_defs.h"
+
+    void target(bool b, STATUSOR_INT sor) {
+      for (int i = 0; i < 10; i++) {
+        (false || !(b && sor.ok())) ? Fatal().value() : 0;
+
+        do {
+          sor.value();
+        } while (b);
+      }
+    }
+  )cc");
+}
+
+TEST_P(UncheckedStatusOrAccessModelTest, CheckMacro) {
+  ExpectDiagnosticsFor(R"cc(
+#include "unchecked_statusor_access_test_defs.h"
+
+    void target(STATUSOR_INT sor) {
+      CHECK(sor.ok());
+      sor.value();
+    }
+  )cc");
+  ExpectDiagnosticsFor(R"cc(
+#include "unchecked_statusor_access_test_defs.h"
+
+    void target(STATUSOR_INT sor) {
+      CHECK(!sor.ok());
+      sor.value();  // [[unsafe]]
+    }
+  )cc");
+}
+
+TEST_P(UncheckedStatusOrAccessModelTest, QcheckMacro) {
+  ExpectDiagnosticsFor(R"cc(
+#include "unchecked_statusor_access_test_defs.h"
+
+    void target(STATUSOR_INT sor) {
+      QCHECK(sor.ok());
+      sor.value();
+    }
+  )cc");
+  ExpectDiagnosticsFor(R"cc(
+#include "unchecked_statusor_access_test_defs.h"
+
+    void target(STATUSOR_INT sor) {
+      QCHECK(!sor.ok());
+      sor.value();  // [[unsafe]]
+    }
+  )cc");
+}
+
+TEST_P(UncheckedStatusOrAccessModelTest, CheckNeMacro) {
+  ExpectDiagnosticsFor(R"cc(
+#include "unchecked_statusor_access_test_defs.h"
+
+    void target(STATUSOR_INT sor) {
+      CHECK_NE(sor.status(), absl::OkStatus());
+      sor.value();  // [[unsafe]]
+    }
+  )cc");
+}
+
+TEST_P(UncheckedStatusOrAccessModelTest, QcheckNeMacro) {
+  ExpectDiagnosticsFor(R"cc(
+#include "unchecked_statusor_access_test_defs.h"
+
+    void target(STATUSOR_INT sor) {
+      QCHECK_NE(sor.status(), absl::OkStatus());
+      sor.value();  // [[unsafe]]
+    }
+  )cc");
+}
+
+TEST_P(UncheckedStatusOrAccessModelTest, GlobalVars) {
+  // The following examples are not sound as there could be opaque calls between
+  // the ok() and the value() calls that change the StatusOr value.
+  ExpectDiagnosticsFor(R"cc(
+#include "unchecked_statusor_access_test_defs.h"
+
+    static STATUSOR_INT sor;
+
+    void target() {
+      if (sor.ok())
+        sor.value();
+      else
+        sor.value();  // [[unsafe]]
+    }
+  )cc");
+  ExpectDiagnosticsFor(R"cc(
+#include "unchecked_statusor_access_test_defs.h"
+
+    void target() {
+      static STATUSOR_INT sor;
+      if (sor.ok())
+        sor.value();
+      else
+        sor.value();  // [[unsafe]]
+    }
+  )cc");
+  ExpectDiagnosticsFor(R"cc(
+#include "unchecked_statusor_access_test_defs.h"
+
+    struct Foo {
+      static STATUSOR_INT sor;
+    };
+
+    void target(Foo foo) {
+      if (foo.sor.ok())
+        foo.sor.value();
+      else
+        foo.sor.value();  // [[unsafe]]
+    }
+  )cc");
+  ExpectDiagnosticsFor(R"cc(
+#include "unchecked_statusor_access_test_defs.h"
+
+    struct Foo {
+      static STATUSOR_INT sor;
+    };
+
+    void target() {
+      if (Foo::sor.ok())
+        Foo::sor.value();
+      else
+        Foo::sor.value();  // [[unsafe]]
+    }
+  )cc");
+  ExpectDiagnosticsFor(R"cc(
+#include "unchecked_statusor_access_test_defs.h"
+
+    struct Foo {
+      static STATUSOR_INT sor;
+
+      static void target() {
+        if (sor.ok())
+          sor.value();
+        else
+          sor.value();  // [[unsafe]]
+      }
+    };
+  )cc");
+  ExpectDiagnosticsFor(R"cc(
+#include "unchecked_statusor_access_test_defs.h"
+
+    struct Foo {
+      static STATUSOR_INT sor;
+
+      void target() {
+        if (sor.ok())
+          sor.value();
+        else
+          sor.value();  // [[unsafe]]
+      }
+    };
+  )cc");
+  ExpectDiagnosticsFor(R"cc(
+#include "unchecked_statusor_access_test_defs.h"
+
+    struct S {
+      static const int x = -1;
+    };
+
+    int target(S s) { return s.x; }
+  )cc");
+}
+
+TEST_P(UncheckedStatusOrAccessModelTest, ReferenceReceivers) {
+  // The following examples are not sound as there could be opaque calls between
+  // the ok() and the value() calls that change the StatusOr value. However,
+  // this is the behavior that users expect so it is here to stay.
+  ExpectDiagnosticsFor(R"cc(
+#include "unchecked_statusor_access_test_defs.h"
+
+    void target(STATUSOR_INT& sor) {
+      if (sor.ok())
+        sor.value();
+      else
+        sor.value();  // [[unsafe]]
+    }
+  )cc");
+  ExpectDiagnosticsFor(R"cc(
+#include "unchecked_statusor_access_test_defs.h"
+
+    struct Foo {
+      STATUSOR_INT& sor;
+    };
+
+    void target(Foo foo) {
+      if (foo.sor.ok())
+        foo.sor.value();
+      else
+        foo.sor.value();  // [[unsafe]]
+    }
+  )cc");
+  ExpectDiagnosticsFor(R"cc(
+#include "unchecked_statusor_access_test_defs.h"
+
+    struct Bar {
+      STATUSOR_INT sor;
+    };
+
+    struct Foo {
+      Bar& bar;
+    };
+
+    void target(Foo foo) {
+      if (foo.bar.sor.ok())
+        foo.bar.sor.value();
+      else
+        foo.bar.sor.value();  // [[unsafe]]
+    }
+  )cc");
+  ExpectDiagnosticsFor(R"cc(
+#include "unchecked_statusor_access_test_defs.h"
+
+    struct Foo {
+      STATUSOR_INT& sor;
+    };
+
+    void target(Foo& foo) {
+      if (foo.sor.ok())
+        foo.sor.value();
+      else
+        foo.sor.value();  // [[unsafe]]
+    }
+  )cc");
+}
+
+TEST_P(UncheckedStatusOrAccessModelTest, Lambdas) {
+  ExpectDiagnosticsForLambda(R"cc(
+#include "unchecked_statusor_access_test_defs.h"
+
+    void target() {
+      [](STATUSOR_INT sor) {
+        if (sor.ok())
+          sor.value();
+        else
+          sor.value();  // [[unsafe]]
+      }(Make<STATUSOR_INT>());
+    }
+  )cc");
+  ExpectDiagnosticsForLambda(R"cc(
+#include "unchecked_statusor_access_test_defs.h"
+
+    void target(STATUSOR_INT sor) {
+      [sor]() {
+        if (sor.ok())
+          sor.value();
+        else
+          sor.value();  // [[unsafe]]
+      }();
+    }
+  )cc");
+  ExpectDiagnosticsForLambda(R"cc(
+#include "unchecked_statusor_access_test_defs.h"
+
+    void target(STATUSOR_INT sor) {
+      [&sor]() {
+        if (sor.ok())
+          sor.value();
+        else
+          sor.value();  // [[unsafe]]
+      }();
+    }
+  )cc");
+  ExpectDiagnosticsForLambda(R"cc(
+#include "unchecked_statusor_access_test_defs.h"
+
+    void target(STATUSOR_INT sor) {
+      [sor2 = sor]() {
+        if (sor2.ok())
+          sor2.value();
+        else
+          sor2.value();  // [[unsafe]]
+      }();
+    }
+  )cc");
+  ExpectDiagnosticsForLambda(R"cc(
+#include "unchecked_statusor_access_test_defs.h"
+
+    void target(STATUSOR_INT sor) {
+      [&]() {
+        if (sor.ok())
+          sor.value();
+        else
+          sor.value();  // [[unsafe]]
+      }();
+    }
+  )cc");
+  ExpectDiagnosticsForLambda(R"cc(
+#include "unchecked_statusor_access_test_defs.h"
+
+    void target(STATUSOR_INT sor) {
+      [=]() {
+        if (sor.ok())
+          sor.value();
+        else
+          sor.value();  // [[unsafe]]
+      }();
+    }
+  )cc");
+  ExpectDiagnosticsForLambda(R"cc(
+#include "unchecked_statusor_access_test_defs.h"
+
+    struct Foo {
+      STATUSOR_INT sor;
+
+      void target() {
+        [this]() {
+          if (sor.ok())
+            sor.value();
+          else
+            sor.value();  // [[unsafe]]
+        }();
+      }
+    };
+  )cc");
+}
+
+TEST_P(UncheckedStatusOrAccessModelTest, GoodLambda) {
+  ExpectDiagnosticsFor(R"cc(
+#include "unchecked_statusor_access_test_defs.h"
+
+    int target() {
+      STATUSOR_INT sor = Make<STATUSOR_INT>();
+      if (sor.ok()) return [&s = sor.value()] { return s; }();
+      return 0;
+    }
+  )cc");
+}
+
+TEST_P(UncheckedStatusOrAccessModelTest, Status) {
+  ExpectDiagnosticsFor(R"cc(
+#include "unchecked_statusor_access_test_defs.h"
+
+    void foo();
+
+    void target(STATUS s) {
+      if (s.ok()) foo();
+    }
+  )cc");
+  ExpectDiagnosticsFor(R"cc(
+#include "unchecked_statusor_access_test_defs.h"
+
+    void foo();
+
+    void target() {
+      STATUS s = Make<STATUSOR_INT>().status();
+      if (s.ok()) foo();
+    }
+  )cc");
+}
+
+TEST_P(UncheckedStatusOrAccessModelTest, ExpectThatMacro) {
+  ExpectDiagnosticsFor(R"cc(
+#include "unchecked_statusor_access_test_defs.h"
+
+    void target(STATUSOR_INT sor) {
+      EXPECT_THAT(sor, testing::status::IsOk());
+
+      sor.value();  // [[unsafe]]
+    }
+  )cc");
+  ExpectDiagnosticsFor(R"cc(
+#include "unchecked_statusor_access_test_defs.h"
+
+    void target(STATUSOR_INT sor) {
+      EXPECT_THAT(sor.status(), testing::status::IsOk());
+
+      sor.value();  // [[unsafe]]
+    }
+  )cc");
+  ExpectDiagnosticsFor(R"cc(
+#include "unchecked_statusor_access_test_defs.h"
+
+    void target() {
+      STATUSOR_INT sor = Make<STATUSOR_INT>();
+      EXPECT_THAT(sor, testing::status::IsOk());
+
+      sor.value();  // [[unsafe]]
+    }
+  )cc");
+}
+
+TEST_P(UncheckedStatusOrAccessModelTest, ExpectOkMacro) {
+  ExpectDiagnosticsFor(R"cc(
+#include "unchecked_statusor_access_test_defs.h"
+
+    void target(STATUSOR_INT sor) {
+      EXPECT_OK(sor);
+
+      sor.value();  // [[unsafe]]
+    }
+  )cc");
+  ExpectDiagnosticsFor(R"cc(
+#include "unchecked_statusor_access_test_defs.h"
+
+    void target(STATUSOR_INT sor) {
+      EXPECT_OK(sor.status());
+
+      sor.value();  // [[unsafe]]
+    }
+  )cc");
+  ExpectDiagnosticsFor(R"cc(
+#include "unchecked_statusor_access_test_defs.h"
+
+    void target() {
+      STATUSOR_INT sor = Make<STATUSOR_INT>();
+      EXPECT_OK(sor);
+
+      sor.value();  // [[unsafe]]
+    }
+  )cc");
+}
+
+TEST_P(UncheckedStatusOrAccessModelTest, BreadthFirstBlockTraversalLoop) {
+  // Evaluating the CFG blocks of the code below in breadth-first order results
+  // in an infinite loop. Each iteration of the while loop below results in a
+  // new value being assigned to the storage location of sor1. However,
+  // following a bread-first order of evaluation, downstream blocks will join
+  // environments of different generations of predecessor blocks having distinct
+  // values assigned to the sotrage location of sor1, resulting in not assigning
+  // a value to the storage location of sor1 in successors. As iterations of the
+  // analysis go, the state of the environment flips between having a value
+  // assigned to the storage location of sor1 and not having a value assigned to
+  // it. Since the evaluation of the copy constructor expression in bar(sor1)
+  // depends on a value being assigned to sor1, the state of the environment
+  // also flips between having a storage location assigned to the bar(sor1)
+  // expression and not having a storage location assigned to it. This leads to
+  // an infinite loop as the environment can't stabilize.
+  ExpectDiagnosticsFor(R"cc(
+#include "unchecked_statusor_access_test_defs.h"
+
+    void foo(int, int);
+    STATUSOR_INT bar(STATUSOR_INT);
+    void baz(int);
+
+    void target() {
+      while (true) {
+        STATUSOR_INT sor1 = Make<STATUSOR_INT>();
+        if (sor1.ok()) {
+          STATUSOR_INT sor2 = Make<STATUSOR_INT>();
+          if (sor2.ok()) foo(sor1.value(), sor2.value());
+        }
+
+        STATUSOR_INT sor3 = bar(sor1);
+        for (int i = 0; i < 5; i++) sor3 = bar(sor1);
+
+        baz(sor3.value());  // [[unsafe]]
+      }
+    }
+  )cc");
+}
+
+TEST_P(UncheckedStatusOrAccessModelTest, ReturnValue) {
+  ExpectDiagnosticsFor(R"cc(
+#include "unchecked_statusor_access_test_defs.h"
+
+    void target() {
+      STATUSOR_INT sor = Make<STATUSOR_INT>();
+      if (sor.ok())
+        sor.value();
+      else
+        sor.value();  // [[unsafe]]
+    }
+  )cc");
+}
+
+TEST_P(UncheckedStatusOrAccessModelTest, Goto) {
+  ExpectDiagnosticsFor(R"cc(
+#include "unchecked_statusor_access_test_defs.h"
+
+    void target(STATUSOR_INT sor) {
+    label:
+      if (sor.ok())
+        sor.value();
+      else
+        sor.value();  // [[unsafe]]
+      goto label;
+    }
+  )cc");
+  ExpectDiagnosticsFor(R"cc(
+#include "unchecked_statusor_access_test_defs.h"
+
+    void target(STATUSOR_INT sor) {
+    label:
+      if (!sor.ok()) goto label;
+      sor.value();
+    }
+  )cc");
+  ExpectDiagnosticsFor(R"cc(
+#include "unchecked_statusor_access_test_defs.h"
+
+    void target(STATUSOR_INT sor) {
+      if (!sor.ok()) return;
+      goto label;
+    label:
+      sor.value();
+    }
+  )cc");
+}
+
+TEST_P(UncheckedStatusOrAccessModelTest, JoinDistinctValues) {
+  ExpectDiagnosticsFor(R"cc(
+#include "unchecked_statusor_access_test_defs.h"
+
+    void target(bool b) {
+      STATUSOR_INT sor;
+      if (b)
+        sor = Make<STATUSOR_INT>();
+      else
+        sor = Make<STATUSOR_INT>();
+
+      if (sor.ok())
+        sor.value();
+      else
+        sor.value();  // [[unsafe]]
+    }
+  )cc");
+  ExpectDiagnosticsFor(R"cc(
+#include "unchecked_statusor_access_test_defs.h"
+
+    void target(bool b) {
+      STATUSOR_INT sor;
+      if (b) {
+        sor = Make<STATUSOR_INT>();
+        if (!sor.ok()) return;
+      } else {
+        sor = Make<STATUSOR_INT>();
+        if (!sor.ok()) return;
+      }
+      sor.value();
+    }
+  )cc");
+  ExpectDiagnosticsFor(R"cc(
+#include "unchecked_statusor_access_test_defs.h"
+
+    void target(bool b) {
+      STATUSOR_INT sor;
+      if (b) {
+        sor = Make<STATUSOR_INT>();
+        if (!sor.ok()) return;
+      } else {
+        sor = Make<STATUSOR_INT>();
+      }
+      sor.value();  // [[unsafe]]
+    }
+  )cc");
+}
+
+TEST_P(UncheckedStatusOrAccessModelTest, VarDeclInitExprFromPairAccess) {
+  ExpectDiagnosticsFor(R"cc(
+#include "unchecked_statusor_access_test_defs.h"
+
+    void target() {
+      auto sor = Make<std::pair<int, STATUSOR_INT>>().second;
+      if (sor.ok())
+        sor.value();
+      else
+        sor.value();  // [[unsafe]]
+    }
+  )cc");
+  ExpectDiagnosticsFor(R"cc(
+#include "unchecked_statusor_access_test_defs.h"
+
+    void target() {
+      const auto& sor = Make<std::pair<int, STATUSOR_INT>>().second;
+      if (sor.ok())
+        sor.value();
+      else
+        sor.value();  // [[unsafe]]
+    }
+  )cc");
+}
+
+TEST_P(UncheckedStatusOrAccessModelTest, LValueToRValueCastOfChangingValue) {
+  ExpectDiagnosticsFor(R"cc(
+#include "unchecked_statusor_access_test_defs.h"
+
+    bool foo();
+
+    void target(bool b1) {
+      STATUSOR_INT sor;
+      if (b1)
+        sor = Make<STATUSOR_INT>();
+      else
+        sor = Make<STATUSOR_INT>();
+
+      do {
+        const auto& b2 = foo();
+        if (b2) break;
+
+        sor.value();  // [[unsafe]]
+      } while (true);
+    }
+  )cc");
+}
+
+TEST_P(UncheckedStatusOrAccessModelTest, ConstructorInitializer) {
+  ExpectDiagnosticsFor(R"cc(
+#include "unchecked_statusor_access_test_defs.h"
+
+    class target {
+      target() : foo_(Make<STATUSOR_INT>().value()) {  // [[unsafe]]
+      }
+      int foo_;
+    };
+  )cc");
+}
+
+TEST_P(UncheckedStatusOrAccessModelTest, AssignStatusToBoolVar) {
+  ExpectDiagnosticsFor(R"cc(
+#include "unchecked_statusor_access_test_defs.h"
+
+    void target(STATUSOR_INT sor) {
+      bool ok = sor.ok();
+      if (ok)
+        sor.value();
+      else
+        sor.value();  // [[unsafe]]
+    }
+  )cc");
+  ExpectDiagnosticsFor(R"cc(
+#include "unchecked_statusor_access_test_defs.h"
+
+    void target(STATUSOR_INT sor) {
+      bool not_ok = !sor.ok();
+      if (not_ok)
+        sor.value();  // [[unsafe]]
+      else
+        sor.value();
+    }
+  )cc");
+}
+
+TEST_P(UncheckedStatusOrAccessModelTest, StructuredBindings) {
+  // Binding to a pair (which is actually a struct in the mock header).
+  ExpectDiagnosticsFor(R"cc(
+#include "unchecked_statusor_access_test_defs.h"
+
+    void target() {
+      const auto [sor, x] = Make<std::pair<STATUSOR_INT, int>>();
+      if (sor.ok()) sor.value();
+    }
+  )cc");
+
+  // Unsafe case.
+  ExpectDiagnosticsFor(R"cc(
+#include "unchecked_statusor_access_test_defs.h"
+
+    void target() {
+      const auto [sor, x] = Make<std::pair<STATUSOR_INT, int>>();
+      sor.value();  // [[unsafe]]
+    }
+  )cc");
+
+  // As a reference.
+  ExpectDiagnosticsFor(R"cc(
+#include "unchecked_statusor_access_test_defs.h"
+
+    void target() {
+      const auto& [sor, x] = Make<std::pair<STATUSOR_INT, int>>();
+      if (sor.ok()) sor.value();
+    }
+  )cc");
+
+  // Binding to a ref in a struct.
+  ExpectDiagnosticsFor(R"cc(
+#include "unchecked_statusor_access_test_defs.h"
+
+    struct S {
+      STATUSOR_INT& sor;
+      int i;
+    };
+
+    void target() {
+      const auto& [sor, i] = Make<S>();
+      if (sor.ok()) sor.value();
+    }
+  )cc");
+
+  // In a loop.
+  ExpectDiagnosticsFor(R"cc(
+#include "unchecked_statusor_access_test_defs.h"
+
+    void target() {
+      auto vals = Make<std::vector<std::pair<int, STATUSOR_INT>>>();
+      for (const auto& [x, sor] : vals)
+        if (sor.ok()) sor.value();
+    }
+  )cc");
+
+  // Similar to the above, but InitExpr already has the storage initialized,
+  // and bindings refer to them.
+  ExpectDiagnosticsFor(R"cc(
+#include "unchecked_statusor_access_test_defs.h"
+
+    void target() {
+      auto vals = Make<std::vector<std::pair<int, STATUSOR_INT>>>();
+      for (const auto& p : vals) {
+        const auto& [i, sor] = p;
+        if (sor.ok()) sor.value();
+      }
+    }
+  )cc");
+}
+
+TEST_P(UncheckedStatusOrAccessModelTest, AssignCompositeLogicExprToVar) {
+  ExpectDiagnosticsFor(
+      R"cc(
+#include "unchecked_statusor_access_test_defs.h"
+
+        void target(STATUSOR_INT sor, bool b) {
+          bool c = sor.ok() && b;
+          if (c) sor.value();
+        }
+      )cc");
+
+  ExpectDiagnosticsFor(
+      R"cc(
+#include "unchecked_statusor_access_test_defs.h"
+
+        void target(STATUSOR_INT sor, bool b) {
+          bool c = !(!sor.ok() || !b);
+          if (c) sor.value();
+        }
+      )cc");
+}
+
+TEST_P(UncheckedStatusOrAccessModelTest, Subclass) {
+  ExpectDiagnosticsFor(
+      R"cc(
+#include "unchecked_statusor_access_test_defs.h"
+
+        class Foo : public STATUSOR_INT {};
+
+        void target(Foo opt) {
+          opt.value();  // [[unsafe]]
+        }
+      )cc");
+}
+
+TEST_P(UncheckedStatusOrAccessModelTest, SubclassStatus) {
+  ExpectDiagnosticsFor(
+      R"cc(
+#include "unchecked_statusor_access_test_defs.h"
+
+        class Foo : public STATUS {};
+
+        void target(Foo opt) { opt.ok(); }
+      )cc");
+}
+
+TEST_P(UncheckedStatusOrAccessModelTest, SubclassOk) {
+  ExpectDiagnosticsFor(
+      R"cc(
+#include "unchecked_statusor_access_test_defs.h"
+
+        class Foo : public STATUSOR_INT {};
+
+        void target(Foo opt) {
+          if (opt.ok()) opt.value();
+        }
+      )cc");
+}
+
+TEST_P(UncheckedStatusOrAccessModelTest, SubclassOperator) {
+  ExpectDiagnosticsFor(
+      R"cc(
+#include "unchecked_statusor_access_test_defs.h"
+
+        class Foo : public STATUSOR_INT {};
+
+        void target(Foo opt) {
+          *opt;  // [[unsafe]]
+        }
+      )cc");
+}
+
+TEST_P(UncheckedStatusOrAccessModelTest, UnwrapValueWithStatusCheck) {
+  ExpectDiagnosticsFor(R"cc(
+#include "unchecked_statusor_access_test_defs.h"
+
+    void target(STATUSOR_INT sor) {
+      if (sor.status().ok())
+        sor.value();
+      else
+        sor.value();  // [[unsafe]]
+    }
+  )cc");
+}
+
+TEST_P(UncheckedStatusOrAccessModelTest, UnwrapValueWithStatusRefCheck) {
+  ExpectDiagnosticsFor(R"cc(
+#include "unchecked_statusor_access_test_defs.h"
+
+    void target(STATUSOR_INT sor) {
+      const STATUS& s = sor.status();
+      if (s.ok())
+        sor.value();
+      else
+        sor.value();  // [[unsafe]]
+    }
+  )cc");
+}
+
+TEST_P(UncheckedStatusOrAccessModelTest, UnwrapValueWithStatusPtrCheck) {
+  ExpectDiagnosticsFor(R"cc(
+#include "unchecked_statusor_access_test_defs.h"
+
+    void target(STATUSOR_INT sor) {
+      const STATUS* s = &sor.status();
+      if (s->ok())
+        sor.value();
+      else
+        sor.value();  // [[unsafe]]
+    }
+  )cc");
+}
+
+TEST_P(UncheckedStatusOrAccessModelTest, MembersUsedInsideStatus) {
+  ExpectDiagnosticsFor(R"cc(
+    namespace absl {
+
+    class Status {
+     public:
+      bool ok() const;
+
+      void target() const { ok(); }
+    };
+
+    }  // namespace absl
+  )cc");
+}
+
+TEST_P(UncheckedStatusOrAccessModelTest, StatusUpdate) {
+  ExpectDiagnosticsFor(R"cc(
+#include "unchecked_statusor_access_test_defs.h"
+
+    void target(STATUSOR_INT sor) {
+      STATUS s;
+      s.Update(sor.status());
+      if (s.ok())
+        sor.value();
+      else
+        sor.value();  // [[unsafe]]
+    }
+  )cc");
+
+  ExpectDiagnosticsFor(R"cc(
+#include "unchecked_statusor_access_test_defs.h"
+
+    void target(STATUSOR_INT sor1, STATUSOR_INT sor2) {
+      STATUS s;
+      s.Update(sor1.status());
+      s.Update(sor2.status());
+      if (s.ok()) {
+        sor1.value();
+        sor2.value();
+      }
+    }
+  )cc");
+
+  ExpectDiagnosticsFor(R"cc(
+#include "unchecked_statusor_access_test_defs.h"
+
+    void target(STATUSOR_INT sor1, STATUSOR_INT sor2) {
+      STATUS s;
+      s.Update(sor1.status());
+      CHECK(s.ok());
+      s.Update(sor2.status());
+      sor1.value();
+      sor2.value();  // [[unsafe]]
+    }
+  )cc");
+
+  ExpectDiagnosticsFor(R"cc(
+#include "unchecked_statusor_access_test_defs.h"
+
+    void target(STATUSOR_INT sor1, STATUSOR_INT sor2) {
+      STATUS s;
+      s.Update(sor1.status());
+      CHECK(s.ok());
+      sor1.value();
+      sor2.value();  // [[unsafe]]
+    }
+  )cc");
+
+  ExpectDiagnosticsFor(R"cc(
+#include "unchecked_statusor_access_test_defs.h"
+
+    void target(STATUSOR_INT sor1, STATUSOR_INT sor2) {
+      STATUS s;
+      STATUS sor1_status = sor1.status();
+      s.Update(std::move(sor1_status));
+      CHECK(s.ok());
+      sor1.value();
+      sor2.value();  // [[unsafe]]
+    }
+  )cc");
+
+  ExpectDiagnosticsFor(R"cc(
+#include "unchecked_statusor_access_test_defs.h"
+
+    void target(STATUSOR_INT sor1, STATUSOR_INT sor2) {
+      STATUS s;
+      STATUS sor1_status = sor1.status();
+      sor1_status.Update(sor2.status());
+      s.Update(std::move(sor1_status));
+      CHECK(s.ok());
+      sor1.value();
+      sor2.value();
+    }
+  )cc");
+  ExpectDiagnosticsFor(R"cc(
+#include "unchecked_statusor_access_test_defs.h"
+
+    const STATUS& OptStatus();
+
+    void target(STATUSOR_INT sor) {
+      auto s = sor.status();
+      s.Update(OptStatus());
+      if (s.ok()) sor.value();
+    }
+  )cc");
+}
+
+TEST_P(UncheckedStatusOrAccessModelTest, EqualityCheck) {
+  ExpectDiagnosticsFor(
+      R"cc(
+#include "unchecked_statusor_access_test_defs.h"
+
+        void target(STATUSOR_INT x, STATUSOR_INT y) {
+          if (x.ok()) {
+            if (x == y)
+              y.value();
+            else
+              y.value();  // [[unsafe]]
+          }
+        }
+      )cc");
+  ExpectDiagnosticsFor(
+      R"cc(
+#include "unchecked_statusor_access_test_defs.h"
+
+        void target(STATUSOR_INT x, STATUSOR_INT y) {
+          if (x.ok()) {
+            if (y == x)
+              y.value();
+            else
+              y.value();  // [[unsafe]]
+          }
+        }
+      )cc");
+  ExpectDiagnosticsFor(R"cc(
+#include "unchecked_statusor_access_test_defs.h"
+
+    void target(STATUSOR_INT x, STATUSOR_INT y) {
+      if (x.ok()) {
+        if (x != y)
+          y.value();  // [[unsafe]]
+        else
+          y.value();
+      }
+    }
+  )cc");
+  ExpectDiagnosticsFor(R"cc(
+#include "unchecked_statusor_access_test_defs.h"
+
+    void target(STATUSOR_INT x, STATUSOR_INT y) {
+      if (x.ok()) {
+        if (y != x)
+          y.value();  // [[unsafe]]
+        else
+          y.value();
+      }
+    }
+  )cc");
+  ExpectDiagnosticsFor(R"cc(
+#include "unchecked_statusor_access_test_defs.h"
+
+    void target(STATUSOR_INT x, STATUSOR_INT y) {
+      if (x.ok()) {
+        if (!(x == y))
+          y.value();  // [[unsafe]]
+        else
+          y.value();
+      }
+    }
+  )cc");
+  ExpectDiagnosticsFor(
+      R"cc(
+#include "unchecked_statusor_access_test_defs.h"
+
+        void target(STATUSOR_INT x, STATUSOR_INT y) {
+          if (x.ok()) {
+            if (!(x != y))
+              y.value();
+            else
+              y.value();  // [[unsafe]]
+          }
+        }
+      )cc");
+  ExpectDiagnosticsFor(R"cc(
+#include "unchecked_statusor_access_test_defs.h"
+
+    void target(STATUSOR_INT x, STATUSOR_INT y) {
+      if (x == y)
+        if (x.ok()) y.value();
+    }
+  )cc");
+  ExpectDiagnosticsFor(
+      R"cc(
+#include "unchecked_statusor_access_test_defs.h"
+
+        void target(STATUSOR_INT x, STATUSOR_INT y) {
+          if (x.ok()) {
+            if (x.status() == y.status())
+              y.value();
+            else
+              y.value();  // [[unsafe]]
+          }
+        }
+      )cc");
+  ExpectDiagnosticsFor(R"cc(
+#include "unchecked_statusor_access_test_defs.h"
+
+    void target(STATUSOR_INT x, STATUSOR_INT y) {
+      if (x.ok()) {
+        if (x.status() != y.status())
+          y.value();  // [[unsafe]]
+        else
+          y.value();
+      }
+    }
+  )cc");
+  ExpectDiagnosticsFor(
+      R"cc(
+#include "unchecked_statusor_access_test_defs.h"
+
+        void target(STATUSOR_INT x, STATUSOR_INT y) {
+          if (x.ok()) {
+            if (x.ok() == y.ok())
+              y.value();
+            else
+              y.value();  // [[unsafe]]
+          }
+        }
+      )cc");
+  ExpectDiagnosticsFor(R"cc(
+#include "unchecked_statusor_access_test_defs.h"
+
+    void target(STATUSOR_INT x, STATUSOR_INT y) {
+      if (x.ok()) {
+        if (x.ok() != y.ok())
+          y.value();  // [[unsafe]]
+        else
+          y.value();
+      }
+    }
+  )cc");
+  ExpectDiagnosticsFor(
+      R"cc(
+#include "unchecked_statusor_access_test_defs.h"
+
+        void target(STATUSOR_INT x, STATUSOR_INT y) {
+          if (x.ok()) {
+            if (x.status().ok() == y.status().ok())
+              y.value();
+            else
+              y.value();  // [[unsafe]]
+          }
+        }
+      )cc");
+  ExpectDiagnosticsFor(R"cc(
+#include "unchecked_statusor_access_test_defs.h"
+
+    void target(STATUSOR_INT x, STATUSOR_INT y) {
+      if (x.ok()) {
+        if (x.status().ok() != y.status().ok())
+          y.value();  // [[unsafe]]
+        else
+          y.value();
+      }
+    }
+  )cc");
+  ExpectDiagnosticsFor(
+      R"cc(
+#include "unchecked_statusor_access_test_defs.h"
+
+        void target(STATUSOR_INT x, STATUSOR_INT y) {
+          if (x.ok()) {
+            if (x.status().ok() == y.ok())
+              y.value();
+            else
+              y.value();  // [[unsafe]]
+          }
+        }
+      )cc");
+  ExpectDiagnosticsFor(R"cc(
+#include "unchecked_statusor_access_test_defs.h"
+
+    void target(STATUSOR_INT x, STATUSOR_INT y) {
+      if (x.ok()) {
+        if (x.status().ok() != y.ok())
+          y.value();  // [[unsafe]]
+        else
+          y.value();
+      }
+    }
+  )cc");
+  ExpectDiagnosticsFor(R"cc(
+#include "unchecked_statusor_access_test_defs.h"
+
+    void target(bool b, STATUSOR_INT sor) {
+      if (sor.ok() == b) {
+        if (b) sor.value();
+      }
+    }
+  )cc");
+  ExpectDiagnosticsFor(R"cc(
+#include "unchecked_statusor_access_test_defs.h"
+
+    void target(STATUSOR_INT sor) {
+      if (sor.ok() == true) sor.value();
+    }
+  )cc");
+  ExpectDiagnosticsFor(R"cc(
+#include "unchecked_statusor_access_test_defs.h"
+
+    void target(STATUSOR_INT sor) {
+      if (sor.ok() == false) sor.value();  // [[unsafe]]
+    }
+  )cc");
+  ExpectDiagnosticsFor(R"cc(
+#include "unchecked_statusor_access_test_defs.h"
+
+    void target(bool b) {
+      STATUSOR_INT sor1;
+      STATUSOR_INT sor2 = Make<STATUSOR_INT>();
+      if (sor1 == sor2) sor2.value();  // [[unsafe]]
+    }
+  )cc");
+  ExpectDiagnosticsFor(R"cc(
+#include "unchecked_statusor_access_test_defs.h"
+
+    void target(bool b) {
+      STATUSOR_INT sor1 = Make<STATUSOR_INT>();
+      STATUSOR_INT sor2;
+      if (sor1 == sor2) sor1.value();  // [[unsafe]]
+    }
+  )cc");
+}
+
+
+} // namespace
+
+std::string
+GetAliasMacros(UncheckedStatusOrAccessModelTestAliasKind AliasKind) {
+  switch (AliasKind) {
+  case UncheckedStatusOrAccessModelTestAliasKind::kUnaliased:
+    return R"cc(
+#define STATUSOR_INT ::absl::StatusOr<int>
+#define STATUSOR_BOOL ::absl::StatusOr<bool>
+#define STATUSOR_VOIDPTR ::absl::StatusOr<void*>
+#define STATUS ::absl::Status
+      )cc";
+  case UncheckedStatusOrAccessModelTestAliasKind::kPartiallyAliased:
+    return R"cc(
+        template <typename T>
+        using StatusOrAlias = ::absl::StatusOr<T>;
+#define STATUSOR_INT StatusOrAlias<int>
+#define STATUSOR_BOOL StatusOrAlias<bool>
+#define STATUSOR_VOIDPTR StatusOrAlias<void*>
+#define STATUS ::absl::Status
+      )cc";
+  case UncheckedStatusOrAccessModelTestAliasKind::kFullyAliased:
+    return R"cc(
+        using StatusOrIntAlias = ::absl::StatusOr<int>;
+#define STATUSOR_INT StatusOrIntAlias
+        using StatusOrBoolAlias = ::absl::StatusOr<bool>;
+#define STATUSOR_BOOL StatusOrBoolAlias
+        using StatusOrVoidPtrAlias = ::absl::StatusOr<void*>;
+#define STATUSOR_VOIDPTR StatusOrVoidPtrAlias
+        using StatusAlias = ::absl::Status;
+#define STATUS StatusAlias
+      )cc";
+  }
+  llvm_unreachable("Unknown alias kind.");
+}
+
+std::vector<std::pair<std::string, std::string>>
+GetHeaders(UncheckedStatusOrAccessModelTestAliasKind AliasKind) {
+  auto Headers = test::getMockHeaders();
+
+  Headers.emplace_back("unchecked_statusor_access_test_defs.h",
+                       R"cc(
+#include "cstddef.h"
+#include "statusor_defs.h"
+#include "std_optional.h"
+#include "std_vector.h"
+#include "std_pair.h"
+#include "absl_log.h"
+#include "testing_defs.h"
+
+                             template <typename T>
+                             T Make();
+
+                             class Fatal {
+                              public:
+                               ~Fatal() __attribute__((noreturn));
+                               int value();
+                             };
+                       )cc" +
+                           GetAliasMacros(AliasKind));
+  return Headers;
+}
+} // namespace clang::dataflow::statusor_model
diff --git a/clang/unittests/Analysis/FlowSensitive/UncheckedStatusOrAccessModelTestFixture.h b/clang/unittests/Analysis/FlowSensitive/UncheckedStatusOrAccessModelTestFixture.h
new file mode 100644
index 0000000000000..ff1d323a0086f
--- /dev/null
+++ b/clang/unittests/Analysis/FlowSensitive/UncheckedStatusOrAccessModelTestFixture.h
@@ -0,0 +1,157 @@
+//===- UncheckedStatusOrAccessModelTestFixture.h --------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_CLANG_ANALYSIS_FLOW_SENSITIVE_UNCHECKEDSTATUSORACCESSMODELTESTFIXTURE_H_
+#define LLVM_CLANG_ANALYSIS_FLOW_SENSITIVE_UNCHECKEDSTATUSORACCESSMODELTESTFIXTURE_H_
+
+#include <algorithm>
+#include <iterator>
+#include <string>
+#include <utility>
+#include <vector>
+
+#include "TestingSupport.h"
+#include "clang/AST/ASTContext.h"
+#include "clang/AST/Decl.h"
+#include "clang/ASTMatchers/ASTMatchers.h"
+#include "clang/Analysis/CFG.h"
+#include "clang/Analysis/FlowSensitive/DataflowEnvironment.h"
+#include "clang/Analysis/FlowSensitive/MatchSwitch.h"
+#include "clang/Analysis/FlowSensitive/Models/UncheckedStatusOrAccessModel.h"
+#include "clang/Basic/SourceLocation.h"
+#include "clang/Tooling/Tooling.h"
+#include "llvm/ADT/DenseMap.h"
+#include "llvm/ADT/DenseSet.h"
+#include "llvm/ADT/STLExtras.h"
+#include "llvm/Support/Error.h"
+#include "gmock/gmock.h"
+#include "gtest/gtest.h"
+
+namespace clang::dataflow::statusor_model {
+
+enum class UncheckedStatusOrAccessModelTestAliasKind {
+  kUnaliased = 0,        // no alias
+  kPartiallyAliased = 1, // template<typename T> using Alias = absl::StatusOr;
+  kFullyAliased = 2,     // using Alias = absl::StatusOr<int>;
+};
+
+// Base class for the test executors. This is needed to abstract away the
+// template parameter from the UncheckedStatusOrAccessModelTestExecutor. This
+// allows us to use UncheckedStatusOrAccessModelTestExecutorBase* in the
+// UncheckedStatusOrAccessModelTest.
+class UncheckedStatusOrAccessModelTestExecutorBase {
+public:
+  virtual void
+  ExpectDiagnosticsFor(std::string SourceCode,
+                       UncheckedStatusOrAccessModelTestAliasKind) const = 0;
+  virtual void ExpectDiagnosticsForLambda(
+      std::string SourceCode,
+      UncheckedStatusOrAccessModelTestAliasKind) const = 0;
+  virtual ~UncheckedStatusOrAccessModelTestExecutorBase() = default;
+};
+
+// Returns these macros according to the alias kind:
+//  - STATUS
+//  - STATUSOR_INT
+//  - STATUSOR_BOOL
+//  - STATUSOR_VOIDPTR
+// Tests should use these macros instead of e.g. absl::StatusOr<int> to ensure
+// the model is insensitive to whether the StatusOr<> is aliased or not.
+std::string GetAliasMacros(UncheckedStatusOrAccessModelTestAliasKind AliasKind);
+
+std::vector<std::pair<std::string, std::string>>
+GetHeaders(UncheckedStatusOrAccessModelTestAliasKind AliasKind);
+
+// This allows us to run the same test suite for multiple models. This allows
+// vendors to model internal APIs in an extension of the base model, and make
+// sure that these tests still pass.
+template <typename Model>
+class UncheckedStatusOrAccessModelTestExecutor
+    : public UncheckedStatusOrAccessModelTestExecutorBase {
+public:
+  void ExpectDiagnosticsFor(
+      std::string SourceCode,
+      UncheckedStatusOrAccessModelTestAliasKind AliasKind) const override {
+    using namespace ::clang::ast_matchers; // NOLINT: Too many names
+    ExpectDiagnosticsFor(SourceCode, hasName("target"), AliasKind);
+  }
+
+  void ExpectDiagnosticsForLambda(
+      std::string SourceCode,
+      UncheckedStatusOrAccessModelTestAliasKind AliasKind) const override {
+    using namespace ::clang::ast_matchers; // NOLINT: Too many names
+    ExpectDiagnosticsFor(SourceCode,
+                         allOf(hasOverloadedOperatorName("()"),
+                               hasDeclContext(cxxRecordDecl(isLambda()))),
+                         AliasKind);
+  }
+
+  template <typename FuncDeclMatcher>
+  void ExpectDiagnosticsFor(
+      std::string SourceCode, FuncDeclMatcher FuncMatcher,
+      UncheckedStatusOrAccessModelTestAliasKind AliasKind) const {
+    std::vector<std::pair<std::string, std::string>> Headers =
+        GetHeaders(AliasKind);
+
+    UncheckedStatusOrAccessModelOptions Options{};
+    std::vector<SourceLocation> Diagnostics;
+    llvm::Error Error = test::checkDataflow<Model>(
+        test::AnalysisInputs<Model>(
+            SourceCode, std::move(FuncMatcher),
+            [](ASTContext &Ctx, Environment &Env) { return Model(Ctx, Env); })
+            .withPostVisitCFG(
+                [&Diagnostics,
+                 Diagnoser = UncheckedStatusOrAccessDiagnoser(Options)](
+                    ASTContext &Ctx, const CFGElement &Elt,
+                    const TransferStateForDiagnostics<
+                        UncheckedStatusOrAccessModel::Lattice> &State) mutable {
+                  auto EltDiagnostics = Diagnoser(Elt, Ctx, State);
+                  llvm::move(EltDiagnostics, std::back_inserter(Diagnostics));
+                })
+            .withASTBuildArgs(
+                {"-fsyntax-only", "-std=c++17", "-Wno-undefined-inline"})
+            .withASTBuildVirtualMappedFiles(
+                tooling::FileContentMappings(Headers.begin(), Headers.end())),
+        /*VerifyResults=*/[&Diagnostics, SourceCode](
+                              const llvm::DenseMap<unsigned, std::string>
+                                  &Annotations,
+                              const test::AnalysisOutputs &AO) {
+          llvm::DenseSet<unsigned> AnnotationLines;
+          for (const auto &[Line, _] : Annotations)
+            AnnotationLines.insert(Line);
+          auto &SrcMgr = AO.ASTCtx.getSourceManager();
+          llvm::DenseSet<unsigned> DiagnosticLines;
+          for (SourceLocation &Loc : Diagnostics)
+            DiagnosticLines.insert(SrcMgr.getPresumedLineNumber(Loc));
+
+          EXPECT_THAT(DiagnosticLines, testing::ContainerEq(AnnotationLines))
+              << "\nFailing code:\n"
+              << SourceCode;
+        });
+    if (Error)
+      FAIL() << llvm::toString(std::move(Error));
+  }
+};
+
+class UncheckedStatusOrAccessModelTest
+    : public ::testing::TestWithParam<
+          std::pair<UncheckedStatusOrAccessModelTestExecutorBase *,
+                    UncheckedStatusOrAccessModelTestAliasKind>> {
+protected:
+  void ExpectDiagnosticsFor(std::string SourceCode) {
+    GetParam().first->ExpectDiagnosticsFor(SourceCode, GetParam().second);
+  }
+
+  void ExpectDiagnosticsForLambda(std::string SourceCode) {
+    GetParam().first->ExpectDiagnosticsForLambda(SourceCode, GetParam().second);
+  }
+};
+
+} // namespace clang::dataflow::statusor_model
+
+#endif // LLVM_CLANG_ANALYSIS_FLOW_SENSITIVE_UNCHECKEDSTATUSORACCESSMODELTESTFIXTURE_H_
diff --git a/llvm/utils/gn/secondary/clang/lib/Analysis/FlowSensitive/Models/BUILD.gn b/llvm/utils/gn/secondary/clang/lib/Analysis/FlowSensitive/Models/BUILD.gn
index 3fd3aab7970d5..01a05bcfda415 100644
--- a/llvm/utils/gn/secondary/clang/lib/Analysis/FlowSensitive/Models/BUILD.gn
+++ b/llvm/utils/gn/secondary/clang/lib/Analysis/FlowSensitive/Models/BUILD.gn
@@ -11,5 +11,6 @@ static_library("Models") {
   sources = [
     "ChromiumCheckModel.cpp",
     "UncheckedOptionalAccessModel.cpp",
+    "UncheckedStatusOrAccessModel.cpp",
   ]
 }
diff --git a/llvm/utils/gn/secondary/clang/unittests/Analysis/FlowSensitive/BUILD.gn b/llvm/utils/gn/secondary/clang/unittests/Analysis/FlowSensitive/BUILD.gn
index c9f3a0745ed46..c74a44c7016c6 100644
--- a/llvm/utils/gn/secondary/clang/unittests/Analysis/FlowSensitive/BUILD.gn
+++ b/llvm/utils/gn/secondary/clang/unittests/Analysis/FlowSensitive/BUILD.gn
@@ -44,6 +44,8 @@ unittest("ClangAnalysisFlowSensitiveTests") {
     "TransferTest.cpp",
     "TypeErasedDataflowAnalysisTest.cpp",
     "UncheckedOptionalAccessModelTest.cpp",
+    "UncheckedStatusOrAccessModelTest.cpp",
+    "UncheckedStatusOrAccessModelTestFixture.cpp",
     "ValueTest.cpp",
     "WatchedLiteralsSolverTest.cpp",
   ]

>From 3201b9f6d6f38999469ef5ee85c1c9315d4f6a7a Mon Sep 17 00:00:00 2001
From: Florian Mayer <fmayer at google.com>
Date: Thu, 16 Oct 2025 14:55:49 -0700
Subject: [PATCH 2/3] =?UTF-8?q?[=F0=9D=98=80=F0=9D=97=BD=F0=9D=97=BF]=20ch?=
 =?UTF-8?q?anges=20introduced=20through=20rebase?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

Created using spr 1.3.7

[skip ci]
---
 .../Models/UncheckedStatusOrAccessModel.cpp   | 77 ++++++++++---------
 ...ncheckedStatusOrAccessModelTestFixture.cpp |  1 -
 2 files changed, 40 insertions(+), 38 deletions(-)

diff --git a/clang/lib/Analysis/FlowSensitive/Models/UncheckedStatusOrAccessModel.cpp b/clang/lib/Analysis/FlowSensitive/Models/UncheckedStatusOrAccessModel.cpp
index 4ebf3e4251dd6..24381083b3f25 100644
--- a/clang/lib/Analysis/FlowSensitive/Models/UncheckedStatusOrAccessModel.cpp
+++ b/clang/lib/Analysis/FlowSensitive/Models/UncheckedStatusOrAccessModel.cpp
@@ -116,13 +116,12 @@ static auto valueOperatorCall() {
 }
 
 static clang::ast_matchers::TypeMatcher statusType() {
-  using namespace ::clang::ast_matchers;  // NOLINT: Too many names
+  using namespace ::clang::ast_matchers; // NOLINT: Too many names
   return hasCanonicalType(qualType(hasDeclaration(statusClass())));
 }
 
-
 static auto isComparisonOperatorCall(llvm::StringRef operator_name) {
-  using namespace ::clang::ast_matchers;  // NOLINT: Too many names
+  using namespace ::clang::ast_matchers; // NOLINT: Too many names
   return cxxOperatorCallExpr(
       hasOverloadedOperatorName(operator_name), argumentCountIs(2),
       hasArgument(0, anyOf(hasType(statusType()), hasType(statusOrType()))),
@@ -318,10 +317,10 @@ static void transferStatusUpdateCall(const CXXMemberCallExpr *Expr,
   State.Env.setValue(locForOk(*ThisLoc), NewVal);
 }
 
-static BoolValue* evaluateStatusEquality(RecordStorageLocation& LhsStatusLoc,
-                                         RecordStorageLocation& RhsStatusLoc,
-                                         Environment& Env) {
-  auto& A = Env.arena();
+static BoolValue *evaluateStatusEquality(RecordStorageLocation &LhsStatusLoc,
+                                         RecordStorageLocation &RhsStatusLoc,
+                                         Environment &Env) {
+  auto &A = Env.arena();
   // Logically, a Status object is composed of an error code that could take one
   // of multiple possible values, including the "ok" value. We track whether a
   // Status object has an "ok" value and represent this as an `ok` bit. Equality
@@ -331,10 +330,10 @@ static BoolValue* evaluateStatusEquality(RecordStorageLocation& LhsStatusLoc,
   // only track the `ok` bits, we can't make any conclusions about equality when
   // we know that two Status objects have non-ok values.
 
-  auto& LhsOkVal = valForOk(LhsStatusLoc, Env);
-  auto& RhsOkVal = valForOk(RhsStatusLoc, Env);
+  auto &LhsOkVal = valForOk(LhsStatusLoc, Env);
+  auto &RhsOkVal = valForOk(RhsStatusLoc, Env);
 
-  auto& Res = Env.makeAtomicBoolValue();
+  auto &Res = Env.makeAtomicBoolValue();
 
   // lhs && rhs => res (a.k.a. !res => !lhs || !rhs)
   Env.assume(A.makeImplies(A.makeAnd(LhsOkVal.formula(), RhsOkVal.formula()),
@@ -346,10 +345,11 @@ static BoolValue* evaluateStatusEquality(RecordStorageLocation& LhsStatusLoc,
   return &Res;
 }
 
-static BoolValue* evaluateStatusOrEquality(
-    RecordStorageLocation& LhsStatusOrLoc,
-    RecordStorageLocation& RhsStatusOrLoc, Environment& Env) {
-  auto& A = Env.arena();
+static BoolValue *
+evaluateStatusOrEquality(RecordStorageLocation &LhsStatusOrLoc,
+                         RecordStorageLocation &RhsStatusOrLoc,
+                         Environment &Env) {
+  auto &A = Env.arena();
   // Logically, a StatusOr<T> object is composed of two values - a Status and a
   // value of type T. Equality of StatusOr objects compares both values.
   // Therefore, merely comparing the `ok` bits of the Status values isn't
@@ -359,9 +359,9 @@ static BoolValue* evaluateStatusOrEquality(
   // codes matters. Since we only track the `ok` bits of the Status values, we
   // can't make any conclusions about equality when we know that two StatusOr
   // objects are engaged or when their Status values contain non-ok error codes.
-  auto& LhsOkVal = valForOk(locForStatus(LhsStatusOrLoc), Env);
-  auto& RhsOkVal = valForOk(locForStatus(RhsStatusOrLoc), Env);
-  auto& res = Env.makeAtomicBoolValue();
+  auto &LhsOkVal = valForOk(locForStatus(LhsStatusOrLoc), Env);
+  auto &RhsOkVal = valForOk(locForStatus(RhsStatusOrLoc), Env);
+  auto &res = Env.makeAtomicBoolValue();
 
   // res => (lhs == rhs)
   Env.assume(A.makeImplies(
@@ -369,17 +369,18 @@ static BoolValue* evaluateStatusOrEquality(
   return &res;
 }
 
-
-static BoolValue* evaluateEquality(const Expr* LhsExpr, const Expr* RhsExpr,
-                                   Environment& Env) {
+static BoolValue *evaluateEquality(const Expr *LhsExpr, const Expr *RhsExpr,
+                                   Environment &Env) {
   // Check the type of both sides in case an operator== is added that admits
   // different types.
   if (isStatusOrType(LhsExpr->getType()) &&
       isStatusOrType(RhsExpr->getType())) {
-    auto* LhsStatusOrLoc = Env.get<RecordStorageLocation>(*LhsExpr);
-    if (LhsStatusOrLoc == nullptr) return nullptr;
-    auto* RhsStatusOrLoc = Env.get<RecordStorageLocation>(*RhsExpr);
-    if (RhsStatusOrLoc == nullptr) return nullptr;
+    auto *LhsStatusOrLoc = Env.get<RecordStorageLocation>(*LhsExpr);
+    if (LhsStatusOrLoc == nullptr)
+      return nullptr;
+    auto *RhsStatusOrLoc = Env.get<RecordStorageLocation>(*RhsExpr);
+    if (RhsStatusOrLoc == nullptr)
+      return nullptr;
 
     return evaluateStatusOrEquality(*LhsStatusOrLoc, *RhsStatusOrLoc, Env);
 
@@ -387,23 +388,26 @@ static BoolValue* evaluateEquality(const Expr* LhsExpr, const Expr* RhsExpr,
     // different types.
   }
   if (isStatusType(LhsExpr->getType()) && isStatusType(RhsExpr->getType())) {
-    auto* LhsStatusLoc = Env.get<RecordStorageLocation>(*LhsExpr);
-    if (LhsStatusLoc == nullptr) return nullptr;
+    auto *LhsStatusLoc = Env.get<RecordStorageLocation>(*LhsExpr);
+    if (LhsStatusLoc == nullptr)
+      return nullptr;
 
-    auto* RhsStatusLoc = Env.get<RecordStorageLocation>(*RhsExpr);
-    if (RhsStatusLoc == nullptr) return nullptr;
+    auto *RhsStatusLoc = Env.get<RecordStorageLocation>(*RhsExpr);
+    if (RhsStatusLoc == nullptr)
+      return nullptr;
 
     return evaluateStatusEquality(*LhsStatusLoc, *RhsStatusLoc, Env);
   }
   return nullptr;
 }
 
-static void transferComparisonOperator(const CXXOperatorCallExpr* Expr,
-                                       LatticeTransferState& State,
+static void transferComparisonOperator(const CXXOperatorCallExpr *Expr,
+                                       LatticeTransferState &State,
                                        bool IsNegative) {
-  auto* LhsAndRhsVal =
+  auto *LhsAndRhsVal =
       evaluateEquality(Expr->getArg(0), Expr->getArg(1), State.Env);
-  if (LhsAndRhsVal == nullptr) return;
+  if (LhsAndRhsVal == nullptr)
+    return;
 
   if (IsNegative)
     State.Env.setValue(*Expr, State.Env.makeNot(*LhsAndRhsVal));
@@ -411,7 +415,6 @@ static void transferComparisonOperator(const CXXOperatorCallExpr* Expr,
     State.Env.setValue(*Expr, *LhsAndRhsVal);
 }
 
-
 CFGMatchSwitch<LatticeTransferState>
 buildTransferMatchSwitch(ASTContext &Ctx,
                          CFGMatchSwitchBuilder<LatticeTransferState> Builder) {
@@ -427,15 +430,15 @@ buildTransferMatchSwitch(ASTContext &Ctx,
                                         transferStatusUpdateCall)
       .CaseOfCFGStmt<CXXOperatorCallExpr>(
           isComparisonOperatorCall("=="),
-          [](const CXXOperatorCallExpr* Expr, const MatchFinder::MatchResult&,
-             LatticeTransferState& State) {
+          [](const CXXOperatorCallExpr *Expr, const MatchFinder::MatchResult &,
+             LatticeTransferState &State) {
             transferComparisonOperator(Expr, State,
                                        /*IsNegative=*/false);
           })
       .CaseOfCFGStmt<CXXOperatorCallExpr>(
           isComparisonOperatorCall("!="),
-          [](const CXXOperatorCallExpr* Expr, const MatchFinder::MatchResult&,
-             LatticeTransferState& State) {
+          [](const CXXOperatorCallExpr *Expr, const MatchFinder::MatchResult &,
+             LatticeTransferState &State) {
             transferComparisonOperator(Expr, State,
                                        /*IsNegative=*/true);
           })
diff --git a/clang/unittests/Analysis/FlowSensitive/UncheckedStatusOrAccessModelTestFixture.cpp b/clang/unittests/Analysis/FlowSensitive/UncheckedStatusOrAccessModelTestFixture.cpp
index 99f04cc8fe7e7..d3b10688643b5 100644
--- a/clang/unittests/Analysis/FlowSensitive/UncheckedStatusOrAccessModelTestFixture.cpp
+++ b/clang/unittests/Analysis/FlowSensitive/UncheckedStatusOrAccessModelTestFixture.cpp
@@ -2828,7 +2828,6 @@ TEST_P(UncheckedStatusOrAccessModelTest, EqualityCheck) {
   )cc");
 }
 
-
 } // namespace
 
 std::string

>From df3a6b51009de7bb49cb02841c00f5e957155521 Mon Sep 17 00:00:00 2001
From: Florian Mayer <fmayer at google.com>
Date: Fri, 17 Oct 2025 13:28:15 -0700
Subject: [PATCH 3/3] =?UTF-8?q?[=F0=9D=98=80=F0=9D=97=BD=F0=9D=97=BF]=20ch?=
 =?UTF-8?q?anges=20introduced=20through=20rebase?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

Created using spr 1.3.7

[skip ci]
---
 .../Models/UncheckedStatusOrAccessModel.h     |  2 -
 .../Models/UncheckedStatusOrAccessModel.cpp   | 41 ++++++++++++-------
 2 files changed, 26 insertions(+), 17 deletions(-)

diff --git a/clang/include/clang/Analysis/FlowSensitive/Models/UncheckedStatusOrAccessModel.h b/clang/include/clang/Analysis/FlowSensitive/Models/UncheckedStatusOrAccessModel.h
index 14f28543fc04c..cb619fb0cb5bb 100644
--- a/clang/include/clang/Analysis/FlowSensitive/Models/UncheckedStatusOrAccessModel.h
+++ b/clang/include/clang/Analysis/FlowSensitive/Models/UncheckedStatusOrAccessModel.h
@@ -35,8 +35,6 @@ clang::ast_matchers::DeclarationMatcher statusOrClass();
 clang::ast_matchers::DeclarationMatcher statusClass();
 // Match declaration of `absl::internal_statusor::OperatorBase`.
 clang::ast_matchers::DeclarationMatcher statusOrOperatorBaseClass();
-clang::ast_matchers::TypeMatcher possiblyAliasedStatusType();
-clang::ast_matchers::TypeMatcher possiblyAliasedStatusOrType();
 clang::ast_matchers::TypeMatcher statusOrType();
 
 // Get RecordStorageLocation for the `Status` contained in the `StatusOr`
diff --git a/clang/lib/Analysis/FlowSensitive/Models/UncheckedStatusOrAccessModel.cpp b/clang/lib/Analysis/FlowSensitive/Models/UncheckedStatusOrAccessModel.cpp
index 24381083b3f25..2741476fe4781 100644
--- a/clang/lib/Analysis/FlowSensitive/Models/UncheckedStatusOrAccessModel.cpp
+++ b/clang/lib/Analysis/FlowSensitive/Models/UncheckedStatusOrAccessModel.cpp
@@ -39,8 +39,30 @@ using ::clang::ast_matchers::StatementMatcher;
 
 } // namespace
 
-static bool isStatusOrOperatorBaseType(QualType type) {
-  return isRecordTypeWithName(type, "absl::internal_statusor::OperatorBase");
+static bool namespaceEquals(const NamespaceDecl *NS,
+                            clang::ArrayRef<clang::StringRef> NamespaceNames) {
+  while (!NamespaceNames.empty() && NS) {
+    if (NS->getName() != NamespaceNames.consume_back())
+      return false;
+    NS = dyn_cast_or_null<NamespaceDecl>(NS->getParent());
+  }
+  return NamespaceNames.empty() && !NS;
+}
+
+// TODO: move this to a proper place to share with the rest of clang
+static bool isTypeNamed(QualType Type, clang::ArrayRef<clang::StringRef> NS,
+                        StringRef Name) {
+  if (Type.isNull())
+    return false;
+  if (auto *RD = Type->getAsRecordDecl())
+    if (RD->getName() == Name)
+      if (const auto *N = dyn_cast_or_null<NamespaceDecl>(RD->getDeclContext()))
+        return namespaceEquals(N, NS);
+  return false;
+}
+
+static bool isStatusOrOperatorBaseType(QualType Type) {
+  return isTypeNamed(Type, {"absl", "internal_statusor"}, "OperatorBase");
 }
 
 static bool isSafeUnwrap(RecordStorageLocation *StatusOrLoc,
@@ -199,17 +221,6 @@ clang::ast_matchers::DeclarationMatcher statusOrOperatorBaseClass() {
       hasName("absl::internal_statusor::OperatorBase"));
 }
 
-clang::ast_matchers::TypeMatcher possiblyAliasedStatusOrType() {
-  using namespace ::clang::ast_matchers; // NOLINT: Too many names
-  return hasUnqualifiedDesugaredType(
-      recordType(hasDeclaration(statusOrClass())));
-}
-
-clang::ast_matchers::TypeMatcher possiblyAliasedStatusType() {
-  using namespace ::clang::ast_matchers; // NOLINT: Too many names
-  return hasUnqualifiedDesugaredType(recordType(hasDeclaration(statusClass())));
-}
-
 clang::ast_matchers::TypeMatcher statusOrType() {
   using namespace ::clang::ast_matchers; // NOLINT: Too many names
   return hasCanonicalType(qualType(hasDeclaration(statusOrClass())));
@@ -221,11 +232,11 @@ bool isRecordTypeWithName(QualType Type, llvm::StringRef TypeName) {
 }
 
 bool isStatusOrType(QualType Type) {
-  return isRecordTypeWithName(Type, "absl::StatusOr");
+  return isTypeNamed(Type, {"absl"}, "StatusOr");
 }
 
 bool isStatusType(QualType Type) {
-  return isRecordTypeWithName(Type, "absl::Status");
+  return isTypeNamed(Type, {"absl"}, "Status");
 }
 
 llvm::StringMap<QualType> getSyntheticFields(QualType Ty, QualType StatusType,



More information about the llvm-commits mailing list